Skip to content

Commit

Permalink
initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
pfrazee committed Sep 13, 2015
1 parent 6023c0f commit 923696f
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 1 deletion.
57 changes: 57 additions & 0 deletions example.js
@@ -0,0 +1,57 @@
#! /usr/bin/env node

var muxrpcli = require('.')
var zerr = require('zerr')
var MissingArgError = zerr('BadArg', '"%" is required')
var BadTypeError = zerr('BadArg', '"%" must be a valid %')

function isAddress (v) {
return v && typeof v == 'string' && v.split('.').length == 4
}

var manifest = {
usage: 'async',
whoami: 'async',
ping: 'async'
}

var api = {
usage: function (cmd, cb) {
switch (cmd) {
case 'whoami':
return cb(null, 'whoami. get your profile info.')
case 'ping':
return cb(null, 'ping {target} [-n times]. send `n` pings to `target`, defaults to 1')
}
cb(null, [
'myexample usage:',
' - whoami. get your profile info.',
' - ping {target} [-n times]. send `n` pings to `target`, defaults to 1'
].join('\n'))
},

whoami: function(cb) {
cb(null, 'bob, obviously')
},

ping: function(target, opts, cb) {
if (typeof target == 'function') cb = target, target = null
if (typeof opts == 'function') cb = opts, opts = null

if (!target) return cb(MissingArgError('target'))
if (!isAddress(target)) return cb(BadTypeError('target', 'address'))

var n = 1
if (opts && opts.n) {
n = +opts.n
if (isNaN(n)) return cb(BadTypeError('n', 'number'))
}

for (var i=0; i < n; i++) {
console.log('ping', target)
}
cb()
}
}

muxrpcli(process.argv.slice(2), manifest, api)
131 changes: 131 additions & 0 deletions index.js
@@ -0,0 +1,131 @@
var minimist = require('minimist')
var toPull = require('stream-to-pull-stream')
var pull = require('pull-stream')

var usageErrors = [
'UsageError',
'BadParamError',
'BadArgError'
]

var isBuffer = Buffer.isBuffer
function isObject (o) {
return o && 'object' === typeof o && !Buffer.isBuffer(o)
}
function isString (s) {
return 'string' === typeof s
}
function isUsageError (err) {
return usageErrors.indexOf(err.name) >= 0
}

function maybeStringify () {
return pull.map(function (b) {
if(isBuffer(b)) return b
return JSON.stringify(b, null, 2) + '\n\n'
})
}

function get(obj, path) {
path.forEach(function (k) {
obj = obj ? obj[k] : null
})
return obj
}

function usage (cmd, manifest, rpc) {
var usageType = get(manifest, ['usage'])
var usageCmd = get(rpc, ['usage'])
if (!usageType || !usageCmd || (usageType != 'sync' && usageType != 'async'))
next(null, 'Invalid command')
else
usageCmd(Array.isArray(cmd) ? cmd.join('.') : cmd, next)

function next (err, str) {
if (err)
str = err.toString()
console.error(str)
process.exit(1)
}
}

function onerror (err, cmd, manifest, rpc) {
if (isUsageError(err)) {
console.log(err.name + ': ' + err.message)
usage(cmd, rpc, manifest)
} else {
console.error(err)
return process.exit(1)
}
}

module.exports = function (argv, manifest, rpc) {
// parse out `cmd`, `args`, and `isStdin`
var parsedArgv = minimist(argv)
var cmd = parsedArgv._[0], args = parsedArgv._.slice(1)
var isStdin = ('.' === args[0] || '--' === args[0])

delete parsedArgv._
if (Object.keys(parsedArgv).length)
args.push(parsedArgv)

// route to the command
if (!cmd)
return usage(false, manifest, rpc)
if (parsedArgv.h || parsedArgv.help)
return usage(cmd, manifest, rpc)
cmd = cmd.split('.')
var cmdType = get(manifest, cmd)
if (!cmdType)
return usage(false, manifest, rpc)

// handle stdin-mode
if(!process.stdin.isTTY && isStdin) {
pull(
toPull.source(process.stdin),
pull.collect(function (err, ary) {
var str = Buffer.concat(ary).toString('utf8')
var data = JSON.parse(str)
next([data])
})
)
}
else
next(args)

function next (args) {

if ('async' === cmdType || cmdType === 'sync') {
get(rpc, cmd).apply(null, args.concat([function (err, ret) {
if (err)
return onerror(err, cmd, rpc, manifest)
console.log(JSON.stringify(ret, null, 2))
process.exit()
}]))
}
else if ('source' === cmdType)
pull(
get(rpc, cmd).apply(null, args),
maybeStringify(),
toPull.sink(process.stdout, function (err) {
if (err)
return onerror(err, cmd, rpc, manifest)
process.exit()
})
)
else if ('sink' === cmdType)
pull(
toPull.source(process.stdin),
get(rpc, cmd).apply(null, args.concat([function (err, res) {
if (err)
return onerror(err, cmd, rpc, manifest)
console.log(JSON.stringify(res, null, 2))
process.exit()
}]))
)
else {
console.error('Invalid Manifest:', cmdType, 'is not a valid method-type')
process.exit(1)
}
}
}
10 changes: 9 additions & 1 deletion package.json
Expand Up @@ -12,5 +12,13 @@
"cli"
],
"author": "Paul Frazee <pfrazee@gmail.com>",
"license": "MIT"
"license": "MIT",
"dependencies": {
"minimist": "^1.2.0",
"pull-stream": "^2.28.3",
"stream-to-pull-stream": "^1.6.6"
},
"devDependencies": {
"zerr": "^1.0.0"
}
}

0 comments on commit 923696f

Please sign in to comment.