Last active
June 1, 2023 12:58
-
-
Save 80sVectorz/606c4afd91e06d852393d4a66392646a to your computer and use it in GitHub Desktop.
Library script to support complex command line argument parsing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
-- Parse Goblin -- | |
VERSION 1.0 | |
Bitburner script: https://github.com/bitburner-official/bitburner-src https://store.steampowered.com/app/1812820/Bitburner/ | |
Library script to support complex command line argument parsing. | |
Features: | |
- full names with optional short forms: foo --bar / foo -b | |
- value argument: count --target 10 | |
- boolean flags: cool_script --help | |
- group arguments: modify_targets --add n00dles foodnstuff | |
- and required arguments. | |
Install with the following path: /lib/parse_goblin.js | |
Created by 80sVectorz: https://gist.github.com/80sVectorz/606c4afd91e06d852393d4a66392646a | |
*/ | |
export class Param{ | |
constructor(long,defaultValue,{short=null,isFlag=false,isGrouped=false,isRequired=false}={}) { | |
/* | |
long: The long for param name E.G "param_a" which would trigger with --param_a ... | |
defaultValue: The default value for this parameter | |
*short: The short form param name E.G 'a' which would trigger with -a ... | |
*isFlag: If the param is a flag that does not need a specified value E.G --help | |
This would return the flipped default value of the param if included: | |
foo --help | |
returns: {"help":true} | |
foo | |
returns: {"help":false} | |
*isGrouped: This allows the param to recieve a array of values E.G count2 --range 1 10 | |
returns: {"range":[1,10]} | |
*isRequired: If the param is required. An error message will show when it is missing | |
*/ | |
if(typeof long != "string"){ | |
throw Error("Argument long needs to be a string"); | |
} | |
if(typeof short != "string" && short!=null){ | |
throw Error("Argument short needs to be either a string or null"); | |
} | |
if(typeof isFlag!="boolean"){ | |
throw Error("Type of argument isFlag has to be a boolean"); | |
} | |
if(typeof isGrouped!="boolean"){ | |
throw Error("Type of argument isGrouped has to be a boolean"); | |
} | |
if(typeof isRequired!="boolean"){ | |
throw Error("Type of argument isRequired has to be a boolean"); | |
} | |
if(short.length!=1){ | |
throw Error("Argument short has to be a single character string"); | |
} | |
if(isFlag && isGrouped) { | |
throw Error("Param can not be a flag and group at the same time."); | |
} | |
if(isFlag && typeof defaultValue!="boolean") { | |
throw Error("Param has to be of type bool to use isFlag"); | |
} | |
if(!isGrouped && !["string","number","boolean"].includes(typeof defaultValue)){ | |
throw Error("Type of argument defaultValue has to be one of: string, number or boolean"); | |
} else if (isGrouped && !Array.isArray(defaultValue)) { | |
throw Error("Argument defaultValue has to be an array because to use isGrouped"); | |
} | |
if (isGrouped){ | |
let groupType = null; | |
for(var i=0;i<defaultValue.length;i++){ | |
if(groupType && typeof defaultValue[i] != groupType){ | |
throw Error("Types of grouped default value elements can not be mixed"); | |
} | |
if(!["string","number","boolean"].includes(typeof defaultValue[i])){ | |
throw Error("Type of argument grouped defaultValue element has to be one of: string, number or boolean"); | |
} else if (!groupType){ | |
groupType=typeof defaultValue[i]; | |
} | |
} | |
} | |
this.long = long; | |
this.short = short; | |
this.defaultValue = defaultValue; | |
this.isFlag = isFlag; | |
this.isGrouped = isGrouped; | |
this.isRequired = isRequired; | |
} | |
} | |
export class Schema{ | |
constructor(params,param_side){ | |
if(!Array.isArray(params)){ | |
throw Error("argument params has to be an array"); | |
} | |
for(var i=0;i<params.length;i++){ | |
if(!(params[i] instanceof Param)){ | |
throw Error("argument params can only contain param instances"); | |
} | |
} | |
this.params = params; | |
} | |
find(key){ | |
for(var i=0;i<this.params.length;i++){ | |
if(this.params[i].long == key || this.params[i].short == key){ | |
return i; | |
} | |
} | |
return -1; | |
} | |
longOf(key){ | |
return this.params[key].long; | |
} | |
shortOf(key){ | |
return this.params[key].short; | |
} | |
defaultValueOf(key){ | |
return this.params[key].defaultValue; | |
} | |
isFlagOf(key){ | |
return this.params[key].isFlag; | |
} | |
isGroupedOf(key){ | |
return this.params[key].isGrouped; | |
} | |
isRequiredOf(key){ | |
return this.params[key].isRequired; | |
} | |
} | |
/** @param {NS} ns */ | |
export function parse(ns,schema) { | |
let parsedParams = {}; | |
let missingParams = new Set(); | |
let passedArgs = ns.args; | |
for(var i = 0;i<schema.params.length;i++){ | |
parsedParams[schema.longOf(i)] = schema.defaultValueOf(i); | |
if (schema.isRequiredOf(i)){ | |
missingParams.add(schema.longOf(i)); | |
} | |
} | |
let inGroup = false; | |
let openKey = -1; | |
for(var i = 0;i<passedArgs.length;i++){ | |
var segment = passedArgs[i]; | |
segment = segment.toString(); | |
if (openKey!=-1 && inGroup && (segment.startsWith("--") || (segment.startsWith("-") && segment.length==2))){ | |
inGroup=false; | |
missingParams.delete(schema.longOf(openKey)); | |
openKey=-1; | |
segment = passedArgs[i]; | |
} | |
segment = passedArgs[i]; | |
if(openKey==-1){ | |
switch(typeof segment){ | |
case "string" : | |
if (segment.startsWith("--")){ | |
var key = schema.find(segment.slice(2)); | |
if(key!=-1){ | |
if (schema.isFlagOf(key)){ | |
parsedParams[schema.longOf(key)] = !schema.defaultValueOf(key); | |
break; | |
} | |
if (schema.isGroupedOf(key)){ | |
inGroup=true; | |
parsedParams[schema.longOf(key)] = []; | |
} | |
openKey = key; | |
break; | |
} else { | |
ns.tprint(`Found invalid argument: ${segment}`); | |
ns.exit(); | |
} | |
} else if (segment.startsWith("-")){ | |
if(segment.length == 2){ | |
var key = schema.find(segment.slice(1)); | |
if(key!=-1){ | |
if (schema.isFlagOf(key)){ | |
parsedParams[schema.longOf(key)] = !schema.defaultValueOf(key); | |
break; | |
} | |
if (schema.isGroupedOf(key)){ | |
inGroup=true; | |
parsedParams[schema.longOf(key)] = []; | |
} | |
openKey = key; | |
break; | |
} | |
} | |
ns.tprint(`Found invalid argument: ${segment}`); | |
ns.exit(); | |
} | |
default: | |
if(inGroup){ | |
break; | |
} | |
ns.tprint(`Recieved unexpected ${typeof segment}: ${segment}`); | |
ns.exit(); | |
} | |
} | |
else { | |
segment = passedArgs[i]; | |
if (typeof schema.defaultValueOf(openKey) == typeof segment || inGroup){ | |
if(inGroup){ | |
if (typeof segment == typeof schema.defaultValueOf(openKey)[0]){ | |
parsedParams[schema.longOf(openKey)].push(segment); | |
} else { | |
ns.tprint(`Recieved incorrect value type for element of group ${schema.longOf(openKey)}: got ${typeof segment} expected ${typeof schema.defaultValueOf(openKey)[0]}`); | |
ns.exit(); | |
} | |
} else { | |
parsedParams[schema.longOf(openKey)] = segment; | |
} | |
if(!inGroup){ | |
openKey = -1; | |
} | |
} else { | |
ns.tprint(`Recieved incorrect value type for ${schema.longOf(openKey)}: got ${typeof segment} expected ${typeof schema.defaultValueOf(openKey)}`); | |
ns.exit(); | |
} | |
} | |
} | |
if(missingParams.length > 0){ | |
if(missingParams.length == 1){ | |
ns.tprint(`Missing the following required argument: ${missingParams[0]}`); | |
} else { | |
ns.tprint(`Missing the following required arguments: ${missingParams.slice(missingParams.length-1).join(", ")} & ${missingParams[missingParams.length-1]}.`); | |
} | |
ns.exit(); | |
} | |
return parsedParams; | |
} | |
export function check(ns){ | |
ns.print("I'm the parse goblin Nyehehhehe!"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment