cli (4817B)
1 #!/usr/bin/env node 2 3 /** 4 * @license Apache-2.0 5 * 6 * Copyright (c) 2018 The Stdlib Authors. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 'use strict'; 22 23 // MODULES // 24 25 var proc = require( 'process' ); 26 var resolve = require( 'path' ).resolve; 27 var readFileSync = require( '@stdlib/fs/read-file' ).sync; 28 var writeFileSync = require( '@stdlib/fs/write-file' ).sync; 29 var CLI = require( '@stdlib/cli/ctor' ); 30 var stdout = require( '@stdlib/streams/node/stdout' ); 31 var cwd = require( '@stdlib/process/cwd' ); 32 var Uint8Array = require( '@stdlib/array/uint8' ); 33 var Uint32Array = require( '@stdlib/array/uint32' ); 34 var isUint8Array = require( '@stdlib/assert/is-uint8array' ); 35 var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive; 36 var gcopy = require( '@stdlib/blas/base/gcopy' ); 37 var array2buffer = require( '@stdlib/buffer/from-array' ); 38 var randomStream = require( './../lib' ); 39 40 41 // FUNCTIONS // 42 43 /** 44 * Callback invoked once a source stream ends. 45 * 46 * @private 47 */ 48 function onEnd() { 49 // Append a trailing newline in accordance with standard POSIX behavior: 50 console.log( '' ); // eslint-disable-line no-console 51 } 52 53 /** 54 * Attempts to load a PRNG state. 55 * 56 * @private 57 * @param {string} filepath - absolute path to file containing PRNG state 58 * @returns {(Uint32Array|Error)} PRNG state or an error 59 */ 60 function loadState( filepath ) { 61 var state; 62 var len; 63 64 state = readFileSync( filepath ); 65 if ( state instanceof Error ) { 66 return state; 67 } 68 len = state.length; 69 70 // For older Node.js environments, convert the `Buffer` to a `Uint8Array`... 71 if ( !isUint8Array( state ) ) { 72 state = gcopy( len, state, 1, new Uint8Array( len ), 1 ); 73 } 74 // Create a PRNG state array "view": 75 len /= Uint32Array.BYTES_PER_ELEMENT; 76 if ( !isInteger( len ) ) { 77 return new RangeError( 'invalid option. `state` has an invalid length.' ); 78 } 79 return new Uint32Array( state.buffer, state.byteOffset, len ); 80 } 81 82 83 // MAIN // 84 85 /** 86 * Main execution sequence. 87 * 88 * @private 89 * @returns {void} 90 */ 91 function main() { 92 var stream; 93 var flags; 94 var opts; 95 var args; 96 var cli; 97 var err; 98 var dir; 99 var i; 100 101 // Create a command-line interface: 102 cli = new CLI({ 103 'pkg': require( './../package.json' ), 104 'options': require( './../etc/cli_opts.json' ), 105 'help': readFileSync( resolve( __dirname, '..', 'docs', 'usage.txt' ), { 106 'encoding': 'utf8' 107 }) 108 }); 109 110 // Get any provided command-line options: 111 flags = cli.flags(); 112 if ( flags.help || flags.version ) { 113 return; 114 } 115 116 // Get the current working directory: 117 dir = cwd(); 118 119 // Get any provided command-line arguments: 120 args = cli.args(); 121 if ( args.length < 2 ) { 122 err = new Error( 'insufficient arguments. Must provide distribution parameters.' ); 123 return onError( err ); 124 } 125 args[ 0 ] = parseFloat( args[ 0 ] ); 126 args[ 1 ] = parseFloat( args[ 1 ] ); 127 128 opts = {}; 129 if ( flags.iter ) { 130 opts.iter = parseInt( flags.iter, 10 ); 131 } 132 if ( flags.sep ) { 133 opts.sep = flags.sep; 134 } 135 if ( flags.state ) { 136 opts.state = loadState( resolve( dir, flags.state ) ); 137 if ( opts.state instanceof Error ) { 138 return onError( opts.state ); 139 } 140 } else if ( flags.seed ) { 141 opts.seed = flags.seed.split( ',' ); 142 for ( i = 0; i < opts.seed.length; i++ ) { 143 opts.seed[ i ] = parseInt( opts.seed[ i ], 10 ); 144 } 145 } 146 if ( flags.snapshot ) { 147 proc.on( 'exit', onExit ); 148 } 149 150 // Create a source stream and pipe to `stdout`: 151 stream = randomStream( args[ 0 ], args[ 1 ], opts ); 152 stream.on( 'end', onEnd ); 153 154 stream.pipe( stdout ); 155 156 /** 157 * Callback invoked upon exiting the process. 158 * 159 * @private 160 * @param {integer} code - exit code 161 */ 162 function onExit( code ) { 163 var state; 164 var err; 165 166 // Get the current PRNG state: 167 state = stream.state; 168 169 // Create a byte array "view": 170 state = new Uint8Array( state.buffer, state.byteOffset, stream.byteLength ); // eslint-disable-line max-len 171 172 // Convert the byte array to a `Buffer` (with support for older Node.js environments): 173 state = array2buffer( state ); 174 175 // Attempt to write the state to file: 176 err = writeFileSync( resolve( dir, flags.snapshot ), state ); 177 if ( err ) { 178 onError( err, code || 1 ); 179 } 180 } 181 182 /** 183 * Callback invoked upon encountering an error. 184 * 185 * @private 186 * @param {Error} error - error 187 * @param {integer} [code] - exit code 188 */ 189 function onError( error, code ) { 190 cli.error( error, code || 1 ); 191 } 192 } 193 194 main();