Skip to content

Commit

Permalink
feat: add format command
Browse files Browse the repository at this point in the history
resolves #2

License: MIT
Signed-off-by: Alan Shaw <[email protected]>
  • Loading branch information
Alan Shaw committed Sep 13, 2018
1 parent d6b2e86 commit a7b8fa7
Show file tree
Hide file tree
Showing 6 changed files with 395 additions and 7 deletions.
105 changes: 99 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,57 @@ npm install cid-tool # for programmatic usage
### CLI

```console
$ cid
$ cid --help
cid <command>

Commands:
cid base32 [cids...] Convert CIDs to base 32 CID version 1.
cid bases List available multibase encoding names.
cid codecs List available CID codec names.
cid hashes List available multihash hashing algorithm
names.
cid format [cids...] Format and convert a CID in various useful ways.
cid hashes List available multihash hashing algorithm names.

Options:
--version Show version number [boolean]
--help Show help [boolean]
--version Show version number [boolean]
--help Show help [boolean]
```

Help is also available for each command e.g.

```console
$ cid format --help
cid format [cids...]

Format and convert a CID in various useful ways.

Options:
--version Show version number [boolean]
--help Show help [boolean]
--format, -f Printf style format string:

%% literal %
%b multibase name
%B multibase code
%v version string
%V version number
%c codec name
%C codec code
%h multihash name
%H multihash code
%L hash digest length
%m multihash encoded in base %b (with multibase prefix)
%M multihash encoded in base %b without multibase prefix
%d hash digest encoded in base %b (with multibase prefix)
%D hash digest encoded in base %b without multibase prefix
%s cid string encoded in base %b (1)
%S cid string encoded in base %b without multibase prefix
%P cid prefix: %v-%c-%h-%L

(1) For CID version 0 the multibase must be base58btc and
no prefix is used. For Cid version 1 the multibase prefix
is included. [string] [default: "%s"]
--cid-version, -v CID version to convert to. [number]
--base, -b Multibase to display output in. [string]
```

### Module
Expand All @@ -51,7 +89,7 @@ Convert the passed CID to base 32 CID version 1.

