Skip to content

Commit

Permalink
feat: enhance CLI - more commands & accept stdin
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed Aug 25, 2021
1 parent 7d06ac5 commit 333b379
Show file tree
Hide file tree
Showing 2 changed files with 280 additions and 90 deletions.
165 changes: 125 additions & 40 deletions lib/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,145 @@
import process from 'process'
import { decode, encode } from '../cborg.js'
import { tokensToDiagnostic } from './diagnostic.js'
import { fromHex, toHex } from './byte-utils.js'

const cmd = process.argv[2]
import { fromHex as _fromHex, toHex } from './byte-utils.js'

/**
* @param {number} code
*/
function usage (code) {
console.error('Usage: cborg <command> <args>')
console.error('Valid commands:')
console.error('\thex2diag <hex input>')
console.error('\thex2json [--pretty] <hex input>')
console.error('\tjson2hex \'<json input>\'')
console.error('\thex2diag [hex input]')
console.error('\thex2bin [hex input]')
console.error('\thex2json [--pretty] [hex input]')
console.error('\tbin2hex [binary input]')
console.error('\tbin2diag [binary input]')
console.error('\tbin2json [--pretty] [binary input]')
console.error('\tjson2hex \'[json input]\'')
console.error('\tjson2diag \'[json input]\'')
console.error('\tjson2bin \'[json input]\'')
console.error('Input may either be supplied as an argument or piped via stdin')
process.exit(code || 0)
}

if (cmd === 'help') {
usage(0)
} else if (cmd === 'hex2json') {
const argv = process.argv.filter((s) => s !== '--pretty')
const pretty = argv.length !== process.argv.length
if (argv.length < 4) {
console.error('hex2json requires a hexadecimal input string')
usage(1)
}
const bin = fromHex(argv[3])
console.log(JSON.stringify(decode(bin), undefined, pretty ? 2 : undefined))
} else if (cmd === 'hex2diag') {
if (process.argv.length < 4) {
console.error('hex2diag requires a hexadecimal input string')
usage(1)
}
const bin = fromHex(process.argv[3])
for (const line of tokensToDiagnostic(bin)) {
console.log(line)
async function fromStdin () {
const chunks = []
for await (const chunk of process.stdin) {
chunks.push(chunk)
}
} else if (cmd === 'json2hex') {
if (process.argv.length < 4) {
console.error('json2hex requires a JSON input string')
usage(1)
return Buffer.concat(chunks)
}

/**
* @param {string} str
* @returns {Uint8Array}
*/
function fromHex (str) {
str = str.replace(/\r?\n/g, '') // let's be charitable
/* c8 ignore next 3 */
if (!(/^([0-9a-f]{2})*$/i).test(str)) {
throw new Error('Input string is not hexadecimal format')
}
const obj = JSON.parse(process.argv[3])
console.log(toHex(encode(obj)))
} else { // no, or unknown cmd
// this is a dirty hack to allow import of this package by the tests
// for inclusion in ipjs bundling, but to silently ignore it so we don't
// print usage and exit(1).
if (process.argv.findIndex((a) => a.endsWith('mocha')) === -1) {
if (cmd) {
console.error(`Unknown command: '${cmd}'`)
}
usage(1)
return _fromHex(str)
}

function argvPretty () {
const argv = process.argv.filter((s) => s !== '--pretty')
const pretty = argv.length !== process.argv.length
return { argv, pretty }
}

async function run () {
const cmd = process.argv[2]

switch (cmd) {
case 'help': {
return usage(0)
}

case 'hex2json': {
const { argv, pretty } = argvPretty()
const bin = fromHex(argv.length < 4 ? (await fromStdin()).toString() : argv[3])
return console.log(JSON.stringify(decode(bin), undefined, pretty ? 2 : undefined))
}

case 'hex2diag': {
const bin = fromHex(process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3])
for (const line of tokensToDiagnostic(bin)) {
console.log(line)
}
return
}

case 'hex2bin': {
// this is really nothing to do with cbor.. just handy
const bin = fromHex(process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3])
return process.stdout.write(bin)
}

case 'bin2hex': {
// this is really nothing to do with cbor.. just handy
/* c8 ignore next 1 */
const bin = process.argv.length < 4 ? (await fromStdin()) : new TextEncoder().encode(process.argv[3])
return console.log(toHex(bin))
}

case 'bin2json': {
const { argv, pretty } = argvPretty()
/* c8 ignore next 1 */
const bin = argv.length < 4 ? (await fromStdin()) : new TextEncoder().encode(argv[3])
return console.log(JSON.stringify(decode(bin), undefined, pretty ? 2 : undefined))
}

case 'bin2diag': {
/* c8 ignore next 1 */
const bin = process.argv.length < 4 ? (await fromStdin()) : new TextEncoder().encode(process.argv[3])
for (const line of tokensToDiagnostic(bin)) {
console.log(line)
}
return
}

case 'json2hex': {
const inp = process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]
const obj = JSON.parse(inp)
return console.log(toHex(encode(obj)))
}

case 'json2diag': {
const inp = process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]
const obj = JSON.parse(inp)
for (const line of tokensToDiagnostic(encode(obj))) {
console.log(line)
}
return
}

case 'json2bin': {
const inp = process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]
const obj = JSON.parse(inp)
return process.stdout.write(encode(obj))
}

default: { // no, or unknown cmd
// this is a dirty hack to allow import of this package by the tests
// for inclusion in ipjs bundling, but to silently ignore it so we don't
// print usage and exit(1).
if (process.argv.findIndex((a) => a.endsWith('mocha')) === -1) {
if (cmd) {
console.error(`Unknown command: '${cmd}'`)
}
usage(1)
}
}
}
}

run().catch((err) => {
/* c8 ignore next 2 */
console.error(err)
process.exit(1)
})

// for ipjs, to get it to compile
export default true
Loading

0 comments on commit 333b379

Please sign in to comment.