« get me outta code hell

cli args (bass boost ur music) - mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
path: root/general-util.js
diff options
context:
space:
mode:
authorFlorrie <towerofnix@gmail.com>2020-02-15 22:13:53 -0400
committerFlorrie <towerofnix@gmail.com>2020-02-15 22:15:15 -0400
commit48ed5168d477fe11fe4f21ae104e3750935b0943 (patch)
treec0d9260246933d180a2fa5405da8e66e9c484705 /general-util.js
parentfd09d0196f8db2102f9364a56f3075bf2cd93c88 (diff)
cli args (bass boost ur music)
$ mtui --player sox --player-options bass +25 \;
Diffstat (limited to 'general-util.js')
-rw-r--r--general-util.js118
1 files changed, 118 insertions, 0 deletions
diff --git a/general-util.js b/general-util.js
index 3aa4180..0a81cdc 100644
--- a/general-util.js
+++ b/general-util.js
@@ -192,3 +192,121 @@ module.exports.getTimeStrings = function({curHour, curMin, curSec, lenHour, lenM
 
   return module.exports.getTimeStringsFromSec(curSecTotal, lenSecTotal)
 }
+
+const parseOptions = async function(options, optionDescriptorMap) {
+  // This function is sorely lacking in comments, but the basic usage is
+  // as such:
+  //
+  // options is the array of options you want to process;
+  // optionDescriptorMap is a mapping of option names to objects that describe
+  // the expected value for their corresponding options.
+  // Returned is a mapping of any specified option names to their values, or
+  // a process.exit(1) and error message if there were any issues.
+  //
+  // Here are examples of optionDescriptorMap to cover all the things you can
+  // do with it:
+  //
+  // optionDescriptorMap: {
+  //   'telnet-server': {type: 'flag'},
+  //   't': {alias: 'telnet-server'}
+  // }
+  //
+  // options: ['t'] -> result: {'telnet-server': true}
+  //
+  // optionDescriptorMap: {
+  //   'directory': {
+  //     type: 'value',
+  //     validate(name) {
+  //       // const whitelistedDirectories = ['apple', 'banana']
+  //       if (whitelistedDirectories.includes(name)) {
+  //         return true
+  //       } else {
+  //         return 'a whitelisted directory'
+  //       }
+  //     }
+  //   },
+  //   'files': {type: 'series'}
+  // }
+  //
+  // ['--directory', 'apple'] -> {'directory': 'apple'}
+  // ['--directory', 'artichoke'] -> (error)
+  // ['--files', 'a', 'b', 'c', ';'] -> {'files': ['a', 'b', 'c']}
+  //
+  // TODO: Be able to validate the values in a series option.
+
+  const handleDashless = optionDescriptorMap[parseOptions.handleDashless]
+  const result = {}
+  for (let i = 0; i < options.length; i++) {
+    const option = options[i]
+    if (option.startsWith('--')) {
+      // --x can be a flag or expect a value or series of values
+      let name = option.slice(2).split('=')[0] // '--x'.split('=') = ['--x']
+      let descriptor = optionDescriptorMap[name]
+      if (!descriptor) {
+        console.error(`Unknown option name: ${name}`)
+        process.exit(1)
+      }
+      if (descriptor.alias) {
+        name = descriptor.alias
+        descriptor = optionDescriptorMap[name]
+      }
+      if (descriptor.type === 'flag') {
+        result[name] = true
+      } else if (descriptor.type === 'value') {
+        let value = option.slice(2).split('=')[1]
+        if (!value) {
+          value = options[++i]
+          if (!value || value.startsWith('-')) {
+            value = null
+          }
+        }
+        if (!value) {
+          console.error(`Expected a value for --${name}`)
+          process.exit(1)
+        }
+        result[name] = value
+      } else if (descriptor.type === 'series') {
+        if (!options.slice(i).includes(';')) {
+          console.error(`Expected a series of values concluding with ; (\\;) for --${name}`)
+          process.exit(1)
+        }
+        const endIndex = i + options.slice(i).indexOf(';')
+        result[name] = options.slice(i + 1, endIndex)
+        i = endIndex
+      }
+      if (descriptor.validate) {
+        const validation = await descriptor.validate(result[name])
+        if (validation !== true) {
+          console.error(`Expected ${validation} for --${name}`)
+          process.exit(1)
+        }
+      }
+    } else if (option.startsWith('-')) {
+      // mtui doesn't use any -x=y or -x y format optionuments
+      // -x will always just be a flag
+      let name = option.slice(1)
+      let descriptor = optionDescriptorMap[name]
+      if (!descriptor) {
+        console.error(`Unknown option name: ${name}`)
+        process.exit(1)
+      }
+      if (descriptor.alias) {
+        name = descriptor.alias
+        descriptor = optionDescriptorMap[name]
+      }
+      if (descriptor.type === 'flag') {
+        result[name] = true
+      } else {
+        console.error(`Use --${name} (value) to specify ${name}`)
+        process.exit(1)
+      }
+    } else if (handleDashless) {
+      handleDashless(option)
+    }
+  }
+  return result
+}
+
+parseOptions.handleDashless = Symbol()
+
+module.exports.parseOptions = parseOptions