cbor
Version:
Encode and parse CBOR documents.
177 lines (150 loc) • 5.2 kB
text/coffeescript
# jslint node: true
stream = require 'stream'
Evented = require './evented'
BufferStream = require '../lib/BufferStream'
Simple = require '../lib/simple'
utils = require '../lib/utils'
# Output the diagnostic format from a set of CBOR bytes. Either pipe another
# stream into an instance of this class, or pass a string, Buffer, or
# BufferStream into `options.input`, assign callbacks, then call `start()`.
#
# @event end() Done processing the input
# @event error(er) An error has occured
# @param er [Error]
# @event complete(obj) A complete CBOR object has been read from the stream
# There is no need to hook this event usually, it's mostly for testing.
# @param obj [Stream] the stream that was written to
# @todo Consider turning this into a Transform stream
module.exports = class Diagnose extends stream.Writable
# Create a Diagnose.
# @param options [Object] options for creation
# @option options separator [String] output between detected objects (default: '\n')
# @option options output [Writable] where the output should go (default: process.stdout)
# @option options input [Buffer,String,BufferStream] optional input
# @option options encoding [String] encoding of a String `input` (default: 'hex')
# @option options offset [Integer] *byte* offset into the input from which to start
constructor: (options={}) ->
super()
@options = utils.extend
separator: '\n'
output: process.stdout
streamErrors: false
, options
@on 'finish', ()->
@parser.end()
@parser = new Evented @options
@parser.on 'value', @_on_value
@parser.on 'array-start', @_on_array_start
@parser.on 'array-stop', @_on_array_stop
@parser.on 'map-start', @_on_map_start
@parser.on 'map-stop', @_on_map_stop
@parser.on 'stream-start', @_on_stream_start
@parser.on 'stream-stop', @_on_stream_stop
@parser.on 'end', @_on_end
@parser.on 'error', @_on_error
# All events have been hooked, start parsing the input.
# @note This MUST NOT be called if you're piping a Readable stream in.
start: ()->
@parser.start()
# Convenience function to print to (e.g.) stdout.
# @param input [Buffer,String,BufferStream] the CBOR bytes to write
# @param encoding [String] encoding if `input` is a string (default: 'hex')
# @param output [Writable] Writable stream to output diagnosis info into
# (default: process.stdout)
# @param done [function()]
@diagnose: (input, encoding='hex', output=process.stdout, done)->
if !input?
throw new Error 'input required'
d = new Diagnose
input: input
encoding: encoding
output: output
if done
d.on 'end', done
d.start()
# @nodoc
_stream_val: (val) ->
@options.output.write switch
when val == undefined then 'undefined'
when val == null then 'nil'
when typeof(val) == 'number'
if isNaN(val) then "NaN"
else if !isFinite(val)
if val < 0 then '-Infinity' else 'Infinity'
else
JSON.stringify(val)
when Simple.isSimple(val) then val.toString()
when Buffer.isBuffer(val) then "h'" + val.toString('hex') + "'"
else JSON.stringify(val)
# @nodoc
_on_error: (er) =>
if @options.streamErrors
@options.output.write er.toString()
@emit 'error', er
# @nodoc
_fore: (kind) ->
switch kind
when 'array', 'key', 'stream' then @options.output.write ', '
# @nodoc
_aft: (kind) ->
switch kind
when 'key', 'key-first' then @options.output.write ': '
when null
if @options.separator?
@options.output.write @options.separator
@emit 'complete', @options.output
# @nodoc
_on_value: (val,tags,kind)=>
@_fore kind
if tags? and tags.length
@options.output.write "#{t}(" for t in tags
@_stream_val val
if tags? and tags.length
@options.output.write ")" for t in tags
@_aft kind
# @nodoc
_on_array_start: (count,tags,kind)=>
@_fore kind
if tags? and tags.length
@options.output.write "#{t}(" for t in tags
@options.output.write "["
if count == -1
@options.output.write "_ "
# @nodoc
_on_array_stop: (count,tags,kind)=>
@options.output.write "]"
if tags? and tags.length
@options.output.write ")" for t in tags
@_aft kind
# @nodoc
_on_map_start: (count,tags,kind)=>
@_fore kind
if tags? and tags.length
@options.output.write "#{t}(" for t in tags
@options.output.write "{"
if count == -1
@options.output.write "_ "
# @nodoc
_on_map_stop: (count,tags,kind)=>
@options.output.write "}"
if tags? and tags.length
@options.output.write ")" for t in tags
@_aft kind
# @nodoc
_on_stream_start: (mt,tags,kind)=>
@_fore kind
if tags? and tags.length
@options.output.write "#{t}(" for t in tags
@options.output.write "(_ "
# @nodoc
_on_stream_stop: (count,mt,tags,kind)=>
@options.output.write ")"
if tags? and tags.length
@options.output.write ")" for t in tags
@_aft kind
# @nodoc
_on_end: ()=>
@emit 'end'
# @nodoc
_write: (chunk, enc, next)->
@parser.write chunk, enc, next