cli (4890B)
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 Int32Array = require( '@stdlib/array/int32' ); 34 var Uint32Array = require( '@stdlib/array/uint32' ); 35 var isUint8Array = require( '@stdlib/assert/is-uint8array' ); 36 var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive; 37 var gcopy = require( '@stdlib/blas/base/gcopy' ); 38 var array2buffer = require( '@stdlib/buffer/from-array' ); 39 var randomStream = require( './../lib' ); 40 var DEFAULTS = require( './../lib/defaults.json' ); 41 42 43 // VARIABLES // 44 45 var ctors = { 46 'mt19937': Uint32Array, 47 'minstd': Int32Array, 48 'minstd-shuffle': Int32Array 49 }; 50 51 52 // FUNCTIONS // 53 54 /** 55 * Callback invoked once a source stream ends. 56 * 57 * @private 58 */ 59 function onEnd() { 60 // Append a trailing newline in accordance with standard POSIX behavior: 61 console.log( '' ); // eslint-disable-line no-console 62 } 63 64 /** 65 * Attempts to load a PRNG state. 66 * 67 * @private 68 * @param {string} filepath - absolute path to file containing PRNG state 69 * @param {string} name - PRNG name 70 * @returns {(Uint32Array|Int32Array|Error)} PRNG state or an error 71 */ 72 function loadState( filepath, name ) { 73 var state; 74 var ctor; 75 var len; 76 77 state = readFileSync( filepath ); 78 if ( state instanceof Error ) { 79 return state; 80 } 81 len = state.length; 82 83 // For older Node.js environments, convert the `Buffer` to a `Uint8Array`... 84 if ( !isUint8Array( state ) ) { 85 state = gcopy( len, state, 1, new Uint8Array( len ), 1 ); 86 } 87 // Create a PRNG state array "view": 88 ctor = ctors[ name ]; 89 len /= ctor.BYTES_PER_ELEMENT; 90 if ( !isInteger( len ) ) { 91 return new RangeError( 'invalid option. `state` has an invalid length.' ); 92 } 93 return new ctor( state.buffer, state.byteOffset, len ); 94 } 95 96 97 // MAIN // 98 99 /** 100 * Main execution sequence. 101 * 102 * @private 103 * @returns {void} 104 */ 105 function main() { 106 var stream; 107 var flags; 108 var opts; 109 var cli; 110 var dir; 111 var i; 112 113 // Create a command-line interface: 114 cli = new CLI({ 115 'pkg': require( './../package.json' ), 116 'options': require( './../etc/cli_opts.json' ), 117 'help': readFileSync( resolve( __dirname, '..', 'docs', 'usage.txt' ), { 118 'encoding': 'utf8' 119 }) 120 }); 121 122 // Get any provided command-line options: 123 flags = cli.flags(); 124 if ( flags.help || flags.version ) { 125 return; 126 } 127 128 // Get the current working directory: 129 dir = cwd(); 130 131 opts = {}; 132 if ( flags.iter ) { 133 opts.iter = parseInt( flags.iter, 10 ); 134 } 135 if ( flags.sep ) { 136 opts.sep = flags.sep; 137 } 138 if ( flags.name ) { 139 opts.name = flags.name; 140 } 141 if ( flags.state ) { 142 opts.state = loadState( resolve( dir, flags.state ), opts.name || DEFAULTS.name ); // eslint-disable-line max-len 143 if ( opts.state instanceof Error ) { 144 return onError( opts.state ); 145 } 146 } else if ( flags.seed ) { 147 opts.seed = flags.seed.split( ',' ); 148 for ( i = 0; i < opts.seed.length; i++ ) { 149 opts.seed[ i ] = parseInt( opts.seed[ i ], 10 ); 150 } 151 } 152 if ( flags.snapshot ) { 153 proc.on( 'exit', onExit ); 154 } 155 156 // Create a source stream and pipe to `stdout`: 157 stream = randomStream( opts ); 158 stream.on( 'end', onEnd ); 159 160 stream.pipe( stdout ); 161 162 /** 163 * Callback invoked upon exiting the process. 164 * 165 * @private 166 * @param {integer} code - exit code 167 */ 168 function onExit( code ) { 169 var state; 170 var err; 171 172 // Get the current PRNG state: 173 state = stream.state; 174 175 // Create a byte array "view": 176 state = new Uint8Array( state.buffer, state.byteOffset, stream.byteLength ); // eslint-disable-line max-len 177 178 // Convert the byte array to a `Buffer` (with support for older Node.js environments): 179 state = array2buffer( state ); 180 181 // Attempt to write the state to file: 182 err = writeFileSync( resolve( dir, flags.snapshot ), state ); 183 if ( err ) { 184 onError( err, code || 1 ); 185 } 186 } 187 188 /** 189 * Callback invoked upon encountering an error. 190 * 191 * @private 192 * @param {Error} error - error 193 * @param {integer} [code] - exit code 194 */ 195 function onError( error, code ) { 196 cli.error( error, code || 1 ); 197 } 198 } 199 200 main();