Skip to content

Instantly share code, notes, and snippets.

@Velrok
Created May 21, 2019 13:28
Show Gist options
  • Save Velrok/3ff1219f6ac485e9673b3adaf6d37183 to your computer and use it in GitHub Desktop.
Save Velrok/3ff1219f6ac485e9673b3adaf6d37183 to your computer and use it in GitHub Desktop.
A command line tool to infer a clojure spec from a line seq of data.
#!/usr/bin/env inlein
'{:dependencies [[org.clojure/clojure "1.10.0"]
[spec-provider "0.4.14"]
[org.clojure/tools.cli "0.4.2"]
[cheshire "5.8.1"]]}
(require '[cheshire.core :as json])
(require '[clojure.edn :as edn])
(require '[spec-provider.provider :as sp])
(require '[clojure.string :as string])
(require '[clojure.tools.cli :refer [parse-opts]])
(defn- println-err
[& args]
(binding [*out* *err*]
(apply println args)))
(def cli-options
;; An option with a required argument
[["-n" "--spec-root-key NS" "Spec root namespace."
:default :my-ns/data
:parse-fn #(-> % (string/replace #":" "") keyword)]
;; A boolean option defaulting to nil
["-h" "--help"]])
(defn- read-json
[s {:keys [verbose]
:or {verbose false}}]
(try
(json/parse-string s keyword)
(catch com.fasterxml.jackson.core.JsonParseException e
(when verbose
(println-err (format "ERROR: reading json [%S]"
(.getMessage e)
s)))
::error)))
(defn- read-edn
[s {:keys [verbose]
:or {verbose false}}]
(try
(edn/read-string s)
(catch java.lang.RuntimeException e
(when verbose
(println-err (format "ERROR: reading edn [%S] parsing: %s"
(.getMessage e)
s)))
::error)))
(defn- input-format
[s]
(cond
(not (= ::error (read-json s {}))) ::json
(not (= ::error (read-edn s {}))) ::edn
true ::error))
(let [opts (parse-opts *command-line-args* cli-options)]
(when-not (empty? (:errors opts))
(doseq [e (:errors opts)]
(println-err e))
(System/exit 23))
(when (get-in opts [:options :help])
(println-err (:summary opts))
(System/exit 0))
(let [input (line-seq (java.io.BufferedReader. *in*))
first-line (first input)
_ (println-err "Detecting input format from first line.")
in-fmt (input-format first-line)
_ (println-err (format "Parsing lines as %s." (name in-fmt)))
root-k (get-in opts [:options :spec-root-key])
dataset (map (fn [line]
(case in-fmt
::json (read-json line {:verbose true})
::edn (read-edn line {:verbose true})
::error (do
(println-err "Unknown format. Giving up!")
(System/exit 12))))
input)
_ (println-err (format "Inferring spec from a dataset of %d entries." (count dataset)))]
;(println-err (prn-str opts))
;(println-err (prn-str root-k))
;(println-err (prn-str dataset))
(sp/pprint-specs
(sp/infer-specs dataset root-k)
(symbol (namespace root-k))
's)
(println-err "Done!")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment