std.string

case, coercion, path, plural, prose, and wrapping helpers

std.string is a higher-level standard library family in foundation-base. This page explains when to use it, how it fits internally, and where to find the API surface.

1    Motivation

Use this layer when application or tooling code needs the behavior described by the page title without reaching directly into implementation namespaces. The top-level namespace is the starting point; subnamespaces expose more focused building blocks.

2    How to use it

Require the top-level namespace for common workflows, then move to subnamespaces when you need a lower-level primitive. Existing tests under test/std/string and test/std/string_test.clj are the best executable examples for edge cases.

case conversions

(camel-case "hello-world")
=> "helloWorld"

(snake-case "hello-world")
=> "hello_world"

(spear-case "hello_world")
=> "hello-world"

wrapped operations preserve input type

((wrap split) :a-b #"-")
=> [:a :b]

((wrap subs) :hello-world 3 8)
=> :lo-wo

prose helpers

(prose/| "abc" "def")
=> "abc\ndef"

(prose/single-line "a\nb")
=> "a b"

3    Internal usage

This library family is used across source, tests, generated examples, and docs tooling. During detailed documentation passes, collect concrete usage with code.manage/find-usages and code.manage/locate-code, then keep only high-signal examples in the page narrative.

4    API



+hump-pattern+ ^

NONE
(def ^:private +hump-pattern+ #"[a-z0-9][A-Z]")
link

+non-camel-pattern+ ^

NONE
(def ^:private +non-camel-pattern+ #"[_| |-][A-Za-z]")
link

+non-dot-pattern+ ^

NONE
(def ^:private +non-dot-pattern+ #"[ |-_]")
link

+non-snake-pattern+ ^

NONE
(def ^:private +non-snake-pattern+ #"[ |-]")
link

+non-spear-pattern+ ^

NONE
(def ^:private +non-spear-pattern+ #"[ |_]")
link

blank? ^

[s]
Added 3.0

checks if string is empty or nil

v 3.0
(defn blank?
  ([^CharSequence s]
   (if s
     (loop [index (int 0)]
       (if (= (.length s) index)
         true
         (if (Character/isWhitespace (.charAt s index))
           (recur (inc index))
           false)))
     true)))
link
(blank? nil) => true (blank? "") => true

camel-case ^

[value]
Added 3.0

converts a string-like object to camel case representation

v 3.0
(defn ^String camel-case
  ([^String value]
   (let [s (re-sub value
                   +non-camel-pattern+
                   (fn [s] (common/upper-case (apply str (rest s)))))]
     (str (.toLowerCase (subs s 0 1))
          (subs s 1)))))
link
(camel-case "hello-world") => "helloWorld" ((wrap camel-case) 'hello_world) => 'helloWorld

capital-case ^

[s]
Added 3.0

converts a string object to capital case

v 3.0
(defn ^String capital-case
  ([^CharSequence s]
   (let [s (.toString s)]
     (if (< (count s) 2)
       (.toUpperCase s)
       (str (.toUpperCase (subs s 0 1))
            (.toLowerCase (subs s 1)))))))
link
(capital-case "hello.World") => "Hello.world" ((wrap capital-case) 'hello.World) => 'Hello.world

capital-sep-case ^

[value]
Added 3.0

converts a string-like object to captital case representation

v 3.0
(defn ^String capital-sep-case
  ([^String value]
   (-> (separate-humps value)
       (common/split #"[ |-|_]")
       (->> (map common/capital-case))
       (common/joinl " "))))
link
(capital-sep-case "hello world") => "Hello World" (str ((wrap capital-sep-case) :hello-world)) => ":Hello World"

capitalize ^

[s]
Added 4.0

capitalize the first letter

v 4.0
(defn ^String capitalize
  [^String s]
  (str (.toUpperCase (subs s 0 1))
       (subs s 1)))
link
(capitalize "hello") => "Hello"

caseless= ^

[x y]
Added 3.0

compares two values ignoring case

v 3.0
(defn caseless=
  ([x y]
   (= (lower-case x)
      (lower-case y))))
link
(caseless= "heLLo" "HellO") => true ((wrap caseless=) 'heLLo :HellO) => true

decapitalize ^

[s]
Added 4.0

lowercase the first letter

v 4.0
(defn ^String decapitalize
  [^String s]
  (str (.toLowerCase (subs s 0 1))
       (subs s 1)))
link
(decapitalize "HELLO") => "hELLO"

dot-case ^

[value]
Added 3.0

creates a dot-case representation

v 3.0
(defn ^String dot-case
  ([value]
   (-> (separate-humps value)
       (common/lower-case)
       (common/replace +non-dot-pattern+ "."))))
link
(dot-case "hello-world") => "hello.world" ((wrap dot-case) 'helloWorld) => 'hello.world

ends-with? ^

[s substr]
Added 3.0

checks if string ends with another

v 3.0
(defn ends-with?
  ([^CharSequence s ^String substr]
   (.endsWith (.toString s) substr)))
link
(ends-with? "hello" "lo") => true ((wrap ends-with?) 'hello 'lo) => true

escape ^

[s cmap]
Added 3.0

uses a map to replace chars

v 3.0
(defn ^String escape
  ([^CharSequence s cmap]
   (loop [index (int 0)
          buffer (StringBuilder. (.length s))]
     (if (= (.length s) index)
       (.toString buffer)
       (let [ch (.charAt s index)]
         (if-let [replacement (cmap ch)]
           (.append buffer replacement)
           (.append buffer ch))
         (recur (inc index) buffer))))))
link
(escape "1 < 2 as HTML, & s." {< "<", > ">", & "&"}) => "1 < 2 as HTML, & s."

escape-dollars ^

[s]
Added 3.0

for regex purposes, escape dollar signs in strings

v 3.0
(defn escape-dollars
  ([^String s]
   (.replaceAll s "\$" "\\\$")))
link
(escape-dollars "$") => string?

escape-escapes ^

[s]
Added 3.0

makes sure that newlines are printable

v 3.0
(defn escape-escapes
  ([^String s]
   (.replaceAll s "(\\)([A-Za-z])" "$1$1$2")))
link
(escape-escapes "\n") => "\\n"

escape-newlines ^

[s]
Added 3.0

makes sure that newlines are printable

v 3.0
(defn escape-newlines
  ([^String s]
   (.replaceAll s "\n" "\\n")))
link
(escape-newlines "\n") => "\n"

escape-quotes ^

[s]
Added 3.0

makes sure that quotes are printable in string form

v 3.0
(defn escape-quotes
  ([^String s]
   (.replaceAll s "(\\)?"" "$1$1\\\"")))
link
(escape-quotes ""hello"") => "\"hello\""

filter-empty-lines ^

[s]
Added 3.0

filter empty line

v 3.0
(defn filter-empty-lines
  [s]
  (->> (common/split-lines s)
       (remove (fn [line] (re-find #"^s*$" line)))
       (common/join "n")))
link
(filter-empty-lines (common/join "n" ["a" " " " " "b"])) => "anb"

from-string ^

[string type] [string type opts]
Added 3.0

converts a string to an object

v 3.0
(defn from-string
  ([string type]
   (from-string string type nil))
  ([string type opts]
   (protocol.string/-from-string string type opts)))
link
(from-string "a" clojure.lang.Symbol) => 'a (from-string "std.string" clojure.lang.Namespace) => (find-ns 'std.string)

has-quotes? ^

[s]
Added 3.0

checks if a string has quotes

v 3.0
(defn has-quotes?
  ([^String s]
   (and (.startsWith s """)
        (.endsWith s """))))
link
(has-quotes? ""hello"") => true

includes? ^

[s substr]
Added 3.0

checks if first string contains the second

v 3.0
(defn includes?
  ([^CharSequence s ^CharSequence substr]
   (.contains (.toString s) substr)))
link
(includes? "hello" "ell") => true ((wrap includes?) 'hello 'ell) => true

indent ^

[block n & [{:keys [custom], :or {custom ""}}]]
Added 4.0

indents a block of string

v 4.0
(defn indent
  ([block n & [{:keys [custom] :or
                {custom ""}}]]
   (if (and (not (pos? n))
            (empty? custom))
     block
     (->> (common/split-lines block)
          (map (fn [line] (str (spaces n) custom line)))
          (common/join "n")))))
link
(indent (write-lines ["a" "b" "c"]) 2) => "an bn c"

indent-rest ^

[block n & [{:keys [custom], :or {custom ""}}]]
Added 4.0

indents the rest of the boiy

v 4.0
(defn indent-rest
  ([block n & [{:keys [custom] :or
                {custom ""}}] ]
   (if (and (not (pos? n))
            (empty? custom))
     block
     (let [[head & rest] (common/split-lines block)]
       (cond (empty? rest)
             head

             :else
             (->> rest
                  (map (fn [line] (str (spaces n) custom line)))
                  (common/join "n")
                  (str head "n")))))))
link
(indent-rest (write-lines ["a" "b" "c"]) 2) => "an bn c"

insert-at ^

[s i new]
Added 4.0

inserts a string at the given index

v 4.0
(defn ^String insert-at
  [^String s i new]
  (str (subs s 0 i)
       new
       (subs s i)))
link
(insert-at "hello" 2 "XX") => "heXXllo"

join ^

[coll] [seperator coll]
Added 3.0

like `join` but used with `->>` opts

v 3.0
(defn ^String join
  ([coll]
   (apply str coll))
  ([seperator coll]
   (joinl coll seperator)))
link
(join "." ["a" "b" "c"]) => "a.b.c" ;;(join "." '[a b c]) ;;=> 'a.b.c

join-lines ^

[arr]
Added 4.0

join non empty elements in array

v 4.0
(defn join-lines
  [arr]
  (common/join "n" arr))
link
(join-lines ["hello" "world"]) => "hellonworld"

joinl ^

[coll] [coll separator]
Added 3.0

joins an array using a separator

v 3.0
(defn ^String joinl
  ([coll]
   (apply str coll))
  ([coll separator]
   (loop [sb (StringBuilder. (str (first coll)))
          more (next coll)
          sep (str separator)]
     (if more
       (recur (-> sb (.append sep) (.append (str (first more))))
              (next more)
              sep)
       (str sb)))))
link
(joinl ["a" "b" "c"] ".") => "a.b.c" (joinl ["a" "b" "c"]) => "abc" ((wrap joinl) [:a :b :c] "-") => :a-b-c

layout-lines ^

[tokens] [tokens max]
Added 4.0

layout tokens in lines depending on max length

v 4.0
(defn layout-lines
  ([tokens]
   (layout-lines tokens 80))
  ([tokens max]
   (->> (reduce (fn [[lines curr] tok]
                  (let [stok (str tok)]
                    (if (< (inc (+ (count curr)
                                   (count stok)))
                           max)
                      [lines (str curr " " stok)]
                      [(conj lines curr) stok])))
                [[] (str (first tokens))]
                (rest tokens))
        (apply conj)
        (common/join "n"))))
link
(layout-lines ["hello" "world" "again" "a" "b"] 8) => "hellonworldnagain anb"

lines ^

[s]
Added 3.0

transforms a string to newline separated forms

v 3.0
(defn lines
  [s]
  (cons `| (common/split-lines s)))
link
(lines "abcndef") => '(std.string.prose/| "abc" "def")

lower-case ^

[s]
Added 3.0

converts a string object to lower case

v 3.0
(defn ^String lower-case
  ([^CharSequence s]
   (.. s toString toLowerCase)))
link
(lower-case "Hello.World") => "hello.world" ((wrap lower-case) 'Hello.World) => 'hello.world

lower-sep-case ^

[value]
Added 3.0

converts a string-like object to a lower case representation

v 3.0
(defn ^String lower-sep-case
  ([^String value]
   (-> (separate-humps value)
       (common/split #"[ |-|_]")
       (->> (map common/lower-case))
       (common/joinl " "))))
link
(lower-sep-case "helloWorld") => "hello world" ((wrap lower-sep-case) 'hello-world) => (symbol "hello world")

multi-line? ^

[s]
Added 4.0

check that a string has newlines

v 4.0
(defn multi-line?
  ([s]
   (and s (boolean (re-find #"n" s)))))
link
(multi-line? "anb") => true (multi-line? "a") => false

pascal-case ^

[value]
Added 3.0

converts a string-like object to a pascal case representation

v 3.0
(defn ^String pascal-case
  ([^String value]
   (let [s (re-sub value +non-camel-pattern+
                   (fn [s] (common/upper-case (apply str (rest s)))))]
     (str (.toUpperCase (subs s 0 1))
          (subs s 1)))))
link
(pascal-case "helloWorld") => "HelloWorld" ((wrap pascal-case) :hello-world) => :HelloWorld

path-count ^

[s] [s sep]
Added 3.0

counts the number of elements in a given path

v 3.0
(defn path-count
  ([s]
   (path-count s h/*sep*))
  ([s sep]
   (count (path-split s sep))))
link
(path/path-count "a/b/c") => 3 ((wrap path/path-count) *ns*) => 3

path-join ^

[arr] [arr sep]
Added 3.0

joins a sequence of elements into a path separated value

v 3.0
(defn path-join
  ([arr] (path-join arr h/*sep*))
  ([arr sep]
   (if (seq arr)
     (-> (filter identity arr)
         (common/joinl sep)))))
link
(path/path-join ["a" "b" "c"]) => "a/b/c" ((wrap path/path-join) '[:a :b :c] "-") => :a-b-c ((wrap path/path-join) '[a b c] '-) => 'a-b-c

path-ns ^

[s] [s sep]
Added 3.0

returns the path namespace of the string

v 3.0
(defn path-ns
  ([s]
   (path-ns s h/*sep*))
  ([s sep]
   (-> s
       (path-ns-array sep)
       (path-join sep))))
link
(path/path-ns "a/b/c/d") => "a/b/c" ((wrap path/path-ns) :a.b.c ".") => :a.b

path-ns-array ^

[s] [s sep]
Added 3.0

returns the path vector of the string

v 3.0
(defn path-ns-array
  ([s]
   (path-ns-array s h/*sep*))
  ([s sep]
   (or (butlast (path-split s sep)) [])))
link
(path/path-ns-array "a/b/c/d") => ["a" "b" "c"] ((wrap path/path-ns-array) (keyword "a/b/c/d")) => [:a :b :c]

path-nth ^

[s n] [s n sep]
Added 3.0

check for the val of the string

v 3.0
(defn path-nth
  ([s n]
   (path-nth s n h/*sep*))
  ([s n sep]
   (nth (path-split s sep) n)))
link
(path/path-nth "a/b/c/d" 2) => "c"

path-root ^

[s] [s sep]
Added 3.0

returns the path root of the string

v 3.0
(defn path-root
  ([s]
   (path-root s h/*sep*))
  ([s sep]
   (first (path-ns-array s sep))))
link
(path/path-root "a/b/c/d") => "a" ((wrap path/path-root) 'a.b.c ".") => 'a

path-separator ^

[type]
Added 3.0

returns the default path separator for an object

v 3.0
(definvoke path-separator
  [:memoize {:arglists '([type])
             :function protocol.string/-path-separator}])
link
(path-separator clojure.lang.Namespace) => "." (path-separator clojure.lang.Keyword) => "/"

path-split ^

[s] [s sep]
Added 3.0

splits a sequence of elements into a path separated value

v 3.0
(defn path-split
  ([s] (path-split s h/*sep*))
  ([s sep]
   (common/split s (h/re-create sep))))
link
(path/path-split "a/b/c/d") => '["a" "b" "c" "d"] (path/path-split "a.b.c.d" ".") => ["a" "b" "c" "d"] ((wrap path/path-split) :hello/world) => [:hello :world] ((wrap path/path-split) :hello.world ".") => [:hello :world]

path-stem ^

[s] [s sep]
Added 3.0

returns the path stem of the string

v 3.0
(defn path-stem
  ([s]
   (path-stem s h/*sep*))
  ([s sep]
   (-> s
       (path-stem-array sep)
       (path-join sep))))
link
(path/path-stem "a/b/c/d") => "b/c/d" ((wrap path/path-stem) 'a.b.c.d ".") => 'b.c.d

path-stem-array ^

[s] [s sep]
Added 3.0

returns the path stem vector of the string

v 3.0
(defn path-stem-array
  ([s]
   (path-stem-array s h/*sep*))
  ([s sep]
   (rest (path-split s sep))))
link
(path/path-stem-array "a/b/c/d") => ["b" "c" "d"] ((wrap path/path-stem-array) 'a.b.c.d ".") => '[b c d]

path-sub ^

[s start num] [s start num sep]
Added 3.0

returns a subsection of the path within the string

v 3.0
(defn path-sub
  ([s start num]
   (path-sub s start num h/*sep*))
  ([s start num sep]
   (-> (path-sub-array s start num sep)
       (path-join sep))))
link
(path/path-sub "a/b/c/d" 1 2) => "b/c" ((wrap path/path-sub) (symbol "a/b/c/d") 1 2) => 'b/c

path-sub-array ^

[s start num] [s start num sep]
Added 3.0

returns a sub array of the path within the string

v 3.0
(defn path-sub-array
  ([s start num]
   (path-sub-array s start num h/*sep*))
  ([s start num sep]
   (->> (path-split s sep)
        (drop start)
        (take num))))
link
(path/path-sub-array "a/b/c/d" 1 2) => ["b" "c"] ((wrap path/path-sub-array) (symbol "a/b/c/d") 1 2) => '[b c]

path-val ^

[s] [s sep]
Added 3.0

returns the val of the string

v 3.0
(defn path-val
  ([s]
   (path-val s h/*sep*))
  ([s sep]
   (last (path-split s sep))))
link
(path/path-val "a/b/c/d") => "d" ((wrap path/path-val) 'a.b.c.d ".") => 'd

phrase-case ^

[value]
Added 3.0

converts a string-like object to snake case representation

v 3.0
(defn ^String phrase-case
  ([^String value]
   (let [s (lower-sep-case value)]
     (str (.toUpperCase (subs s 0 1))
          (subs s 1)))))
link
(phrase-case "hello-world") => "Hello world"

re-sub ^

Added 3.0

substitute a pattern by applying a function

v 3.0
(defn- ^String re-sub
  ([^String value pattern sub-func]
   (loop [matcher (re-matcher pattern value)
          result []
          last-end 0]
     (if (.find matcher)
       (recur matcher
              (conj result
                    (.substring value last-end (.start matcher))
                    (sub-func (re-groups matcher)))
              (.end matcher))
       (apply str (conj result (.substring value last-end)))))))
link
(#'case/re-sub "aTa" @#'case/+hump-pattern+ (fn [_] "$")) => "$a"

replace ^

[s match replacement]
Added 3.0

replace value in string with another

v 3.0
(defn ^String replace
  ([^CharSequence s match replacement]
   (let [s (.toString s)]
     (cond
       (instance? Character match) (.replace s ^Character match ^Character replacement)
       (instance? CharSequence match) (.replace s ^CharSequence match ^CharSequence replacement)
       (instance? Pattern match) (if (instance? CharSequence replacement)
                                   (.replaceAll (re-matcher ^Pattern match s)
                                                (.toString ^CharSequence replacement))
                                   (replace-by s match replacement))
       :else (throw (IllegalArgumentException. (str "Invalid match arg: " match)))))))
link
(replace "hello" "el" "AL") => "hALlo" ((wrap replace) :hello "el" "AL") => :hALlo

replace-all ^

[s match re]
Added 4.0

shortcut for ``.replaceAll``

v 4.0
(defn ^String replace-all
  [^String s match re]
  (.replaceAll s match re))
link
(replace-all "hello" "l" "o") => "heooo"

replace-at ^

[s i new]
Added 4.0

replaces the character at the index with a new string

v 4.0
(defn ^String replace-at
  [^String s i new]
  (str (subs s 0 i)
       new
       (subs s (+ i 1))))
link
(replace-at "hello" 4 "a") => "hella" (replace-at "hello" 1 "a") => "hallo"

replace-by ^

NONE
(defn- replace-by
  [^CharSequence s re f]
  (let [m (re-matcher re s)]
    (if (.find m)
      (let [buffer (StringBuffer. (.length s))]
        (loop [found true]
          (if found
            (do (.appendReplacement m buffer (Matcher/quoteReplacement (f (re-groups m))))
                (recur (.find m)))
            (do (.appendTail m buffer)
                (.toString buffer)))))
      s)))
link

reverse ^

[s]
Added 3.0

reverses the string

v 3.0
(defn ^String reverse
  ([^CharSequence s]
   (.toString (.reverse (StringBuilder. s)))))
link
(reverse "hello") => "olleh" ((wrap reverse) :hello) => :olleh

separate-humps ^

Added 3.0

separate words that are camel cased

v 3.0
(defn- ^String separate-humps
  ([^String value]
   (re-sub value +hump-pattern+ #(common/joinl (seq %) " "))))
link
(#'case/separate-humps "aTaTa") => "a Ta Ta"

single-line ^

[s]
Added 3.0

replace newlines with spaces

v 3.0
(defn single-line
  [s]
  (->> (common/split-lines s)
       (common/join " ")))
link
(single-line "anbnc") => "a b c"

single-line? ^

[s]
Added 4.0

check that a string does not have newlines

v 4.0
(defn single-line?
  ([s]
   (not (multi-line? s))))
link
(single-line? "anb") => false (single-line? "a") => true

snake-case ^

[value]
Added 3.0

converts a string-like object to snake case representation

v 3.0
(defn ^String snake-case
  ([value]
   (-> (separate-humps value)
       (common/lower-case)
       (common/replace +non-snake-pattern+ "_"))))
link
(snake-case "hello-world") => "hello_world" ((wrap snake-case) 'helloWorld) => 'hello_world

spaces ^

[n]
Added 4.0

create `n` spaces

v 4.0
(defn spaces
  ([n]
   (apply str (repeat n " "))))
link
(spaces 4) => ""

spear-case ^

[value]
Added 3.0

converts a string-like object to spear case representation

v 3.0
(defn ^String spear-case
  ([value]
   (-> (separate-humps value)
       (common/lower-case)
       (common/replace +non-spear-pattern+ "-"))))
link
(spear-case "hello_world") => "hello-world" ((wrap spear-case) 'helloWorld) => 'hello-world

split ^

[s re]
Added 3.0

splits the string into tokens

v 3.0
(defn ^String split
  ([^CharSequence s ^Pattern re]
   (vec (.split re s -1))))
link
(split "a b c" #" ") => ["a" "b" "c"] ((wrap split) :a.b.c (re-pattern "\.")) => [:a :b :c]

split-lines ^

[s]
Added 3.0

splits the string into separate lines

v 3.0
(defn split-lines
  ([^String s]
   (if (not-empty s)
     (vec (.split s "[\r\n]", -1))
     [])))
link
(split-lines "anbnc") => ["a" "b" "c"]

starts-with? ^

[s substr]
Added 3.0

checks if string starts with another

v 3.0
(defn starts-with?
  ([^CharSequence s ^String substr]
   (.startsWith (.toString s) substr)))
link
(starts-with? "hello" "hel") => true ((wrap starts-with?) 'hello 'hel) => true

str:compare ^

[f]
Added 3.0

wraps a function so that it can compare any two string-like inputs

v 3.0
(defn str:compare
  ([f]
   (fn [x y & args]
     (binding [h/*sep* (path-separator (type x))]
       (apply f (to-string x) (to-string y) args)))))
link
((str:compare =) :hello 'hello) => true

str:op ^

[f] [f return]
Added 3.0

wraps a string function such that it can take any string-like input

v 3.0
(defn str:op
  ([f]
   (str:op f false))
  ([f return]
   (fn [input & args]
     (let [[isample iarr?] (if (sequential? input)
                             [(first input) true]
                             [input false])
           class   (type isample)]
       (binding [h/*sep* (path-separator class)]
         (let [interim (cond (= class String)
                             (apply f input args)

                             iarr?
                             (apply f (map to-string input) args)

                             :else
                             (apply f (to-string input) args))
               oarr?   (sequential? interim)
               output  (cond return
                             interim

                             (= class String)
                             interim

                             oarr?
                             (mapv #(from-string % class) interim)

                             :else
                             (from-string interim class))]
           output))))))
link
((str:op str false) :hello 'hello) => :hellohello ((str:op str true) :hello 'hello) => "hellohello"

strip-quotes ^

[s]
Added 3.0

gets rid of quotes in a string

v 3.0
(defn strip-quotes
  ([s]
   (if (has-quotes? s)
     (subs s 1 (dec (count s)))
     s)))
link
(strip-quotes ""hello"") => "hello"

to-string ^

[string]
Added 3.0

converts an object to a string

v 3.0
(defn ^String to-string
  ([string]
   (protocol.string/-to-string string)))
link
(to-string :hello/world) => "hello/world" (to-string *ns*) => "std.string.coerce-test"

trim ^

[s]
Added 3.0

trims the string of whitespace

v 3.0
(defn ^String trim
  ([^CharSequence s]
   (let [len (.length s)]
     (loop [rindex len]
       (if (zero? rindex)
         ""
         (if (Character/isWhitespace (.charAt s (dec rindex)))
           (recur (dec rindex))
          ;; there is at least one non-whitespace char in the string,
          ;; so no need to check for lindex reaching len.
           (loop [lindex 0]
             (if (Character/isWhitespace (.charAt s lindex))
               (recur (inc lindex))
               (.. s (subSequence lindex rindex) toString)))))))))
link
(trim " hello ") => "hello"

trim-left ^

[s]
Added 3.0

trims the string of whitespace on left

v 3.0
(defn ^String trim-left
  ([^CharSequence s]
   (let [len (.length s)]
     (loop [index 0]
       (if (= len index)
         ""
         (if (Character/isWhitespace (.charAt s index))
           (recur (unchecked-inc index))
           (.. s (subSequence index len) toString)))))))
link
(trim-left " hello ") => "hello "

trim-newlines ^

[s]
Added 3.0

removes newlines from right

v 3.0
(defn ^String trim-newlines
  ([^CharSequence s]
   (loop [index (.length s)]
     (if (zero? index)
       ""
       (let [ch (.charAt s (dec index))]
         (if (or (= ch newline) (= ch return))
           (recur (dec index))
           (.. s (subSequence 0 index) toString)))))))
link
(trim-newlines "nn hello nn") => "nn hello "

trim-right ^

[s]
Added 3.0

trims the string of whitespace on right

v 3.0
(defn ^String trim-right
  ([^CharSequence s]
   (loop [index (.length s)]
     (if (zero? index)
       ""
       (if (Character/isWhitespace (.charAt s (unchecked-dec index)))
         (recur (unchecked-dec index))
         (.. s (subSequence 0 index) toString))))))
link
(trim-right " hello ") => "hello"

truncate ^

[s limit]
Added 3.0

truncates a word

v 3.0
(defn ^String truncate
  ([s limit]
   (let [len (count s)]
     (if (> len limit)
       (subs s 0 limit)
       s))))
link
(truncate "hello there" 5) => "hello"

typeless= ^

[x y]
Added 3.0

compares two representations

v 3.0
(defn ^String typeless=
  ([x y]
   (= (lower-sep-case x)
      (lower-sep-case y))))
link
(typeless= "helloWorld" "hello_world") => true ((wrap typeless=) :a-b-c "a b c") => true ((wrap typeless=) 'getMethod :get-method) => true

upper-camel-case ^

[value]
Added 4.0

convert string to uppercase camel

v 4.0
(defn ^String upper-camel-case
  ([^String value]
   (let [s (re-sub value
                   +non-camel-pattern+
                   (fn [s] (common/upper-case (apply str (rest s)))))]
     (str (.toUpperCase (subs s 0 1))
          (subs s 1)))))
link
(upper-camel-case "hello-world") => "HelloWorld"

upper-case ^

[s]
Added 3.0

converts a string object to upper case

v 3.0
(defn ^String upper-case
  ([^CharSequence s]
   (.. s toString toUpperCase)))
link
(upper-case "hello-world") => "HELLO-WORLD" ((wrap upper-case) :hello-world) => :HELLO-WORLD

upper-sep-case ^

[value]
Added 3.0

converts a string-like object to upper case representation

v 3.0
(defn ^String upper-sep-case
  ([^String value]
   (-> (separate-humps value)
       (common/split #"[ |-|_]")
       (->> (map common/upper-case))
       (common/joinl " "))))
link
(upper-sep-case "hello world") => "HELLO WORLD" (str ((wrap upper-sep-case) 'hello-world)) => "HELLO WORLD"

whitespace? ^

[s]
Added 3.0

checks if the string is all whitespace

v 3.0
(defn whitespace?
  ([s]
   (boolean (or (= "" s) (re-find #"^[st]+$" s)))))
link
(whitespace? " ") => true

wrap ^

[f]
Added 3.0

enables string-like ops for more types

v 3.0
(definvoke wrap
  [:memoize]
  ([f]
   (let [wrapper (get *lookup* f)
         wrapper (if wrapper
                   (wrap-fn wrapper)
                   (wrap-fn f))]
     (wrapper f))))
link
((wrap #(str '+ % '+)) 'hello) => '+hello+

write-line ^

[v]
Added 4.0

writes a line based on data structure

v 4.0
(defn write-line
  ([v]
   (if (vector? v)
     (let [tag  (first (keys (meta v)))
           body (common/join " " (map write-line v))
           body (if (#{"" :quote} tag)
                  (str "'" body "'")
                  body)]
       body)
     (h/strn v))))
link
(write-line [:a :b ["hello" "world"]]) => "a b hello world"

write-lines ^

[lines]
Added 4.0

writes a block of string

v 4.0
(defn write-lines
  [lines]
  (->> (map write-line lines)
       (common/join "n")))
link
(write-lines ["a" "b" "c"]) => "anbnc"

^

[& args]
Added 3.0

joins args with newlines

v 3.0
(defn |
  [& args]
  (common/join "n" args))
link
(| "abc" "def") => "abcndef"


+hump-pattern+ ^

NONE
(def ^:private +hump-pattern+ #"[a-z0-9][A-Z]")
link

+non-camel-pattern+ ^

NONE
(def ^:private +non-camel-pattern+ #"[_| |-][A-Za-z]")
link

+non-dot-pattern+ ^

NONE
(def ^:private +non-dot-pattern+ #"[ |-_]")
link

+non-snake-pattern+ ^

NONE
(def ^:private +non-snake-pattern+ #"[ |-]")
link

+non-spear-pattern+ ^

NONE
(def ^:private +non-spear-pattern+ #"[ |_]")
link

camel-case ^

[value]
Added 3.0

converts a string-like object to camel case representation

v 3.0
(defn ^String camel-case
  ([^String value]
   (let [s (re-sub value
                   +non-camel-pattern+
                   (fn [s] (common/upper-case (apply str (rest s)))))]
     (str (.toLowerCase (subs s 0 1))
          (subs s 1)))))
link
(camel-case "hello-world") => "helloWorld" ((wrap camel-case) 'hello_world) => 'helloWorld

capital-sep-case ^

[value]
Added 3.0

converts a string-like object to captital case representation

v 3.0
(defn ^String capital-sep-case
  ([^String value]
   (-> (separate-humps value)
       (common/split #"[ |-|_]")
       (->> (map common/capital-case))
       (common/joinl " "))))
link
(capital-sep-case "hello world") => "Hello World" (str ((wrap capital-sep-case) :hello-world)) => ":Hello World"

dot-case ^

[value]
Added 3.0

creates a dot-case representation

v 3.0
(defn ^String dot-case
  ([value]
   (-> (separate-humps value)
       (common/lower-case)
       (common/replace +non-dot-pattern+ "."))))
link
(dot-case "hello-world") => "hello.world" ((wrap dot-case) 'helloWorld) => 'hello.world

lower-sep-case ^

[value]
Added 3.0

converts a string-like object to a lower case representation

v 3.0
(defn ^String lower-sep-case
  ([^String value]
   (-> (separate-humps value)
       (common/split #"[ |-|_]")
       (->> (map common/lower-case))
       (common/joinl " "))))
link
(lower-sep-case "helloWorld") => "hello world" ((wrap lower-sep-case) 'hello-world) => (symbol "hello world")

pascal-case ^

[value]
Added 3.0

converts a string-like object to a pascal case representation

v 3.0
(defn ^String pascal-case
  ([^String value]
   (let [s (re-sub value +non-camel-pattern+
                   (fn [s] (common/upper-case (apply str (rest s)))))]
     (str (.toUpperCase (subs s 0 1))
          (subs s 1)))))
link
(pascal-case "helloWorld") => "HelloWorld" ((wrap pascal-case) :hello-world) => :HelloWorld

phrase-case ^

[value]
Added 3.0

converts a string-like object to snake case representation

v 3.0
(defn ^String phrase-case
  ([^String value]
   (let [s (lower-sep-case value)]
     (str (.toUpperCase (subs s 0 1))
          (subs s 1)))))
link
(phrase-case "hello-world") => "Hello world"

re-sub ^

[value pattern sub-func]
Added 3.0

substitute a pattern by applying a function

v 3.0
(defn- ^String re-sub
  ([^String value pattern sub-func]
   (loop [matcher (re-matcher pattern value)
          result []
          last-end 0]
     (if (.find matcher)
       (recur matcher
              (conj result
                    (.substring value last-end (.start matcher))
                    (sub-func (re-groups matcher)))
              (.end matcher))
       (apply str (conj result (.substring value last-end)))))))
link
(#'case/re-sub "aTa" @#'case/+hump-pattern+ (fn [_] "$")) => "$a"

separate-humps ^

[value]
Added 3.0

separate words that are camel cased

v 3.0
(defn- ^String separate-humps
  ([^String value]
   (re-sub value +hump-pattern+ #(common/joinl (seq %) " "))))
link
(#'case/separate-humps "aTaTa") => "a Ta Ta"

snake-case ^

[value]
Added 3.0

converts a string-like object to snake case representation

v 3.0
(defn ^String snake-case
  ([value]
   (-> (separate-humps value)
       (common/lower-case)
       (common/replace +non-snake-pattern+ "_"))))
link
(snake-case "hello-world") => "hello_world" ((wrap snake-case) 'helloWorld) => 'hello_world

spear-case ^

[value]
Added 3.0

converts a string-like object to spear case representation

v 3.0
(defn ^String spear-case
  ([value]
   (-> (separate-humps value)
       (common/lower-case)
       (common/replace +non-spear-pattern+ "-"))))
link
(spear-case "hello_world") => "hello-world" ((wrap spear-case) 'helloWorld) => 'hello-world

typeless= ^

[x y]
Added 3.0

compares two representations

v 3.0
(defn ^String typeless=
  ([x y]
   (= (lower-sep-case x)
      (lower-sep-case y))))
link
(typeless= "helloWorld" "hello_world") => true ((wrap typeless=) :a-b-c "a b c") => true ((wrap typeless=) 'getMethod :get-method) => true

upper-camel-case ^

[value]
Added 4.0

convert string to uppercase camel

v 4.0
(defn ^String upper-camel-case
  ([^String value]
   (let [s (re-sub value
                   +non-camel-pattern+
                   (fn [s] (common/upper-case (apply str (rest s)))))]
     (str (.toUpperCase (subs s 0 1))
          (subs s 1)))))
link
(upper-camel-case "hello-world") => "HelloWorld"

upper-sep-case ^

[value]
Added 3.0

converts a string-like object to upper case representation

v 3.0
(defn ^String upper-sep-case
  ([^String value]
   (-> (separate-humps value)
       (common/split #"[ |-|_]")
       (->> (map common/upper-case))
       (common/joinl " "))))
link
(upper-sep-case "hello world") => "HELLO WORLD" (str ((wrap upper-sep-case) 'hello-world)) => "HELLO WORLD"


from-string ^

[string type] [string type opts]
Added 3.0

converts a string to an object

v 3.0
(defn from-string
  ([string type]
   (from-string string type nil))
  ([string type opts]
   (protocol.string/-from-string string type opts)))
link
(from-string "a" clojure.lang.Symbol) => 'a (from-string "std.string" clojure.lang.Namespace) => (find-ns 'std.string)

path-separator ^

[type]
Added 3.0

returns the default path separator for an object

v 3.0
(definvoke path-separator
  [:memoize {:arglists '([type])
             :function protocol.string/-path-separator}])
link
(path-separator clojure.lang.Namespace) => "." (path-separator clojure.lang.Keyword) => "/"

str:compare ^

[f]
Added 3.0

wraps a function so that it can compare any two string-like inputs

v 3.0
(defn str:compare
  ([f]
   (fn [x y & args]
     (binding [h/*sep* (path-separator (type x))]
       (apply f (to-string x) (to-string y) args)))))
link
((str:compare =) :hello 'hello) => true

str:op ^

[f] [f return]
Added 3.0

wraps a string function such that it can take any string-like input

v 3.0
(defn str:op
  ([f]
   (str:op f false))
  ([f return]
   (fn [input & args]
     (let [[isample iarr?] (if (sequential? input)
                             [(first input) true]
                             [input false])
           class   (type isample)]
       (binding [h/*sep* (path-separator class)]
         (let [interim (cond (= class String)
                             (apply f input args)

                             iarr?
                             (apply f (map to-string input) args)

                             :else
                             (apply f (to-string input) args))
               oarr?   (sequential? interim)
               output  (cond return
                             interim

                             (= class String)
                             interim

                             oarr?
                             (mapv #(from-string % class) interim)

                             :else
                             (from-string interim class))]
           output))))))
link
((str:op str false) :hello 'hello) => :hellohello ((str:op str true) :hello 'hello) => "hellohello"

to-string ^

[string]
Added 3.0

converts an object to a string

v 3.0
(defn ^String to-string
  ([string]
   (protocol.string/-to-string string)))
link
(to-string :hello/world) => "hello/world" (to-string *ns*) => "std.string.coerce-test"


path-count ^

[s] [s sep]
Added 3.0

counts the number of elements in a given path

v 3.0
(defn path-count
  ([s]
   (path-count s h/*sep*))
  ([s sep]
   (count (path-split s sep))))
link
(path/path-count "a/b/c") => 3 ((wrap path/path-count) *ns*) => 3

path-join ^

[arr] [arr sep]
Added 3.0

joins a sequence of elements into a path separated value

v 3.0
(defn path-join
  ([arr] (path-join arr h/*sep*))
  ([arr sep]
   (if (seq arr)
     (-> (filter identity arr)
         (common/joinl sep)))))
link
(path/path-join ["a" "b" "c"]) => "a/b/c" ((wrap path/path-join) '[:a :b :c] "-") => :a-b-c ((wrap path/path-join) '[a b c] '-) => 'a-b-c

path-ns ^

[s] [s sep]
Added 3.0

returns the path namespace of the string

v 3.0
(defn path-ns
  ([s]
   (path-ns s h/*sep*))
  ([s sep]
   (-> s
       (path-ns-array sep)
       (path-join sep))))
link
(path/path-ns "a/b/c/d") => "a/b/c" ((wrap path/path-ns) :a.b.c ".") => :a.b

path-ns-array ^

[s] [s sep]
Added 3.0

returns the path vector of the string

v 3.0
(defn path-ns-array
  ([s]
   (path-ns-array s h/*sep*))
  ([s sep]
   (or (butlast (path-split s sep)) [])))
link
(path/path-ns-array "a/b/c/d") => ["a" "b" "c"] ((wrap path/path-ns-array) (keyword "a/b/c/d")) => [:a :b :c]

path-nth ^

[s n] [s n sep]
Added 3.0

check for the val of the string

v 3.0
(defn path-nth
  ([s n]
   (path-nth s n h/*sep*))
  ([s n sep]
   (nth (path-split s sep) n)))
link
(path/path-nth "a/b/c/d" 2) => "c"

path-root ^

[s] [s sep]
Added 3.0

returns the path root of the string

v 3.0
(defn path-root
  ([s]
   (path-root s h/*sep*))
  ([s sep]
   (first (path-ns-array s sep))))
link
(path/path-root "a/b/c/d") => "a" ((wrap path/path-root) 'a.b.c ".") => 'a

path-split ^

[s] [s sep]
Added 3.0

splits a sequence of elements into a path separated value

v 3.0
(defn path-split
  ([s] (path-split s h/*sep*))
  ([s sep]
   (common/split s (h/re-create sep))))
link
(path/path-split "a/b/c/d") => '["a" "b" "c" "d"] (path/path-split "a.b.c.d" ".") => ["a" "b" "c" "d"] ((wrap path/path-split) :hello/world) => [:hello :world] ((wrap path/path-split) :hello.world ".") => [:hello :world]

path-stem ^

[s] [s sep]
Added 3.0

returns the path stem of the string

v 3.0
(defn path-stem
  ([s]
   (path-stem s h/*sep*))
  ([s sep]
   (-> s
       (path-stem-array sep)
       (path-join sep))))
link
(path/path-stem "a/b/c/d") => "b/c/d" ((wrap path/path-stem) 'a.b.c.d ".") => 'b.c.d

path-stem-array ^

[s] [s sep]
Added 3.0

returns the path stem vector of the string

v 3.0
(defn path-stem-array
  ([s]
   (path-stem-array s h/*sep*))
  ([s sep]
   (rest (path-split s sep))))
link
(path/path-stem-array "a/b/c/d") => ["b" "c" "d"] ((wrap path/path-stem-array) 'a.b.c.d ".") => '[b c d]

path-sub ^

[s start num] [s start num sep]
Added 3.0

returns a subsection of the path within the string

v 3.0
(defn path-sub
  ([s start num]
   (path-sub s start num h/*sep*))
  ([s start num sep]
   (-> (path-sub-array s start num sep)
       (path-join sep))))
link
(path/path-sub "a/b/c/d" 1 2) => "b/c" ((wrap path/path-sub) (symbol "a/b/c/d") 1 2) => 'b/c

path-sub-array ^

[s start num] [s start num sep]
Added 3.0

returns a sub array of the path within the string

v 3.0
(defn path-sub-array
  ([s start num]
   (path-sub-array s start num h/*sep*))
  ([s start num sep]
   (->> (path-split s sep)
        (drop start)
        (take num))))
link
(path/path-sub-array "a/b/c/d" 1 2) => ["b" "c"] ((wrap path/path-sub-array) (symbol "a/b/c/d") 1 2) => '[b c]

path-val ^

[s] [s sep]
Added 3.0

returns the val of the string

v 3.0
(defn path-val
  ([s]
   (path-val s h/*sep*))
  ([s sep]
   (last (path-split s sep))))
link
(path/path-val "a/b/c/d") => "d" ((wrap path/path-val) 'a.b.c.d ".") => 'd


escape-dollars ^

[s]
Added 3.0

for regex purposes, escape dollar signs in strings

v 3.0
(defn escape-dollars
  ([^String s]
   (.replaceAll s "\$" "\\\$")))
link
(escape-dollars "$") => string?

escape-escapes ^

[s]
Added 3.0

makes sure that newlines are printable

v 3.0
(defn escape-escapes
  ([^String s]
   (.replaceAll s "(\\)([A-Za-z])" "$1$1$2")))
link
(escape-escapes "\n") => "\\n"

escape-newlines ^

[s]
Added 3.0

makes sure that newlines are printable

v 3.0
(defn escape-newlines
  ([^String s]
   (.replaceAll s "\n" "\\n")))
link
(escape-newlines "\n") => "\n"

escape-quotes ^

[s]
Added 3.0

makes sure that quotes are printable in string form

v 3.0
(defn escape-quotes
  ([^String s]
   (.replaceAll s "(\\)?"" "$1$1\\\"")))
link
(escape-quotes ""hello"") => "\"hello\""

filter-empty-lines ^

[s]
Added 3.0

filter empty line

v 3.0
(defn filter-empty-lines
  [s]
  (->> (common/split-lines s)
       (remove (fn [line] (re-find #"^s*$" line)))
       (common/join "n")))
link
(filter-empty-lines (common/join "n" ["a" " " " " "b"])) => "anb"

has-quotes? ^

[s]
Added 3.0

checks if a string has quotes

v 3.0
(defn has-quotes?
  ([^String s]
   (and (.startsWith s """)
        (.endsWith s """))))
link
(has-quotes? ""hello"") => true

indent ^

[block n & [{:keys [custom], :or {custom ""}}]]
Added 4.0

indents a block of string

v 4.0
(defn indent
  ([block n & [{:keys [custom] :or
                {custom ""}}]]
   (if (and (not (pos? n))
            (empty? custom))
     block
     (->> (common/split-lines block)
          (map (fn [line] (str (spaces n) custom line)))
          (common/join "n")))))
link
(indent (write-lines ["a" "b" "c"]) 2) => "an bn c"

indent-rest ^

[block n & [{:keys [custom], :or {custom ""}}]]
Added 4.0

indents the rest of the boiy

v 4.0
(defn indent-rest
  ([block n & [{:keys [custom] :or
                {custom ""}}] ]
   (if (and (not (pos? n))
            (empty? custom))
     block
     (let [[head & rest] (common/split-lines block)]
       (cond (empty? rest)
             head

             :else
             (->> rest
                  (map (fn [line] (str (spaces n) custom line)))
                  (common/join "n")
                  (str head "n")))))))
link
(indent-rest (write-lines ["a" "b" "c"]) 2) => "an bn c"

join-lines ^

[arr]
Added 4.0

join non empty elements in array

v 4.0
(defn join-lines
  [arr]
  (common/join "n" arr))
link
(join-lines ["hello" "world"]) => "hellonworld"

layout-lines ^

[tokens] [tokens max]
Added 4.0

layout tokens in lines depending on max length

v 4.0
(defn layout-lines
  ([tokens]
   (layout-lines tokens 80))
  ([tokens max]
   (->> (reduce (fn [[lines curr] tok]
                  (let [stok (str tok)]
                    (if (< (inc (+ (count curr)
                                   (count stok)))
                           max)
                      [lines (str curr " " stok)]
                      [(conj lines curr) stok])))
                [[] (str (first tokens))]
                (rest tokens))
        (apply conj)
        (common/join "n"))))
link
(layout-lines ["hello" "world" "again" "a" "b"] 8) => "hellonworldnagain anb"

lines ^

[s]
Added 3.0

transforms a string to newline separated forms

v 3.0
(defn lines
  [s]
  (cons `| (common/split-lines s)))
link
(lines "abcndef") => '(std.string.prose/| "abc" "def")

multi-line? ^

[s]
Added 4.0

check that a string has newlines

v 4.0
(defn multi-line?
  ([s]
   (and s (boolean (re-find #"n" s)))))
link
(multi-line? "anb") => true (multi-line? "a") => false

single-line ^

[s]
Added 3.0

replace newlines with spaces

v 3.0
(defn single-line
  [s]
  (->> (common/split-lines s)
       (common/join " ")))
link
(single-line "anbnc") => "a b c"

single-line? ^

[s]
Added 4.0

check that a string does not have newlines

v 4.0
(defn single-line?
  ([s]
   (not (multi-line? s))))
link
(single-line? "anb") => false (single-line? "a") => true

spaces ^

[n]
Added 4.0

create `n` spaces

v 4.0
(defn spaces
  ([n]
   (apply str (repeat n " "))))
link
(spaces 4) => ""

strip-quotes ^

[s]
Added 3.0

gets rid of quotes in a string

v 3.0
(defn strip-quotes
  ([s]
   (if (has-quotes? s)
     (subs s 1 (dec (count s)))
     s)))
link
(strip-quotes ""hello"") => "hello"

whitespace? ^

[s]
Added 3.0

checks if the string is all whitespace

v 3.0
(defn whitespace?
  ([s]
   (boolean (or (= "" s) (re-find #"^[st]+$" s)))))
link
(whitespace? " ") => true

write-line ^

[v]
Added 4.0

writes a line based on data structure

v 4.0
(defn write-line
  ([v]
   (if (vector? v)
     (let [tag  (first (keys (meta v)))
           body (common/join " " (map write-line v))
           body (if (#{"" :quote} tag)
                  (str "'" body "'")
                  body)]
       body)
     (h/strn v))))
link
(write-line [:a :b ["hello" "world"]]) => "a b hello world"

write-lines ^

[lines]
Added 4.0

writes a block of string

v 4.0
(defn write-lines
  [lines]
  (->> (map write-line lines)
       (common/join "n")))
link
(write-lines ["a" "b" "c"]) => "anbnc"

^

[& args]
Added 3.0

joins args with newlines

v 3.0
(defn |
  [& args]
  (common/join "n" args))
link
(| "abc" "def") => "abcndef"


*lookup* ^

NONE
(def ^:dynamic *lookup*
  (reduce (fn [out [k arr]]
            (into out (map vector arr (repeat k))))
          {}
          +defaults+))
link

+defaults+ ^

NONE
(def +defaults+)
link

join ^

[arr] [sep arr]
Added 3.0

extends common/join to all string-like types

v 3.0
(defn join
  ([arr]
   (join arr h/*sep*))
  ([sep arr]
   (coerce/from-string (common/join sep (map coerce/to-string arr))
                       (type (first arr)))))
link
(join "." [:a :b :c]) => :a.b.c

wrap ^

[f]
Added 3.0

enables string-like ops for more types

v 3.0
(definvoke wrap
  [:memoize]
  ([f]
   (let [wrapper (get *lookup* f)
         wrapper (if wrapper
                   (wrap-fn wrapper)
                   (wrap-fn f))]
     (wrapper f))))
link
((wrap #(str '+ % '+)) 'hello) => '+hello+

wrap-fn ^

Added 3.0

multimethod for extending wrap

v 3.0
(defmulti wrap-fn
  identity)
link
(let [f #(str '+ % '+)] (((wrap-fn f) f) :hello)) => :+hello+

5    std.string: A Comprehensive Summary

The std.string module in foundation-base provides a rich set of utilities for manipulating, transforming, and analyzing strings in Clojure. It extends core string functionalities, offers various casing conversions, path manipulation, pluralization/singularization, and prose formatting. A key feature is its wrap mechanism, which allows these string operations to work seamlessly across different "string-like" types (e.g., keywords, symbols, namespaces, byte arrays) by coercing them to strings, applying the operation, and then coercing them back to their original type.

The module is organized into several sub-namespaces:

5.1    std.string.case

This namespace focuses on converting strings between various common casing conventions.

  • camel-case [value]: Converts to camelCase (e.g., "hello-world" -> "helloWorld").n upper-camel-case [value]: Converts to UpperCamelCase (e.g., "hello-world" -> "HelloWorld").n capital-sep-case [value]: Converts to Capital Separated Case (e.g., "hello world" -> "Hello World").n lower-sep-case [value]: Converts to lower separated case (e.g., "helloWorld" -> "hello world").n pascal-case [value]: Converts to PascalCase (same as upper-camel-case).n phrase-case [value]: Converts to Phrase case (e.g., "hello-world" -> "Hello world").n dot-case [value]: Converts to dot.case (e.g., "hello-world" -> "hello.world").n snake-case [value]: Converts to snake_case (e.g., "hello-world" -> "helloworld").n spear-case [value]: Converts to spear-case (e.g., "helloworld" -> "hello-world").n upper-sep-case [value]: Converts to UPPER SEPARATED CASE (e.g., "hello world" -> "HELLO WORLD").n typeless= [x y]: Compares two string-like objects for equality, ignoring casing and separators (e.g., "helloWorld" and "hello_world" are true).

5.2    std.string.coerce

This namespace provides the core mechanisms for coercing various types to and from strings, and for defining how string operations should behave for different types.

  • from-string [string type & [opts]]: Converts a string to an object of a specified type (e.g., clojure.lang.Symbol, clojure.lang.Namespace).n to-string [string]: Converts an object to its string representation.n path-separator [type]: A multimethod that returns the default path separator for a given type (e.g., . for Namespace, / for Keyword).n str:op [f & [return]]: A higher-order function that wraps a string function f. It automatically coerces input arguments to strings, applies f, and then coerces the result back to the original input type (or String if return is true).n str:compare [f]: A higher-order function that wraps a comparison function f. It coerces two string-like inputs to strings before applying f.n protocol.string/IString extensions: Extends nil, Object, String, byte[], char[], Class, clojure.lang.Keyword, clojure.lang.Symbol, clojure.lang.Namespace to implement IString for to-string functionality.n protocol.string/-from-string extensions: Extends String, byte[], char[], Class, clojure.lang.Keyword, clojure.lang.Symbol, clojure.lang.Namespace for from-string functionality.n* protocol.string/-path-separator extensions: Extends Class and clojure.lang.Namespace for path-separator functionality.

5.3    std.string.common

This namespace offers a collection of general-purpose string utilities, including checks, splits, joins, case conversions, and replacements.

  • blank? [s]: Checks if a string is nil or contains only whitespace.n split [s re]: Splits a string by a regular expression into a vector.n split-lines [s]: Splits a string into a vector of lines.n joinl [coll & [separator]]: Joins a collection of strings with an optional separator.n join [separator coll]: Similar to joinl, but with arguments reordered for threading.n upper-case [s]: Converts a string to uppercase.n lower-case [s]: Converts a string to lowercase.n capital-case [s]: Capitalizes the first letter of a string and lowercases the rest.n reverse [s]: Reverses a string.n starts-with? [s substr]: Checks if a string starts with a substring.n ends-with? [s substr]: Checks if a string ends with a substring.n includes? [s substr]: Checks if a string contains a substring.n trim [s]: Trims leading and trailing whitespace.n trim-left [s]: Trims leading whitespace.n trim-right [s]: Trims trailing whitespace.n trim-newlines [s]: Trims trailing newlines.n escape [s cmap]: Escapes characters in a string using a character map.n replace [s match replacement]: Replaces occurrences of match with replacement. match can be a character, string, or Pattern.n caseless= [x y]: Compares two strings for equality, ignoring case.n truncate [s limit]: Truncates a string to a specified limit.n capitalize [s]: Capitalizes the first letter of a string.n decapitalize [s]: Lowercases the first letter of a string.n replace-all [s match re]: Shortcut for String.replaceAll.

5.4    std.string.path

This namespace provides functions for manipulating string representations of paths, treating them as hierarchical structures.

  • path-join [arr & [sep]]: Joins a sequence of path elements into a path string using a separator (defaults to h/*sep*).n path-split [s & [sep]]: Splits a path string into a sequence of elements using a separator (defaults to h/*sep*).n path-ns-array [s & [sep]]: Returns all but the last element of a path as a vector (the "namespace" part).n path-ns [s & [sep]]: Returns the "namespace" part of a path as a joined string.n path-root [s & [sep]]: Returns the first element of a path (the "root").n path-stem-array [s & [sep]]: Returns all but the first element of a path as a vector (the "stem" part).n path-stem [s & [sep]]: Returns the "stem" part of a path as a joined string.n path-val [s & [sep]]: Returns the last element of a path (the "value").n path-nth [s n & [sep]]: Returns the n-th element of a path.n path-sub-array [s start num & [sep]]: Returns a sub-array of path elements.n path-sub [s start num & [sep]]: Returns a sub-section of a path as a joined string.n* path-count [s & [sep]]: Counts the number of elements in a path.

5.5    std.string.plural

This namespace provides functionalities for pluralizing and singularizing English words, handling irregular forms and uncountable nouns.

  • *uncountable*, *singular-irregular*, *plural-irregular*, *plural-rules*, *singular-rules*: Dynamic vars (atoms) holding the rules and exceptions for pluralization/singularization.n +base-uncountable+, +base-irregular+, +base-plural+, +base-singular+: Initial data for the rules.n uncountable? [word]: Checks if a word is in the list of uncountable nouns.n irregular? [word]: Checks if a word is an irregular plural/singular form.n resolve-rules [rules word]: Applies a set of rules to a word to find a matching pattern and replacement.n singular [s]: Converts a word to its singular form.n plural [s]: Converts a word to its plural form.n* *initalized*: A var that ensures the base rules are loaded into the dynamic atoms upon namespace loading.

5.6    std.string.prose

This namespace offers utilities for formatting and manipulating text as prose, including handling quotes, whitespace, line breaks, and indentation.

  • has-quotes? [s]: Checks if a string is enclosed in double quotes.n strip-quotes [s]: Removes leading and trailing double quotes from a string.n whitespace? [s]: Checks if a string consists entirely of whitespace.n escape-dollars [s]: Escapes dollar signs in a string for regex purposes.n escape-newlines [s]: Escapes newline characters (&#92;n) to &#92;&#92;n for printable representation.n escape-escapes [s]: Escapes backslashes that precede letters.n escape-quotes [s]: Escapes double quotes in a string.n filter-empty-lines [s]: Removes empty or whitespace-only lines from a multi-line string.n single-line [s]: Replaces newlines with spaces, converting a multi-line string to a single line.n join-lines [arr]: Joins non-empty elements of an array with newlines.n spaces [n]: Creates a string with n spaces.n write-line [v]: Writes a single line based on a data structure, handling nested structures.n write-lines [lines]: Writes a block of strings, joining them with newlines.n indent [block n & [opts]]: Indents a block of text by n spaces, optionally with a custom prefix.n indent-rest [block n & [opts]]: Indents all lines of a block except the first.n multi-line? [s]: Checks if a string contains newline characters.n single-line? [s]: Checks if a string does not contain newline characters.n* layout-lines [tokens & [max]]: Lays out a sequence of tokens into lines, respecting a maximum line length.

5.7    std.string.wrap

This namespace provides the core wrap functionality, which is a powerful mechanism for extending string operations to work with various "string-like" types.

  • wrap-fn [f]: A multimethod that determines the appropriate wrapper function for a given string operation f.n :default method uses coerce/str:op (coerces to string, applies f, coerces back).n :return method uses coerce/str:op with return set to true (always returns a string).n :compare method uses coerce/str:compare (coerces both inputs to string, applies f).n +defaults+: A map defining which string functions should use which wrap-fn method.n *lookup*: A dynamic var (atom) that maps string functions to their corresponding wrapper types (e.g., :compare, :return, :default).n join [arr & [sep]]: An extended version of common/join that works with string-like types.n* wrap [f]: The main macro that takes a string function f and returns a new function that can operate on string-like types. It uses *lookup* and wrap-fn to determine the correct coercion and wrapping logic.

Overall Importance:

The std.string module is a fundamental utility within the foundation-base project. Its comprehensive set of string manipulation tools, combined with the powerful wrap mechanism, enables:

  • Flexible String Handling: Operations can be applied uniformly to various string-like data types, reducing boilerplate and improving code readability.n Data Normalization: Standardized casing and path manipulation functions are crucial for consistent data processing and interoperability with external systems.n Code Generation and Transformation: The ability to manipulate strings and symbols effectively is vital for meta-programming tasks, such as generating code in different target languages.n Improved Readability and Formatting: Prose-related functions help in presenting information clearly and consistently.n Extensibility: The protocol-based coercion and wrapping mechanisms allow for easy extension to new "string-like" types as needed.

By providing these versatile and extensible string utilities, std.string significantly contributes to the foundation-base project's ability to handle diverse data formats and support its multi-language development ecosystem.