| Name | Type | Description |
|------|------|-------------|
| cids |[`CID`](https://github.com/ipld/js-cid/)\|`String`\|`Buffer` | CID to convert. |
| cid | [`CID`](https://github.com/ipld/js-cid/)\|`String`\|`Buffer` | CID to convert. |

#### Returns

Expand Down Expand Up @@ -122,6 +160,61 @@ List available [CID codec name and code pairs](https://github.com/multiformats/m
/* ... */ ]
```

### `CIDTool.format(cid, [options])`

Format and convert a CID in various useful ways.

#### Parameters

| Name | Type | Description |
|------|------|-------------|
| cid | [`CID`](https://github.com/ipld/js-cid/)\|`String`\|`Buffer` | CID to format |
| options | `Object` | (optional) options for formatting |
| options.format | `String` | Format string to use, default "%s" |
| options.base | `String` | Multibase name or code to use for output |
| options.cidVersion | `Number` | Convert the CID to the given version if possible |

Available format specifiers:

* %% literal %
* %b multibase name
* %B multibase code
* %v version string
* %V version number
* %c codec name
* %C codec code
* %h multihash name
* %H multihash code
* %L hash digest length
* %m multihash encoded in base %b (with multibase prefix)
* %M multihash encoded in base %b without multibase prefix
* %d hash digest encoded in base %b (with multibase prefix)
* %D hash digest encoded in base %b without multibase prefix
* %s cid string encoded in base %b (1)
* %S cid string encoded in base %b without multibase prefix
* %P cid prefix: %v-%c-%h-%L

(1) For CID version 0 the multibase must be base58btc and no prefix is used. For Cid version 1 the multibase prefix is included.

#### Returns

| Type | Description |
|------|-------------|
| `String` | Formatted string |

#### Example

```js
> CIDTool.format('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn', { format: '%b-%v-%c-%h-%L' })
'base58btc-cidv0-dag-pb-sha2-256-32'

> CIDTool.format('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn', { cidVersion: 1 })
'zdj7WbTaiJT1fgatdet9Ei9iDB5hdCxkbVyhyh8YTUnXMiwYi'

> CIDTool.format('zdj7WksYf5DNoDhTbjNZundK13TdEYo9sNaFWYZuKBM3fNszf', { base: 'base64' })
'mAXASIOVxUFxiKImgAexk+1WFjCsjtgtavDHCEHEPTCZ1aacA'
```

### `CIDTool.hashes()`

List available [multihash hashing algorithm name and code pairs](https://github.com/multiformats/multihash/blob/master/hashtable.csv).
Expand Down
65 changes: 65 additions & 0 deletions src/cli/commands/format.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict'

const CIDTool = require('../../')

module.exports = {
command: 'format [cids...]',

describe: 'Format and convert a CID in various useful ways.',

builder: {
format: {
describe: `Printf style format string:
%% literal %
%b multibase name
%B multibase code
%v version string
%V version number
%c codec name
%C codec code
%h multihash name
%H multihash code
%L hash digest length
%m multihash encoded in base %b (with multibase prefix)
%M multihash encoded in base %b without multibase prefix
%d hash digest encoded in base %b (with multibase prefix)
%D hash digest encoded in base %b without multibase prefix
%s cid string encoded in base %b (1)
%S cid string encoded in base %b without multibase prefix
%P cid prefix: %v-%c-%h-%L
(1) For CID version 0 the multibase must be base58btc and no prefix is used. For Cid version 1 the multibase prefix is included.`,
alias: 'f',
type: 'string',
default: '%s'
},
'cid-version': {
describe: 'CID version to convert to.',
alias: 'v',
type: 'number'
},
base: {
describe: 'Multibase to display output in.',
alias: 'b',
type: 'string'
}
},

handler (argv) {
const options = {
format: argv.format,
cidVersion: argv.cidVersion,
base: argv.base
}

if (argv.cids && argv.cids.length) {
return argv.cids.forEach(cid => console.log(CIDTool.format(cid, options)))
}

process.stdin.on('data', data => {
const cid = data.toString().trim()
console.log(CIDTool.format(cid, options))
})
}
}
2 changes: 1 addition & 1 deletion src/core/base32.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = function base32 (cid) {
try {
cid = new CID(cid)
} catch (err) {
throw explain(err, `invalid cid ${cid}`)
throw explain(err, `invalid cid: ${cid}`)
}

if (cid.version !== 1) {
Expand Down
110 changes: 110 additions & 0 deletions src/core/format.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use strict'

const CID = require('cids')
const bases = require('./bases')
const codecs = require('./codecs')
const explain = require('explain-error')
const multibase = require('multibase')
const multihash = require('multihashes')

module.exports = function format (cid, options) {
options = options || {}

let formatStr = options.format || '%s'

if (formatStr === 'prefix') {
formatStr = '%P'
}

if (!isString(formatStr) || formatStr.indexOf('%') === -1) {
throw new Error(`invalid format string: ${formatStr}`)
}

const originalCid = cid

try {
cid = new CID(cid)
} catch (err) {
throw explain(err, `invalid cid: ${cid}`)
}

if (options.cidVersion != null && cid.version !== options.cidVersion) {
if (options.cidVersion === 0) {
cid = cid.toV0()
} else if (options.cidVersion === 1) {
cid = cid.toV1()
} else {
throw new Error(`invalid cid version: ${options.cidVersion}`)
}
}

let base

if (options.base) {
base = options.base
} else if (isString(originalCid)) {
// Use base of input CID if string
base = multibase.isEncoded(originalCid)
}

base = base || 'base58btc'

// Using multibase code instead of name
if (base.length === 1) {
const baseNameCode = bases().find(b => b.code === base)
if (!baseNameCode) throw new Error(`invalid multibase: ${base}`)
base = baseNameCode.name
}

return formatStr.replace(/%([a-zA-Z%])/g, replacer(cid, base, options))
}

function isString (obj) {
return Object.prototype.toString.call(obj) === '[object String]'
}

function replacer (cid, base, options) {
return (match, specifier) => {
switch (specifier) {
case '%':
return '%'
case 'b': // base name
return base
case 'B': // base code
return bases().find(b => b.name === base).code
case 'v': // version string
return `cidv${cid.version}`
case 'V': // version num
return cid.version
case 'c': // codec name
return cid.codec
case 'C': // codec code
return codecs().find(c => c.name === cid.codec).code
case 'h': // hash fun name
return multihash.decode(cid.multihash).name
case 'H': // hash fun code
return multihash.decode(cid.multihash).code
case 'L': // hash length
return multihash.decode(cid.multihash).length
case 'm': // multihash encoded in base %b
return multibase.encode(base, cid.multihash)
case 'M': // multihash encoded in base %b without base prefix
return multibase.encode(base, cid.multihash).slice(1)
case 'd': // hash digest encoded in base %b
return multibase.encode(base, multihash.decode(cid.multihash).digest)
case 'D': // hash digest encoded in base %b without base prefix
return multibase.encode(base, multihash.decode(cid.multihash).digest).slice(1)
case 's': // cid string encoded in base %b
return cid.toBaseEncodedString(base)
case 'S': // cid string without base prefix
return cid.version === 1
? cid.toBaseEncodedString(base).slice(1)
: multibase.encode(base, cid.buffer).toString().slice(1)
case 'P': // prefix
const { name, length } = multihash.decode(cid.multihash)
return `cidv${cid.version}-${cid.codec}-${name}-${length}`
default:
throw new Error(`unrecognized specifier in format string: ${specifier}`)
}
}
}
32 changes: 32 additions & 0 deletions test/cli/format.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* eslint-env mocha */
'use strict'

const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const CIDToolCli = require('./utils/cid-tool-cli')
const TestCID = require('../fixtures/test-cid')

describe('cli format', () => {
it('should format CID and change to CIDv1', async () => {
const cli = CIDToolCli()
const expectedOutput = TestCID.b58 + '\n'
const { stdout } = await cli(`format ${TestCID.v0} --cid-version=1`)
expect(stdout).to.equal(expectedOutput)
})

it('should format CID and change to base64', async () => {
const cli = CIDToolCli()
const expectedOutput = TestCID.b64 + '\n'
const { stdout } = await cli(`format ${TestCID.b32} --base=base64`)
expect(stdout).to.equal(expectedOutput)
})

it('should format CID and change to CIDv1 and base32', async () => {
const cli = CIDToolCli()
const expectedOutput = TestCID.b32 + '\n'
const { stdout } = await cli(`format ${TestCID.v0} --cid-version=1 --base=base32`)
expect(stdout).to.equal(expectedOutput)
})
})
Loading

0 comments on commit a7b8fa7

Please sign in to comment.