<commit_before>var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var Q = require('q');
var Gittle = require('gittle');

var types = require('../types');
var errors = require('../errors');

function statsToType(stats) {
    if (stats.isDirectory()) return types.DIRECTORY;
    return types.FILE;
}

function Driver(options) {
    this.options = _.defaults(options || {}, {
        root: null
    });
    if (!this.options.root) throw "Local driver requires a 'root' option";
    this.root = path.resolve(this.options.root);

    // Current operation
    this.current = Q();

    // GIT repo
    this.repo = new Gittle(this.root);
}

// Return an absolute path to a file in the repo
Driver.prototype.path = function(p) {
    p = path.resolve(this.root, p);

    if (p.indexOf(this.root) !== 0) {
        throw "Path is out of scope: "+p;
    }

    return p;
};

// Transform an absolute path to relative to this repo
Driver.prototype.relative = function(p) {
    p = path.relative(this.root, p);

    if (p.indexOf('..') === 0) {
        throw "Path is out of scope: "+p;
    }

    return p;
};

// Add a function to the operations queue
Driver.prototype.queue = function(fn, opts) {
    var branch, that = this;
    opts = _.defaults(opts || {}, {

    });

    this.current =  this.current
        .fail(function() { return Q(); })

        // Switch to required branch
        .then(function() {
            return that.repo.current_branch()
            .then(function(_branch) {
                branch = _branch;

                if (opts.ref == branch) return;
                return that.repo.checkout(opts.ref);
            });
        })

        // Execute function
        .then(fn, fn)

        // Switch back to previous ref
        .fin(function() {
            if (opts.ref == branch) return;
            return that.repo.checkout(branch);
        });

    return this.current;
};

// Get infor for a file
Driver.prototype._stat = function(p, opts) {
    var that = this;
    p = this.path(p);
    var relativePath = this.relative(p);
    var tree = that.repo.tree(opts.ref)

    return Q.nfcall(fs.stat, p)
    .then(function(stats) {
        return tree.find(relativePath)
        .then(function(file) {
            if (!file) return null;

            return {
                name: path.basename(p),
                path: relativePath,
                type: statsToType(stats),
                sha: file.id,
                size: stats.size
            };
        });
    });
};

// Get infos for a file
Driver.prototype.stat = function(p, opts) {
    var that = this;

    return this.queue(function() {
        return that._stat(p, opts)
        .then(function(stats) {
            if (!stats) throw "File doesn't exist";

            return Q.nfcall(fs.readFile, p, 'utf-8')
            .then(function(content) {
                stats.content = content;
                return stats;<commit_msg>Fix error in localDriver.stat<commit_after>var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var Q = require('q');
var Gittle = require('gittle');

var types = require('../types');
var errors = require('../errors');

function statsToType(stats) {
    if (stats.isDirectory()) return types.DIRECTORY;
    return types.FILE;
}

function Driver(options) {
    this.options = _.defaults(options || {}, {
        root: null
    });
    if (!this.options.root) throw "Local driver requires a 'root' option";
    this.root = path.resolve(this.options.root);

    // Current operation
    this.current = Q();

    // GIT repo
    this.repo = new Gittle(this.root);
}

// Return an absolute path to a file in the repo
Driver.prototype.path = function(p) {
    p = path.resolve(this.root, p);

    if (p.indexOf(this.root) !== 0) {
        throw "Path is out of scope: "+p;
    }

    return p;
};

// Transform an absolute path to relative to this repo
Driver.prototype.relative = function(p) {
    p = path.relative(this.root, p);

    if (p.indexOf('..') === 0) {
        throw "Path is out of scope: "+p;
    }

    return p;
};

// Add a function to the operations queue
Driver.prototype.queue = function(fn, opts) {
    var branch, that = this;
    opts = _.defaults(opts || {}, {

    });

    this.current =  this.current
        .fail(function() { return Q(); })

        // Switch to required branch
        .then(function() {
            return that.repo.current_branch()
            .then(function(_branch) {
                branch = _branch;

                if (opts.ref == branch) return;
                return that.repo.checkout(opts.ref);
            });
        })

        // Execute function
        .then(fn, fn)

        // Switch back to previous ref
        .fin(function() {
            if (opts.ref == branch) return;
            return that.repo.checkout(branch);
        });

    return this.current;
};

// Get infor for a file
Driver.prototype._stat = function(p, opts) {
    var that = this;
    p = this.path(p);
    var relativePath = this.relative(p);
    var tree = that.repo.tree(opts.ref)

    return Q.nfcall(fs.stat, p)
    .then(function(stats) {
        return tree.find(relativePath)
        .then(function(file) {
            if (!file) return null;

            return {
                name: path.basename(p),
                path: relativePath,
                type: statsToType(stats),
                sha: file.id,
                size: stats.size
            };
        });
    });
};

// Get infos for a file
Driver.prototype.stat = function(p, opts) {
    var that = this;

    return this.queue(function() {
        return that._stat(p, opts)
        .then(function(stats) {
            if (!stats) throw "File doesn't exist";

            return Q.nfcall(fs.readFile, that.path(p), 'utf-8')
            .then(function(content) {
                stats.content = content;
                return stats;
{'fraction_non_alphanumeric': 0.1409498056447524, 'fraction_numerical': 0.001014027378739226, 'mean_word_length': 1.9530938123752495, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4970303', 'n_tokens_mistral': 1801, 'n_tokens_neox': 1735, 'n_words': 472}