babashka/cli

Auto-parse booleans (true / false), nil, and numbers?

borkdude opened this issue ยท 4 comments

It could be reasonable behaviour to auto-parse "false" as false and "nil" as nil. This would in some cases reduce the amount of :coerce config to zero.

Furthermore it could also be reasonable behavior to auto-parse 123 as a number.
Anything else is still parsed as a string.

We should be able to disable the auto-parsing.

{:no-auto-parse true}

and/or in the future:

{:no-auto-parse #{:number :number}}

Could there be a case where you want to have a new 1foo? This seems very unlikely. We could also revert to the string in case of edn/read-string failure.

Would you have want to have false, true? This seems unlikely and if so, you could coerce them to strings, if that is desired.

Here are some examples of conventions used in other libraries.

yogthos/config takes the following approach for coercing environment variables:

The values are parsed using the following strategy:

  1. [0-9]+ -> number
  2. ^(true|false)$ -> boolean
  3. \w+ -> string
  4. try parse as EDN, and return the original value as the default

tolitius/cprop does something similar for environment variables and system properties:

str->value will convert:
* numbers to longs
* alphanumeric values to strings
* true/false to boolean
* and will use Clojure reader for the rest
in case reader can't read OR it reads a symbol, the value will be returned as is (a string)

Thanks. I've looked into @yogthos's str->value function:

(defn str->value
  "ENV vars and system properties are strings. str->value will convert:
   the numbers to longs, the alphanumeric values to strings, and will use Clojure reader for the rest
   in case reader can't read OR it reads a symbol, the value will be returned as is (a string)"
  [v]
  (cond
    (re-matches #"[0-9]+" v) (parse-number v)
    (re-matches #"^(true|false)$" v) (Boolean/parseBoolean v)
    (re-matches #"\w+" v) v
    :else
    (try
      (let [parsed (edn/read-string v)]
        (if (symbol? parsed) v parsed))
      (catch Throwable _ v))))

I think this could just be simplified as: always a read as in, but when the result is a symbol, return the original string and in case of an error too?

Although:

user=> (conf/str->value "1M")
"1M"

I think in this case one would like to have 1M instead of "1M".

I've put in place some automatic coercions now. Documented here:

https://github.com/babashka/cli/blob/main/API.md#auto-coerce