Skip to content


Subversion checkout URL

You can clone with
Download ZIP
branch: master
185 lines (154 sloc) 4.241 kb
(ns garden.util
"Utility functions used by Garden."
(:refer-clojure :exclude [format])
[clojure.string :as string]
[garden.types :as t]
#+cljs [goog.string]
#+cljs [goog.string.format])
(:import garden.types.CSSAtRule))
;; ---------------------------------------------------------------------
;; String utilities
(defn format
"Formats a string using goog.string.format."
[fmt & args]
(apply goog.string/format fmt args))
;; To avoid the pain of #+cljs :refer.
(def format #'clojure.core/format)
(defprotocol ToString
(^String to-str [this] "Convert a value into a string."))
(extend-protocol ToString
#+clj clojure.lang.Keyword
#+cljs Keyword
(to-str [this] (name this))
#+clj Object
#+cljs default
(to-str [this] (str this))
nil (to-str [this] ""))
(defn ^String as-str
"Convert a variable number of values into strings."
[& args]
(apply str (map to-str args)))
(defn string->int
"Convert a string to an integer with optional base."
[s & [radix]]
(let [radix (or radix 10)]
(Integer/parseInt ^String s ^Long radix)
(js/parseInt s radix)))
(defn int->string
"Convert an integer to a string with optional base."
[i & [radix]]
(let [radix (or radix 10)]
(Integer/toString ^Long i ^Long radix)
(.toString i radix)))
(defn space-join
"Return a space separated list of values."
(string/join " " (map to-str xs)))
(defn comma-join
"Return a comma separated list of values. Subsequences are joined with
(let [ys (for [x xs]
(if (sequential? x)
(space-join x)
(to-str x)))]
(string/join ", " ys)))
(defn wrap-quotes
"Wrap a string with double quotes."
(str \" s \"))
;; ---------------------------------------------------------------------
;; Predicates
(defn record?
"True if x is an instance of or satisfies clojure.lang.IRecord."
(satisfies? IRecord x))
(defn hash-map?
"True if `(map? x)` and `x` does not satisfy `clojure.lang.IRecord`."
(and (map? x) (not (record? x))))
^{:doc "Alias to `vector?`."}
rule? vector?)
^{:doc "Alias to `hash-map?`."}
declaration? hash-map?)
(defn at-rule?
(instance? #+clj CSSAtRule #+cljs t/CSSAtRule x))
(defn at-media?
"True if `x` is a CSS `@media` rule."
(and (at-rule? x) (= (:identifier x) :media)))
(defn at-keyframes?
"True if `x` is a CSS `@keyframes` rule."
(and (at-rule? x) (= (:identifier x) :keyframes)))
(defn at-import?
"True if `x` is a CSS `@import` rule."
(and (at-rule? x) (= (:identifier x) :import)))
(defn prefix
"Attach a CSS style prefix to s."
[p s]
(let [p (to-str p)]
(if (= \- (last p))
(str p s)
(str p \- s))))
(defn vendor-prefix
"Attach a CSS vendor prefix to s."
[p s]
(let [p (to-str p)]
(if (= \- (first p))
(prefix p s)
(prefix (str \- p) s))))
;; ---------------------------------------------------------------------
;; Math utilities
(defn natural?
"True if n is a natural number."
(and (integer? n) (pos? n)))
(defn between?
"True if n is a number between a and b."
[n a b]
(let [bottom (min a b)
top (max a b)]
(and (>= n bottom) (<= n top))))
(defn clip
"Return a number such that n is no less than a and no more than b."
[a b n]
(let [[a b] (if (<= a b) [a b] [b a])]
(max a (min b n))))
(defn average
"Return the average of two or more numbers."
[n m & more]
(/ (apply + n m more) (+ 2.0 (count more))))
;; Taken from clojure.math.combinatorics.
(defn cartesian-product
"All the ways to take one item from each sequence."
[& seqs]
(let [v-original-seqs (vec seqs)
(fn step [v-seqs]
(let [increment
(fn [v-seqs]
(loop [i (dec (count v-seqs)), v-seqs v-seqs]
(if (= i -1) nil
(if-let [rst (next (v-seqs i))]
(assoc v-seqs i rst)
(recur (dec i) (assoc v-seqs i (v-original-seqs i)))))))]
(when v-seqs
(cons (map first v-seqs)
(lazy-seq (step (increment v-seqs)))))))]
(when (every? seq seqs)
(lazy-seq (step v-original-seqs)))))
Jump to Line
Something went wrong with that request. Please try again.