Skip to content

Instantly share code, notes, and snippets.

@erwinv
Last active February 8, 2019 22:57
Show Gist options
  • Save erwinv/0a34ac96a85f2a982af27e269829afdb to your computer and use it in GitHub Desktop.
Save erwinv/0a34ac96a85f2a982af27e269829afdb to your computer and use it in GitHub Desktop.
backup paths exercise
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const readline = require('readline')
const promisify = require('util').promisify || function(fn) {
return function(...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, ...results) => {
if (err) reject(err)
else resolve(...results)
})
})
}
}
Array.prototype.flatMap = Array.prototype.flatMap || function(mapFn) {
return this.reduce((acc, x) => acc.concat(mapFn(x)), [])
}
Array.prototype.flat = Array.prototype.flat || function() {
return this.flatMap(arr => arr)
}
const readdirAsync = promisify(fs.readdir)
const statAsync = promisify(fs.stat)
function readFirstLineAsync(filepath) {
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(filepath, 'utf8')
readStream.on('error', reject)
const rl = readline.createInterface({input: readStream})
rl.on('error', reject)
rl.on('line', line => {
readStream.close()
resolve(line)
})
})
}
function getBackupPathsAsync(dir, parentpolicy='branch') {
return Promise.resolve(parentpolicy == 'branch' ?
readFirstLineAsync(path.join(dir, '.backuppolicy'))
.then(val => val.trim())
.then(policy => ['leaf', 'branch', 'ignore'].includes(policy) ? policy : 'leaf')
.catch(_ => 'leaf')
: 'leaf')
.then(policy =>
Promise.resolve(policy == 'ignore' ? [] : readdirAsync(dir))
.then(basenames => basenames.map(base => path.normalize(path.format({dir, base}))))
.catch(_ => [])
.then(fullpaths => {
const withStatsP = fullpaths.map(fullpath =>
statAsync(fullpath)
.catch(_ => ({isFile: () => false, isDirectory: () => false}))
.then(stats => ({fullpath, stats})))
return Promise.all(withStatsP)
})
.then(pathsWithStats => {
const recursivePathsP = pathsWithStats.map(({fullpath, stats}) =>
stats.isFile() ? Promise.resolve([fullpath]) :
stats.isDirectory() ? getBackupPathsAsync(fullpath, policy) :
Promise.resolve([])
)
return Promise.all(recursivePathsP)
}))
.then(subpaths => subpaths.flat())
}
const runningAsMain = require.main == module && !module.parent
if (runningAsMain) {
if (process.argv.length < 3)
return console.error('no dirpath given')
const [,, dir] = process.argv
const log = (a) => console.log(a)
getBackupPathsAsync(dir)
.then(paths => paths.forEach(log))
} else {
Object.assign(module.exports, {
getBackupPathsAsync
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment