Skip to content

Instantly share code, notes, and snippets.

@jiangmiao
Created December 20, 2011 07:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jiangmiao/1500634 to your computer and use it in GitHub Desktop.
Save jiangmiao/1500634 to your computer and use it in GitHub Desktop.
copy-dir
(function() {
var confirm, copy, copyDir, copyFile, copyFileByLoop, copyFileByPipe, copyFileByPump, fs, path, print, puts, readline, strError, util;
fs = require('fs');
path = require('path');
util = require('util');
readline = require('readline').createInterface(process.stdin, process.stdout);
puts = function() {
return console.info.apply(console, arguments);
};
print = function() {
return process.stdout.write.apply(process.stdout, arguments);
};
strError = function(err) {
if (err) {
return "\033[31mERROR!!! " + err.message + "\033[0m";
} else {
return "\033[32mdone.\033[0m";
}
};
confirm = function(text, cb) {
var once;
return (once = function() {
var _this = this;
return readline.question(text, function(_$$_answer) {
var answer;
answer = _$$_answer;
if (answer.match(/^(yes|y)$/i)) {
return cb(true);
} else if (answer.match(/^(no|n)$/i)) {
return cb(false);
} else {
return once();
}
});
})();
};
copyFileByPump = function(src, dest, cb) {
var readStream, writeStream;
readStream = fs.createReadStream(src);
writeStream = fs.createWriteStream(dest);
writeStream.end = function() {
if (this.fd != null) {
return fs.WriteStream.prototype.end.apply(this, arguments);
}
};
return util.pump(readStream, writeStream, cb);
};
copyFileByLoop = function(src, dest, cb) {
var dest_fd, done, src_fd,
_this = this;
src_fd = dest_fd = null;
done = function(err) {
if (src_fd != null) fs.close(src_fd);
if (dest_fd != null) fs.close(dest_fd);
return cb(err);
};
return fs.open(src, "r", function(_$$_err, _$$_src_fd) {
var err;
err = _$$_err;
src_fd = _$$_src_fd;
if (err) return done(err);
return fs.open(dest, "w", function(_$$_err, _$$_dest_fd) {
var buffer, buffer_size, once;
err = _$$_err;
dest_fd = _$$_dest_fd;
if (err) return done(err);
buffer_size = 4 * 4096;
buffer = new Buffer(buffer_size);
return (once = function() {
var _this = this;
return fs.read(src_fd, buffer, 0, buffer_size, null, function(_$$_err, _$$_bytesRead) {
var bytesRead;
err = _$$_err;
bytesRead = _$$_bytesRead;
if (err || bytesRead === 0) return done(err);
return fs.write(dest_fd, buffer, 0, bytesRead, null, function(_$$_err) {
err = _$$_err;
if (err) return done(err);
return once();
});
});
})();
});
});
};
copyFileByPipe = function(src, dest, cb) {
var called, done, readStream, writeStream;
readStream = fs.createReadStream(src);
writeStream = fs.createWriteStream(dest);
called = false;
done = function(err) {
if (called) return;
called = true;
if (err) {
readStream.destroy();
writeStream.destroy();
}
return cb(err);
};
readStream.on('error', done);
writeStream.on('error', done);
writeStream.on('close', done);
return readStream.pipe(writeStream);
};
copyFile = function(src, dest, cb) {
var _this = this;
return path.exists(dest, function(_$$_exists) {
var exists, _asfn0;
exists = _$$_exists;
_asfn0 = function() {
print("Copying \"" + src + "\" to \"" + dest + "\" ... ");
return copyFileByLoop(src, dest, function(_$$_err) {
var err;
err = _$$_err;
puts(strError(err));
return cb();
});
};
if (exists) {
return confirm("File \"" + dest + "\" exists, overwrite? (yes/no) > ", function(_$$_overwrite) {
var overwrite;
overwrite = _$$_overwrite;
if (!overwrite) return cb();
return _asfn0();
});
} else {
return _asfn0();
}
});
};
copyDir = function(src, dest, cb) {
var _this = this;
return path.exists(dest, function(_$$_exists) {
var exists, _asfn1;
exists = _$$_exists;
_asfn1 = function() {
return fs.readdir(src, function(_$$_err, _$$_files) {
var err, files, once;
err = _$$_err;
files = _$$_files;
if (err) {
puts(strError(new Error("Cannot open directory \"" + src + "\"")));
return cb();
}
return (once = function() {
var file, subdest, subsrc,
_this = this;
file = files.shift();
if (!file) return cb();
subsrc = path.join(src, file);
subdest = path.join(dest, file);
return copy(subsrc, subdest, function() {
return once();
});
})();
});
};
if (!exists) {
print("Creating directory \"" + dest + "\" ... ");
return fs.mkdir(dest, function(_$$_err) {
var err;
err = _$$_err;
puts(strError(err));
if (err) return cb();
return _asfn1();
});
} else {
return _asfn1();
}
});
};
copy = function(src, dest, cb) {
var _this = this;
return fs.stat(src, function(_$$_err, _$$_src_stat) {
var err, src_stat;
err = _$$_err;
src_stat = _$$_src_stat;
if (err) {
puts(strError(err));
return cb();
}
if (src_stat.isFile()) {
return copyFile(src, dest, cb);
} else {
return copyDir(src, dest, cb);
}
});
};
copy(process.argv[2], process.argv[3], function() {
puts('Finished.');
readline.close();
return process.stdin.destroy();
});
}).call(this);
# 本代码是 @老赵 的[copy-dir](https://github.com/JeffreyZhao/jscex/blob/master/doc/async/samples/copy-dir-cn.md)的ToffeeScript实现
# Jscex版本位于[Jscex版copy-dir](https://github.com/JeffreyZhao/jscex/blob/master/samples/async/copy-dir.js)
#
# 直接感觉就是异常处理上,Jscex由于支持异常大大节省的错误的判断次数。另外全代码几乎都可以用同步的结构写异步,异常的方便。
#
# ToffeeScript代码从理解上基本等同与不使用任何类库原生JavaScript,只是更改了回调函数的位置。由于CoffeeScript的语法方便,异步写起来也不冗长,只是出口有点多。
fs = require :fs
path = require :path
util = require :util
readline = require(:readline).createInterface process.stdin, process.stdout
puts = -> console.info.apply console, arguments
print = -> process.stdout.write.apply process.stdout, arguments
strError = (err) -> if err then "\033[31mERROR!!! #{err.message}\033[0m" else "\033[32mdone.\033[0m"
# cb = (bool) ->
confirm = (text, cb) ->
do once = ->
answer = readline.question! text
if answer.match /^(yes|y)$/i
cb(true)
else if answer.match /^(no|n)$/i
cb(false)
else
once()
# cb = (err) ->
copyFileByPump = (src, dest, cb) ->
readStream = fs.createReadStream src
writeStream = fs.createWriteStream dest
# 用util.pump, 如果src为0字节且dest不可写, 会出uncaughtException, 弃用
writeStream.end = ->
if @fd?
fs.WriteStream::end.apply @, arguments
util.pump readStream, writeStream, cb
# 比起 util.pump 用传统的的read + write 更可控些
# cb = (err) ->
copyFileByLoop = (src, dest, cb) ->
src_fd = dest_fd = null
done = (err) ->
fs.close src_fd if src_fd?
fs.close dest_fd if dest_fd?
cb(err)
err, src_fd = fs.open! src, "r"
return done(err) if err
err, dest_fd = fs.open! dest, "w"
return done(err) if err
buffer_size = 4*4096
buffer = new Buffer(buffer_size)
do once = ->
err, bytesRead = fs.read! src_fd, buffer, 0, buffer_size, null
return done(err) if err || bytesRead == 0
err = fs.write! dest_fd, buffer, 0, bytesRead, null
return done(err) if err
once()
# cb = (err) ->
copyFileByPipe = (src, dest, cb) ->
readStream = fs.createReadStream src
writeStream = fs.createWriteStream dest
called = false
done = (err) ->
return if called
called = true
if err
readStream.destroy()
writeStream.destroy()
cb(err)
readStream.on :error, done
writeStream.on :error, done
writeStream.on :close, done
readStream.pipe writeStream
# cb = ->
copyFile = (src, dest, cb) ->
exists = path.exists! dest
if! exists
overwrite = confirm! "File \"#{dest}\" exists, overwrite? (yes/no) > "
return cb() unless overwrite
print "Copying \"#{src}\" to \"#{dest}\" ... "
err = copyFileByLoop! src, dest
# err = copyFileByPipe! src, dest
puts strError(err)
cb()
# cb = ->
copyDir = (src, dest, cb) ->
exists = path.exists! dest
unless! exists
print "Creating directory \"#{dest}\" ... "
err = fs.mkdir! dest
puts strError(err)
return cb() if err
err, files = fs.readdir! src
if err
puts strError new Error "Cannot open directory \"#{src}\""
return cb()
do once = ->
file = files.shift()
return cb() unless file
subsrc = path.join src, file
subdest = path.join dest, file
copy! subsrc, subdest
once()
# cb = ->
copy = (src, dest, cb) ->
err, src_stat = fs.stat! src
if err
puts strError(err)
return cb()
if src_stat.isFile()
copyFile src, dest, cb
else
copyDir src, dest, cb
copy process.argv[2], process.argv[3], ->
puts 'Finished.'
readline.close()
process.stdin.destroy()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment