Skip to content

Instantly share code, notes, and snippets.

@lotuc
Last active May 25, 2023 04:34
Show Gist options
  • Save lotuc/fba421f74ea8610494118bc899f9f1b5 to your computer and use it in GitHub Desktop.
Save lotuc/fba421f74ea8610494118bc899f9f1b5 to your computer and use it in GitHub Desktop.
Clojure Utilities
(ns camel-snake-kebab-rexport
(:require
[camel-snake-kebab.core :as csk]
[camel-snake-kebab.internals.string-separator
:refer [split classify-char StringSeparator]]))
;;; kebab patch
;;; https://gist.github.com/lotuc/fba421f74ea8610494118bc899f9f1b5#file-camel_snake_kebab_utils-clj
(defn generic-split [ss]
(let [cs (mapv classify-char ss)
ss-length (.length ^String ss)]
(loop [result (transient []), start 0, current 0]
(let [next (inc current)
result+new (fn [end]
(if (> end start)
(conj! result (.substring ^String ss start end))
result))]
(cond (>= current ss-length)
(or (seq (persistent! (result+new current)))
;; Return this instead of an empty seq:
[""])
(= (nth cs current) :whitespace)
(recur (result+new current) next next)
(let [[a b c] (subvec cs current)]
;; This expression is not pretty,
;; but it compiles down to sane JavaScript.
(or (and (not= a :upper) (= b :upper))
(and (= a :number) (not= b :number)) ; changed here.
(and (= a :upper) (= b :upper) (= c :lower))))
(recur (result+new next) next next)
:else
(recur result start next))))))
(def separator (reify StringSeparator (split [_ s] (generic-split s))))
(comment
(split separator "md5")
(csk/->kebab-case-keyword "md5" {:separator separator})
(csk/->kebab-case-keyword "md5"))
(defmacro ^:private reexport [& names]
`(do
~@(for [n names]
`(defn ~n [s# & rest#]
(~(symbol "csk" (name n))
s# (merge {:separator separator} rest#))))))
(reexport
->PascalCase
->Camel_Snake_Case
->camelCase
->SCREAMING_SNAKE_CASE
->snake_case
->kebab-case
->HTTP-Header-Case
->PascalCaseKeyword
->camelCaseKeyword
->SCREAMING_SNAKE_CASE_KEYWORD
->snake_case_keyword
->kebab-case-keyword
->Camel_Snake_Case_Keyword
->HTTP-Header-Case-Keyword
->PascalCaseString
->camelCaseString
->SCREAMING_SNAKE_CASE_STRING
->snake_case_string
->kebab-case-string
->Camel_Snake_Case_String
->HTTP-Header-Case-String
->PascalCaseSymbol
->camelCaseSymbol
->SCREAMING_SNAKE_CASE_SYMBOL
->snake_case_symbol
->kebab-case-symbol
->Camel_Snake_Case_Symbol
->HTTP-Header-Case-Symbol)
;; Problem
;; md5 becomes md-5, which is not intended.
;; **Do such a global patch maybe problematic**
;; maybe just wrap your own converter
;; Some thirdparty libraries depends on the original behavior.
;; A dirty patch which changes the default separator.
;; https://github.com/clj-commons/camel-snake-kebab/blob/ac08444c94aca4cba25d86f3b3faf36596809380/src/camel_snake_kebab/internals/misc.cljc#LL6C53-L6C53
;; https://github.com/clj-commons/camel-snake-kebab/blob/ac08444c94aca4cba25d86f3b3faf36596809380/src/camel_snake_kebab/internals/string_separator.cljc#L64
;; https://github.com/clj-commons/camel-snake-kebab/issues/42#issuecomment-352219297
#_{:clj-kondo/ignore [:unused-namespace]}
(require '[camel-snake-kebab.core :as csk])
(require '[camel-snake-kebab.internals.string-separator
:refer [classify-char StringSeparator]])
(defn generic-split [ss]
(let [cs (mapv classify-char ss)
ss-length #?(:clj (.length ^String ss)
:cljs (.-length ss))]
(loop [result (transient []), start 0, current 0]
(let [next (inc current)
result+new (fn [end]
(if (> end start)
(conj! result (.substring ^String ss start end))
result))]
(cond (>= current ss-length)
(or (seq (persistent! (result+new current)))
;; Return this instead of an empty seq:
[""])
(= (nth cs current) :whitespace)
(recur (result+new current) next next)
(let [[a b c] (subvec cs current)]
;; This expression is not pretty,
;; but it compiles down to sane JavaScript.
(or (and (not= a :upper) (= b :upper))
(and (= a :number) (not= b :number)) ;; Note this is what we changed.
(and (= a :upper) (= b :upper) (= c :lower))))
(recur (result+new next) next next)
:else
(recur result start next))))))
#_{:clj-kondo/ignore [:unresolved-namespace]}
(alter-var-root
#'camel-snake-kebab.internals.string-separator/generic-separator
(fn [_] (reify StringSeparator
(split [_ s] (generic-split s)))))
;; References
;; - https://github.com/Olical/clojure-giants-shoulders
{:paths ["src"]
:deps
{;; re-export vars & more
potemkin/potemkin {:mvn/version "0.4.6"}}
:aliases
{:dev
{:extra-paths ["envs/dev"]
:extra-deps
{com.lambdaisland/classpath {:mvn/version "0.4.44"} ;; watch deps.edn & reload dependencies
flames/flames {:mvn/version "0.5.0"}}}
;; list outdated dependencies: clojure -M:outdated
:outdated
{:deps {com.github.liquidz/antq {:mvn/version "2.4.1070"}}
:main-opts ["-m" "antq.core"]}
}}
;; https://github.com/clj-commons/potemkin/issues/31
#?(:clj (require '[potemkin :refer [import-vars]])
:cljs
(do
(defmacro import-def
"import a single fn or var
(import-def a b) => (def b a/b)
"
[from-ns def-name]
(let [from-sym# (symbol (str from-ns) (str def-name))]
`(def ~def-name ~from-sym#)))
(defmacro import-vars
"import multiple defs from multiple namespaces
works for vars and fns. not macros.
(same syntax as potemkin.namespaces/import-vars)
(import-vars
[m.n.ns1 a b]
[x.y.ns2 d e f]) =>
(def a m.n.ns1/a)
(def b m.n.ns1/b)
...
(def d m.n.ns2/d)
... etc
"
[& imports]
(let [expanded-imports
(for [[from-ns & defs] imports d defs]
`(import-def ~from-ns ~d))]
`(do ~@expanded-imports)))))
(require '[malli.core :as m])
(require '[malli.transform :as mt])
(require '[malli.util :as mu])
(defn transform-keys
[schema t]
(let [t' (partial map (fn [[k p s]] [(t k) p s]))
w #(cond-> %
(m/entries %)
(mu/transform-entries t'))]
(m/walk schema (m/schema-walker w))))
(comment
(transform-keys [:map [:a :string]] name) ; => [:map ["a" :string]]
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment