It na߳ively coerces types from a str. Meant to be used with walk to, e.g., coerce a dirty CSV/JSON to a typed structure.

(ns confundus.type-guesser
  "A bunch of utilities to try and parse strings
  into more meaningful data types."
  (:require [clojure.string :as str]))

(defn parse-boolean
  [s]
  (let [bool-s (some-> s
                  str/lower-case
                  #{"true" "false"})]
    (if-not bool-s
      (throw (IllegalArgumentException.
              (str "not a boolean:" s)))
      (Boolean/parseBoolean bool-s))))

(def ^:doc "default parser stack"
  parsers
  [#(java.time.LocalDate/parse %)
   #(java.time.LocalTime/parse %)
   #(java.time.LocalDateTime/parse %)
   #(java.time.Instant/parse %)
   parse-boolean
   #(Integer/parseInt %)
   #(Long/parseLong %)
   #(java.math.BigInteger. %)
   #(java.math.BigDecimal. %)
   str])

(defn parse
  ;; TODO: leave non-string values alone
  ([s]
   (parse parsers (str/trim s)))
  ([ps s]
   (let [[p & ps] ps]
     (try (p s)
          (catch Exception _
            (parse ps s))))))

;;;
(require 'clojure.walk)

(defn coerce [ps x]
  (walk/postwalk (fn [x] (if (string? x) (parse x) x)) x))

Once you have run something like coerce over all your data (e.g. over a Kafka topic with onion), you could have a Trifacta-like UI, but for nested data instead.