(merge-env!
  :resource-paths #{"src/main/cljc" "src/spec/cljc" "src/main/js"}
  :dependencies   '[[org.clojure/clojure         "1.9.0-alpha17"]
                    [org.clojure/clojurescript   "1.9.908"]
                    [moxaj/macrowbar             "0.1.2"]

                    ;; test
                    [org.clojure/test.check      "0.10.0-alpha1" :scope "test"]

                    ;; script
                    [adzerk/boot-test            "1.2.0"  :scope "test"]
                    [adzerk/boot-reload          "0.5.1"  :scope "test"]
                    [adzerk/boot-cljs            "2.0.0"  :scope "test"]
                    [adzerk/boot-cljs-repl       "0.3.3"  :scope "test"]
                    [pandeiro/boot-http          "0.8.3"  :scope "test"]
                    [crisptrutski/boot-cljs-test "0.3.0"  :scope "test"]
                    [boot-codox                  "0.10.3" :scope "test"]

                    [com.cemerick/piggieback     "0.2.1"  :scope "test"]
                    [weasel                      "0.7.0"  :scope "test"]
                    [org.clojure/tools.nrepl     "0.2.13" :scope "test"]
                    [nodisassemble               "0.1.3"  :scope "test"]]
  :repositories   [["clojars" {:url      "https://clojars.org/repo"
                               :username (System/getenv "CLOJARS_USER")
                               :password (System/getenv "CLOJARS_PASS")}]])

(require '[adzerk.boot-test :as boot-test]
         '[adzerk.boot-reload :as boot-reload]
         '[adzerk.boot-cljs :as boot-cljs]
         '[adzerk.boot-cljs-repl :as boot-cljs-repl]
         '[pandeiro.boot-http :as boot-http]
         '[crisptrutski.boot-cljs-test :as boot-cljs-test]
         '[codox.boot :as boot-codox])

(task-options!
  pom  {:project     'moxaj/mikron
        :version     "0.6.3-SNAPSHOT"
        :description "mikron is a schema-based serialization library for Clojure and ClojureScript"
        :url         "http://github.com/moxaj/mikron"
        :license     {"Eclipse Public License" "http://www.eclipse.org/legal/epl-v10.html"}}
  push {:ensure-clean  false
        :ensure-branch "master"
        :repo          "clojars"})

;; Util

(def windows?
  "True if running on windows."
  (.. (System/getProperty "os.name") (toLowerCase) (startsWith "windows")))

(defn fix-slashes
  "Replaces all forward slashes with backwards slashes in the given string."
  [^String s]
  (if windows?
    (.replaceAll s "/" "\\\\")
    s))

(defn proc
  "Runs the args as a shell command."
  [& args]
  (-> (ProcessBuilder. args)
      (.inheritIO)
      (.start)
      (.waitFor)))

;; Config

(def cljs-compiler-opts
  "Default ClojureScript compiler options."
  {:static-fns         true
   :fn-invoke-direct   true
   :parallel-build     true
   :optimize-constants true
   :compiler-stats     true
   :elide-asserts      true
   :process-shim       false
   :closure-defines    {'macrowbar.util/DEBUG true}})

(def cljs-test-namespaces
  "ClojureScript test namespaces."
  '[mikron.runtime.core-test
    mikron.runtime.buffer-test])

(def clj-test-namespaces
  "Clojure test namespaces."
  (conj cljs-test-namespaces 'mikron.runtime.core-test2))

;; Tasks

(deftask testing
  "Adds the test files to the fileset."
  []
  (merge-env! :resource-paths #{"src/test/cljc" "src/test/cljs" "resources/test"})
  identity)

(deftask benchmarking
  "Adds the benchmark files to the fileset."
  []
  (merge-env! :resource-paths #{"src/benchmark/cljc" "src/benchmark/java" "resources/benchmark"}
              :dependencies   '[[com.cognitect/transit-clj         "0.8.300"]
                                [com.cognitect/transit-cljs        "0.8.239"]
                                [com.damballa/abracad              "0.4.14-alpha2"]
                                [gloss                             "0.2.6"]
                                [cheshire                          "5.7.1"]
                                [funcool/octet                     "1.0.1"]
                                [com.google.protobuf/protobuf-java "3.3.1"]
                                [com.taoensso/nippy                "2.14.0-alpha1"]
                                [criterium                         "0.4.4"]])
  identity)

(deftask dev
  "Dev task for proto-repl."
  []
  (merge-env! :init-ns        'user
              :resource-paths #{"src/dev"}
              :dependencies   '[[org.clojure/tools.namespace "0.2.11"]
                                [proto-repl                  "0.3.1" :exclusions [org.clojure/core.async]]])
  (require 'clojure.tools.namespace.repl)
  (apply (resolve 'clojure.tools.namespace.repl/set-refresh-dirs) (get-env :directories))
  (comp (testing)
        (benchmarking)
        (javac)))

(deftask test-clj
  "Runs the tests on JVM."
  []
  (comp (testing)
        (boot-test/test :namespaces clj-test-namespaces)))

(deftask test-node
  "Runs the tests in a Node.js environment."
  [o opt          VAL kw   "The optimization level for the cljs compiler."
   s self-hosted?     bool "True if self-hosted."]
  (let [opt (or opt :none)]
    (comp (testing)
          (if self-hosted?
            (with-pass-thru fileset
              (proc "lumo"
                    "-c" (str "\"" (System/getProperty "fake.class.path") "\"")
                    "-k" "lumo_cache"
                    (->> fileset
                         (input-files)
                         (map tmp-file)
                         (by-re [#"mikron[\\/]test_runner[\\/]node_self_hosted.cljs"])
                         (first)
                         (.getAbsolutePath))))
            (boot-cljs-test/test-cljs :js-env        :node
                                      :namespaces    cljs-test-namespaces
                                      :optimizations opt
                                      :cljs-opts     cljs-compiler-opts)))))

(deftask test-browser
  "Runs the tests in a browser environment."
  [o opt    VAL kw "The optimization level for the cljs compiler."
   e js-env VAL kw "The js environment."]
  (let [opt (or opt :none)]
    (comp (testing)
          (boot-cljs-test/test-cljs :js-env        (or js-env :slimer)
                                    :namespaces    cljs-test-namespaces
                                    :optimizations opt
                                    :cljs-opts     cljs-compiler-opts))))

(deftask test
  "Runs the specified tests."
  [p platform     VAL kw   "The platform to run on."
   t target       VAL kw   "The target for the cljs compiler."
   o opt          VAL kw   "The optimization level for the cljs compiler."
   s self-hosted?     bool "True if self-hosted."]
  (let [platform (or platform :clj)]
    (case platform
      :clj  (test-clj)
      :cljs (case (or target :browser)
              :browser (test-browser :opt    opt
                                     :js-env :slimer)
              :nodejs  (test-node :opt          opt
                                  :self-hosted? self-hosted?)))))

(deftask compile-cljs
  "Compiles the cljs source files."
  [o opt VAL kw  "The compiler optimization level."
   i id  VAL str "The id of the build."]
  (let [opt (or opt :none)]
    (boot-cljs/cljs
      :ids              [(fix-slashes (or id "browser/index"))]
      :compiler-options (assoc cljs-compiler-opts :optimizations opt))))

(deftask run-browser-repl
  "Compiles the cljs sources, serves them on localhost:3000, and sets up
   an nrepl listener.
   Terminal A
     - boot run-browser-repl
   Terminal B
     - boot repl -c
     - (boot-cljs-repl/start-repl)
   localhost:3000"
  [o opt VAL kw  "The compiler optimization level."]
  (comp (benchmarking)
        (testing)
        (javac)
        (boot-http/serve :dir "target/browser")
        (watch)
        (boot-reload/reload)
        (boot-cljs-repl/cljs-repl)
        (compile-cljs :id "browser/index" :opt opt)
        (target)
        (speak)))

(deftask run-node-repl
  "Runs a node repl."
  []
  (comp (testing)
        (benchmarking)
        (proc "lumo"
              "-c" (str "\"" (System/getProperty "fake.class.path") "\"")
              "-k" "lumo_cache"
              "-e" (str "\"(require '[mikron.runtime.core :as mikron "
                        ":refer [schema defschema pack unpack gen valid?]])\"")
              "-r")))

(deftask generate-docs
  "Generates the documentation using codox."
  []
  (comp (boot-codox/codox
          :name         "moxaj/mikron"
          :metadata     {:doc/format :markdown}
          :output-path  "docs"
          :source-paths (get-env :resource-paths)
          :exclude-vars #"^((map)?->\p{Upper}|(get|set|put|take).*\*)")
        (sift :move {#"docs" "../docs"})
        (target)))

(deftask deploy
  "Installs the artifact into the local maven repo and pushes to clojars."
  []
  (comp (pom)
        (jar)
        (install)
        (push)))

(merge-env!
  :resource-paths #{"src/main/cljc" "src/spec/cljc" "src/main/js"}
  :dependencies   '[[org.clojure/clojure         "1.9.0-alpha17"]
                    [org.clojure/clojurescript   "1.9.908"]
                    [moxaj/macrowbar             "0.1.2"]

                    ;; test
                    [org.clojure/test.check      "0.10.0-alpha1" :scope "test"]

                    ;; script
                    [adzerk/boot-test            "1.2.0"  :scope "test"]
                    [adzerk/boot-reload          "0.5.1"  :scope "test"]
                    [adzerk/boot-cljs            "2.0.0"  :scope "test"]
                    [adzerk/boot-cljs-repl       "0.3.3"  :scope "test"]
                    [pandeiro/boot-http          "0.8.3"  :scope "test"]
                    [crisptrutski/boot-cljs-test "0.3.0"  :scope "test"]
                    [boot-codox                  "0.10.3" :scope "test"]

                    [com.cemerick/piggieback     "0.2.1"  :scope "test"]
                    [weasel                      "0.7.0"  :scope "test"]
                    [org.clojure/tools.nrepl     "0.2.13" :scope "test"]
                    [nodisassemble               "0.1.3"  :scope "test"]]
  :repositories   [["clojars" {:url      "https://clojars.org/repo"
                               :username (System/getenv "CLOJARS_USER")
                               :password (System/getenv "CLOJARS_PASS")}]])

(require '[boot.util :as boot-util]
         '[adzerk.boot-test :as boot-test]
         '[adzerk.boot-reload :as boot-reload]
         '[adzerk.boot-cljs :as boot-cljs]
         '[adzerk.boot-cljs-repl :as boot-cljs-repl]
         '[pandeiro.boot-http :as boot-http]
         '[crisptrutski.boot-cljs-test :as boot-cljs-test]
         '[codox.boot :as boot-codox])

(task-options!
  pom  {:project     'moxaj/mikron
        :version     "0.6.3-SNAPSHOT"
        :description "mikron is a schema-based serialization library for Clojure and ClojureScript"
        :url         "http://github.com/moxaj/mikron"
        :license     {"Eclipse Public License" "http://www.eclipse.org/legal/epl-v10.html"}}
  push {:ensure-clean  false
        :ensure-branch "master"
        :repo          "clojars"})

;; Util

(def windows?
  "True if running on windows."
  (.. (System/getProperty "os.name") (toLowerCase) (startsWith "windows")))

(defn fix-slashes
  "Replaces all forward slashes with backwards slashes in the given string."
  [^String s]
  (if windows?
    (.replaceAll s "/" "\\\\")
    s))

(defn proc
  "Runs the args as a shell command."
  [& args]
  (let [exit-value (-> (ProcessBuilder. args)
                       (.inheritIO)
                       (.start)
                       (.waitFor))]
    (when-not (zero? exit-value)
      (boot-util/exit-error))))

;; Config

(def cljs-compiler-opts
  "Default ClojureScript compiler options."
  {:static-fns         true
   :fn-invoke-direct   true
   :parallel-build     true
   :optimize-constants true
   :compiler-stats     true
   :elide-asserts      true
   :process-shim       false
   :closure-defines    {'macrowbar.util/DEBUG true}})

(def cljs-test-namespaces
  "ClojureScript test namespaces."
  '[mikron.runtime.core-test
    mikron.runtime.buffer-test])

(def clj-test-namespaces
  "Clojure test namespaces."
  (conj cljs-test-namespaces 'mikron.runtime.core-test2))

;; Tasks

(deftask testing
  "Adds the test files to the fileset."
  []
  (merge-env! :resource-paths #{"src/test/cljc" "src/test/cljs" "resources/test"})
  identity)

(deftask benchmarking
  "Adds the benchmark files to the fileset."
  []
  (merge-env! :resource-paths #{"src/benchmark/cljc" "src/benchmark/java" "resources/benchmark"}
              :dependencies   '[[com.cognitect/transit-clj         "0.8.300"]
                                [com.cognitect/transit-cljs        "0.8.239"]
                                [com.damballa/abracad              "0.4.14-alpha2"]
                                [gloss                             "0.2.6"]
                                [cheshire                          "5.7.1"]
                                [funcool/octet                     "1.0.1"]
                                [com.google.protobuf/protobuf-java "3.3.1"]
                                [com.taoensso/nippy                "2.14.0-alpha1"]
                                [criterium                         "0.4.4"]])
  identity)

(deftask dev
  "Dev task for proto-repl."
  []
  (merge-env! :init-ns        'user
              :resource-paths #{"src/dev"}
              :dependencies   '[[org.clojure/tools.namespace "0.2.11"]
                                [proto-repl                  "0.3.1" :exclusions [org.clojure/core.async]]])
  (require 'clojure.tools.namespace.repl)
  (apply (resolve 'clojure.tools.namespace.repl/set-refresh-dirs) (get-env :directories))
  (comp (testing)
        (benchmarking)
        (javac)))

(deftask test-clj
  "Runs the tests on JVM."
  []
  (comp (testing)
        (boot-test/test :namespaces clj-test-namespaces)))

(deftask test-node
  "Runs the tests in a Node.js environment."
  [o opt          VAL kw   "The optimization level for the cljs compiler."
   s self-hosted?     bool "True if self-hosted."]
  (let [opt (or opt :none)]
    (comp (testing)
          (if self-hosted?
            (with-pass-thru fileset
              (proc "lumo"
                    "-c" (str "\"" (System/getProperty "fake.class.path") "\"")
                    "-k" "lumo_cache"
                    (->> fileset
                         (input-files)
                         (map tmp-file)
                         (by-re [#"mikron[\\/]test_runner[\\/]node_self_hosted.cljs"])
                         (first)
                         (.getAbsolutePath))))
            (boot-cljs-test/test-cljs :js-env        :node
                                      :namespaces    cljs-test-namespaces
                                      :optimizations opt
                                      :cljs-opts     cljs-compiler-opts)))))

(deftask test-browser
  "Runs the tests in a browser environment."
  [o opt    VAL kw "The optimization level for the cljs compiler."
   e js-env VAL kw "The js environment."]
  (let [opt (or opt :none)]
    (comp (testing)
          (boot-cljs-test/test-cljs :js-env        (or js-env :slimer)
                                    :namespaces    cljs-test-namespaces
                                    :optimizations opt
                                    :cljs-opts     cljs-compiler-opts))))

(deftask test
  "Runs the specified tests."
  [p platform     VAL kw   "The platform to run on."
   t target       VAL kw   "The target for the cljs compiler."
   o opt          VAL kw   "The optimization level for the cljs compiler."
   s self-hosted?     bool "True if self-hosted."]
  (let [platform (or platform :clj)]
    (case platform
      :clj  (test-clj)
      :cljs (case (or target :browser)
              :browser (test-browser :opt    opt
                                     :js-env :slimer)
              :nodejs  (test-node :opt          opt
                                  :self-hosted? self-hosted?)))))

(deftask compile-cljs
  "Compiles the cljs source files."
  [o opt VAL kw  "The compiler optimization level."
   i id  VAL str "The id of the build."]
  (let [opt (or opt :none)]
    (boot-cljs/cljs
      :ids              [(fix-slashes (or id "browser/index"))]
      :compiler-options (assoc cljs-compiler-opts :optimizations opt))))

(deftask run-browser-repl
  "Compiles the cljs sources, serves them on localhost:3000, and sets up
   an nrepl listener.
   Terminal A
     - boot run-browser-repl
   Terminal B
     - boot repl -c
     - (boot-cljs-repl/start-repl)
   localhost:3000"
  [o opt VAL kw  "The compiler optimization level."]
  (comp (benchmarking)
        (testing)
        (javac)
        (boot-http/serve :dir "target/browser")
        (watch)
        (boot-reload/reload)
        (boot-cljs-repl/cljs-repl)
        (compile-cljs :id "browser/index" :opt opt)
        (target)
        (speak)))

(deftask run-node-repl
  "Runs a node repl."
  []
  (comp (testing)
        (benchmarking)
        (proc "lumo"
              "-c" (str "\"" (System/getProperty "fake.class.path") "\"")
              "-k" "lumo_cache"
              "-e" (str "\"(require '[mikron.runtime.core :as mikron "
                        ":refer [schema defschema pack unpack gen valid?]])\"")
              "-r")))

(deftask generate-docs
  "Generates the documentation using codox."
  []
  (comp (boot-codox/codox
          :name         "moxaj/mikron"
          :metadata     {:doc/format :markdown}
          :output-path  "docs"
          :source-paths (get-env :resource-paths)
          :exclude-vars #"^((map)?->\p{Upper}|(get|set|put|take).*\*)")
        (sift :move {#"docs" "../docs"})
        (target)))

(deftask deploy
  "Installs the artifact into the local maven repo and pushes to clojars."
  []
  (comp (pom)
        (jar)
        (install)
        (push)))

Change proc to properly exit on non-zero subprocess exit value


(set-env!
  :resource-paths #{"src/main/cljc" "src/spec/cljc" "src/main/js/foreign"}
  :dependencies   '[[org.clojure/clojure         "1.9.0-alpha17"]
                    [org.clojure/clojurescript   "1.9.671"]
                    [org.clojure/test.check      "0.10.0-alpha2" :scope "test"]

                    [adzerk/boot-test            "1.2.0"  :scope "test"]
                    [adzerk/boot-reload          "0.5.1"  :scope "test"]
                    [adzerk/boot-cljs            "2.0.0"  :scope "test"]
                    [adzerk/boot-cljs-repl       "0.3.3"  :scope "test"]
                    [pandeiro/boot-http          "0.8.3"  :scope "test"]
                    [crisptrutski/boot-cljs-test "0.3.0"  :scope "test"]
                    [boot-codox                  "0.10.3" :scope "test"]

                    [me.raynes/conch             "0.8.0"  :scope "test"]
                    [com.cemerick/piggieback     "0.2.1"  :scope "test"]
                    [weasel                      "0.7.0"  :scope "test"]
                    [org.clojure/tools.nrepl     "0.2.13" :scope "test"]
                    [nodisassemble               "0.1.3"  :scope "test"]])

(merge-env!
  :repositories [["clojars" {:url      "https://clojars.org/repo"
                             :username (System/getenv "CLOJARS_USER")
                             :password (System/getenv "CLOJARS_PASS")}]])

(require '[adzerk.boot-test :as boot-test]
         '[adzerk.boot-reload :as boot-reload]
         '[adzerk.boot-cljs :as boot-cljs]
         '[adzerk.boot-cljs-repl :as boot-cljs-repl]
         '[pandeiro.boot-http :as boot-http]
         '[crisptrutski.boot-cljs-test :as boot-cljs-test]
         '[codox.boot :as boot-codox]
         '[me.raynes.conch.low-level :as conch])

(task-options!
  pom  {:project     'moxaj/mikron
        :version     "0.6.3-SNAPSHOT"
        :description "mikron is a schema-based serialization library for Clojure and ClojureScript"
        :url         "http://github.com/moxaj/mikron"
        :license     {"Eclipse Public License" "http://www.eclipse.org/legal/epl-v10.html"}}
  push {:ensure-clean  false
        :ensure-branch "master"
        :repo          "clojars"})

;; Util

(def windows?
  (.. (System/getProperty "os.name") (toLowerCase) (startsWith "windows")))

(defn fix-slashes
  "Replaces all forward slashes with backwards slashes in the given string."
  [^String s]
  (if windows?
    (.replaceAll s "/" "\\\\")
    s))

(defn host-process
  "Connects the stdin and stdout to a process."
  [process]
  (future (conch/feed-from process System/in))
  (future (while true (conch/flush process) (Thread/sleep 100)))
  (conch/stream-to-out process :out))

;; Tasks

(defn proc
  "Returns a task which runs the args as a shell command."
  [& args]
  (with-pass-thru _ (host-process (apply conch/proc args))))

(deftask testing
  "Adds the test files to the fileset."
  []
  (merge-env! :resource-paths #{"src/test/cljc" "src/test/cljs" "resources/test"})
  identity)

(deftask benchmarking
  "Adds the benchmark files to the fileset."
  []
  (merge-env! :resource-paths #{"src/benchmark/cljc" "src/benchmark/java" "resources/benchmark"}
              :dependencies   '[[com.cognitect/transit-clj         "0.8.300"]
                                [com.cognitect/transit-cljs        "0.8.239"]
                                [com.damballa/abracad              "0.4.14-alpha2"]
                                [gloss                             "0.2.6"]
                                [cheshire                          "5.7.1"]
                                [funcool/octet                     "1.0.1"]
                                [com.google.protobuf/protobuf-java "3.3.1"]
                                [com.taoensso/nippy                "2.14.0-alpha1"]
                                [criterium                         "0.4.4"]])
  identity)

(deftask dev
  "Dev task for proto-repl."
  []
  (merge-env! :init-ns        'user
              :resource-paths #{"src/dev"}
              :dependencies   '[[org.clojure/tools.namespace "0.2.11"]
                                [proto-repl                  "0.3.1" :exclusions [org.clojure/core.async]]])
  (require 'clojure.tools.namespace.repl)
  (apply (resolve 'clojure.tools.namespace.repl/set-refresh-dirs) (get-env :directories))
  (comp (testing)
        (benchmarking)
        (javac)))

(deftask test-clj
  "Runs the tests on JVM."
  []
  (comp (testing)
        (boot-test/test :namespaces ['mikron.test.core])))

(def cljs-compiler-opts
  {:static-fns       true
   :fn-invoke-direct true
   :parallel-build   true})

(deftask test-node
  "Runs the tests in a Node.js environment."
  [o opt          VAL kw   "The optimization level for the cljs compiler."
   s self-hosted?     bool "True if self-hosted."]
  (comp (testing)
        (if self-hosted?
          (proc "lumo"
                "-c" (System/getProperty "fake.class.path")
                "-k" "lumo_cache"
                "target/mikron/test_runner/node.cljs")
          (boot-cljs-test/test-cljs :js-env        :node
                                    :namespaces    '[mikron.runtime.core-tests
                                                     mikron.runtime.buffer-tests]
                                    :optimizations (or opt :none)
                                    :cljs-otps     cljs-compiler-opts))))

(deftask test-browser
  "Runs the tests in a browser environment."
  [o opt    VAL kw "The optimization level for the cljs compiler."
   e js-env VAL kw "The js environment."]
  (comp (testing)
        (boot-cljs-test/test-cljs :js-env        (or js-env :slimer)
                                  :namespaces    '[mikron.runtime.core-tests
                                                   mikron.runtime.buffer-tests]
                                  :optimizations (or opt :none)
                                  :cljs-otps     cljs-compiler-opts)))

(deftask test
  "Runs the specified tests."
  [p platform     VAL kw   "The platform to run on."
   t target       VAL kw   "The target for the cljs compiler."
   o opt          VAL kw   "The optimization level for the cljs compiler."
   s self-hosted?     bool "True if self-hosted."]
  (case (or platform :clj)
    :clj  (test-clj)
    :cljs (case (or target :browser)
            :browser (test-browser :opt    opt
                                   :js-env :slimer)
            :nodejs  (test-node :opt          opt
                                :self-hosted? self-hosted?))))

(deftask compile-cljs
  "Compiles the cljs source files."
  [o opt VAL kw  "The compiler optimization level."
   i id  VAL str "The id of the build."]
  (boot-cljs/cljs
    :ids              [(fix-slashes (or id "browser/index"))]
    :compiler-options (assoc cljs-compiler-opts
                        :optimizations (or opt :none))))

(deftask run-browser-repl
  "Compiles the cljs sources, serves them on localhost:3000, and sets up
   an nrepl listener.
   Terminal A
     - boot run-browser-repl
   Terminal B
     - boot repl -c
     - (boot-cljs-repl/start-repl)
   localhost:3000"
  [o opt VAL kw  "The compiler optimization level."]
  (comp (benchmarking)
        (testing)
        (javac)
        (boot-http/serve :dir "target/browser")
        (watch)
        (boot-reload/reload)
        (boot-cljs-repl/cljs-repl)
        (compile-cljs :id "browser/index" :opt opt)
        (target)
        (speak)))

(deftask run-node-repl
  "Runs a node repl.
   TODO does not work - waiting on Lumo commonjs modules"
  []
  (comp (testing)
        (benchmarking)
        (proc "lumo"
              "-c" (str "\"" (System/getProperty "fake.class.path") "\"")
              "-k" "lumo_cache"
              "-e" (str "\"(require '[mikron.runtime.core :as mikron "
                        ":refer [schema defschema pack unpack gen valid?]])\"")
              "-r")))

(deftask generate-docs
  "Generates the documentation using codox."
  []
  (comp (boot-codox/codox
          :name         "moxaj/mikron"
          :metadata     {:doc/format :markdown}
          :output-path  "docs"
          ;:namespaces   [#"^mikron\.(?!codegen)"]
          :exclude-vars #"^((map)?->\p{Upper}|(get|set|put|take).*\*)")
        (sift :move {#"docs" "../docs"})
        (target)))

(deftask deploy
  "Installs the artifact into the local maven repo and pushes to clojars."
  []
  (comp (pom)
        (jar)
        (install)
        (push)))

(set-env!
  :resource-paths #{"src/main/cljc" "src/spec/cljc" "src/main/js/foreign"}
  :dependencies   '[[org.clojure/clojure         "1.9.0-alpha17"]
                    [org.clojure/clojurescript   "1.9.671"]
                    [org.clojure/test.check      "0.10.0-alpha2" :scope "test"]

                    [adzerk/boot-test            "1.2.0"  :scope "test"]
                    [adzerk/boot-reload          "0.5.1"  :scope "test"]
                    [adzerk/boot-cljs            "2.0.0"  :scope "test"]
                    [adzerk/boot-cljs-repl       "0.3.3"  :scope "test"]
                    [pandeiro/boot-http          "0.8.3"  :scope "test"]
                    [crisptrutski/boot-cljs-test "0.3.0"  :scope "test"]
                    [boot-codox                  "0.10.3" :scope "test"]

                    [me.raynes/conch             "0.8.0"  :scope "test"]
                    [com.cemerick/piggieback     "0.2.1"  :scope "test"]
                    [weasel                      "0.7.0"  :scope "test"]
                    [org.clojure/tools.nrepl     "0.2.13" :scope "test"]
                    [nodisassemble               "0.1.3"  :scope "test"]])

(merge-env!
  :repositories [["clojars" {:url      "https://clojars.org/repo"
                             :username (System/getenv "CLOJARS_USER")
                             :password (System/getenv "CLOJARS_PASS")}]])

(require '[adzerk.boot-test :as boot-test]
         '[adzerk.boot-reload :as boot-reload]
         '[adzerk.boot-cljs :as boot-cljs]
         '[adzerk.boot-cljs-repl :as boot-cljs-repl]
         '[pandeiro.boot-http :as boot-http]
         '[crisptrutski.boot-cljs-test :as boot-cljs-test]
         '[codox.boot :as boot-codox]
         '[me.raynes.conch.low-level :as conch])

(task-options!
  pom  {:project     'moxaj/mikron
        :version     "0.6.3-SNAPSHOT"
        :description "mikron is a schema-based serialization library for Clojure and ClojureScript"
        :url         "http://github.com/moxaj/mikron"
        :license     {"Eclipse Public License" "http://www.eclipse.org/legal/epl-v10.html"}}
  push {:ensure-clean  false
        :ensure-branch "master"
        :repo          "clojars"})

;; Util

(def windows?
  (.. (System/getProperty "os.name") (toLowerCase) (startsWith "windows")))

(defn fix-slashes
  "Replaces all forward slashes with backwards slashes in the given string."
  [^String s]
  (if windows?
    (.replaceAll s "/" "\\\\")
    s))

(defn host-process
  "Connects the stdin and stdout to a process."
  [process]
  (future (conch/feed-from process System/in))
  (future (while true (conch/flush process) (Thread/sleep 100)))
  (conch/stream-to-out process :out))

;; Tasks

(defn proc
  "Returns a task which runs the args as a shell command."
  [& args]
  (with-pass-thru _ (host-process (apply conch/proc args))))

(deftask testing
  "Adds the test files to the fileset."
  []
  (merge-env! :resource-paths #{"src/test/cljc" "src/test/cljs" "resources/test"})
  identity)

(deftask benchmarking
  "Adds the benchmark files to the fileset."
  []
  (merge-env! :resource-paths #{"src/benchmark/cljc" "src/benchmark/java" "resources/benchmark"}
              :dependencies   '[[com.cognitect/transit-clj         "0.8.300"]
                                [com.cognitect/transit-cljs        "0.8.239"]
                                [com.damballa/abracad              "0.4.14-alpha2"]
                                [gloss                             "0.2.6"]
                                [cheshire                          "5.7.1"]
                                [funcool/octet                     "1.0.1"]
                                [com.google.protobuf/protobuf-java "3.3.1"]
                                [com.taoensso/nippy                "2.14.0-alpha1"]
                                [criterium                         "0.4.4"]])
  identity)

(deftask dev
  "Dev task for proto-repl."
  []
  (merge-env! :init-ns        'user
              :resource-paths #{"src/dev"}
              :dependencies   '[[org.clojure/tools.namespace "0.2.11"]
                                [proto-repl                  "0.3.1" :exclusions [org.clojure/core.async]]])
  (require 'clojure.tools.namespace.repl)
  (apply (resolve 'clojure.tools.namespace.repl/set-refresh-dirs) (get-env :directories))
  (comp (testing)
        (benchmarking)
        (javac)))

(deftask test-clj
  "Runs the tests on JVM."
  []
  (comp (testing)
        (boot-test/test :namespaces '[mikron.runtime.core-tests
                                      mikron.runtime.buffer-tests])))

(def cljs-compiler-opts
  {:static-fns       true
   :fn-invoke-direct true
   :parallel-build   true})

(deftask test-node
  "Runs the tests in a Node.js environment."
  [o opt          VAL kw   "The optimization level for the cljs compiler."
   s self-hosted?     bool "True if self-hosted."]
  (comp (testing)
        (if self-hosted?
          (proc "lumo"
                "-c" (System/getProperty "fake.class.path")
                "-k" "lumo_cache"
                "target/mikron/test_runner/node.cljs")
          (boot-cljs-test/test-cljs :js-env        :node
                                    :namespaces    '[mikron.runtime.core-tests
                                                     mikron.runtime.buffer-tests]
                                    :optimizations (or opt :none)
                                    :cljs-otps     cljs-compiler-opts))))

(deftask test-browser
  "Runs the tests in a browser environment."
  [o opt    VAL kw "The optimization level for the cljs compiler."
   e js-env VAL kw "The js environment."]
  (comp (testing)
        (boot-cljs-test/test-cljs :js-env        (or js-env :slimer)
                                  :namespaces    '[mikron.runtime.core-tests
                                                   mikron.runtime.buffer-tests]
                                  :optimizations (or opt :none)
                                  :cljs-otps     cljs-compiler-opts)))

(deftask test
  "Runs the specified tests."
  [p platform     VAL kw   "The platform to run on."
   t target       VAL kw   "The target for the cljs compiler."
   o opt          VAL kw   "The optimization level for the cljs compiler."
   s self-hosted?     bool "True if self-hosted."]
  (case (or platform :clj)
    :clj  (test-clj)
    :cljs (case (or target :browser)
            :browser (test-browser :opt    opt
                                   :js-env :slimer)
            :nodejs  (test-node :opt          opt
                                :self-hosted? self-hosted?))))

(deftask compile-cljs
  "Compiles the cljs source files."
  [o opt VAL kw  "The compiler optimization level."
   i id  VAL str "The id of the build."]
  (boot-cljs/cljs
    :ids              [(fix-slashes (or id "browser/index"))]
    :compiler-options (assoc cljs-compiler-opts
                        :optimizations (or opt :none))))

(deftask run-browser-repl
  "Compiles the cljs sources, serves them on localhost:3000, and sets up
   an nrepl listener.
   Terminal A
     - boot run-browser-repl
   Terminal B
     - boot repl -c
     - (boot-cljs-repl/start-repl)
   localhost:3000"
  [o opt VAL kw  "The compiler optimization level."]
  (comp (benchmarking)
        (testing)
        (javac)
        (boot-http/serve :dir "target/browser")
        (watch)
        (boot-reload/reload)
        (boot-cljs-repl/cljs-repl)
        (compile-cljs :id "browser/index" :opt opt)
        (target)
        (speak)))

(deftask run-node-repl
  "Runs a node repl.
   TODO does not work - waiting on Lumo commonjs modules"
  []
  (comp (testing)
        (benchmarking)
        (proc "lumo"
              "-c" (str "\"" (System/getProperty "fake.class.path") "\"")
              "-k" "lumo_cache"
              "-e" (str "\"(require '[mikron.runtime.core :as mikron "
                        ":refer [schema defschema pack unpack gen valid?]])\"")
              "-r")))

(deftask generate-docs
  "Generates the documentation using codox."
  []
  (comp (boot-codox/codox
          :name         "moxaj/mikron"
          :metadata     {:doc/format :markdown}
          :output-path  "docs"
          ;:namespaces   [#"^mikron\.(?!codegen)"]
          :exclude-vars #"^((map)?->\p{Upper}|(get|set|put|take).*\*)")
        (sift :move {#"docs" "../docs"})
        (target)))

(deftask deploy
  "Installs the artifact into the local maven repo and pushes to clojars."
  []
  (comp (pom)
        (jar)
        (install)
        (push)))

Update clj test namespaces

Update clj test namespaces


(ns leiningen.ancient.artifact.check
  (:require [leiningen.ancient.verbose :refer :all]
            [ancient-clj.core :as ancient]
            [version-clj.core :as version]))

;; ## Artifact Map

(defn read-artifact
  "Combine artifact path and artifact vector to a map of `:path`/`:artifact`."
  [path artifact-vector]
  (when (and (vector? artifact-vector)
             (symbol? (first artifact-vector))
             (string? (second artifact-vector)))
    {:path path
     :artifact (ancient/read-artifact artifact-vector)}))

(defn collect-artifacts
  "Collect all artifacts in the given map, based on `:allowed-keys` within
   the given options."
  ([options artifacts]
   (collect-artifacts options [] artifacts))
  ([{:keys [allowed-keys check-clojure?] :as options} path artifacts]
   (let [p (vec path)
         allowed-key? (set allowed-keys)
         allowed? (fn [k v]
                    (and (allowed-key? k)
                         (sequential? v)))]
     (if (map? artifacts)
       (->> (concat
              (for [[k data] artifacts
                    :when (allowed-key? k)]
                (collect-artifacts
                  options
                  (conj p k)
                  data))
              (for [[k data] artifacts
                    :when (map? data)
                    [k' artifacts'] data
                    :when (allowed? k' artifacts')]
                (collect-artifacts
                  options
                  (conj p k k')
                  artifacts')))
            (reduce concat))
       (cond->> (->> (map-indexed
                       (fn [i artifact-vector]
                         (read-artifact (conj p i) artifact-vector))
                       artifacts)
                     (filter identity))
         (not check-clojure?) (filter (comp not #{"clojure"} :id :artifact)))))))

;; ## Check

(defn- latest-version-with-cache!
  "Use the current cache to resolve the latest version of the given artifact."
  [{:keys [cache] :as opts}
   {:keys [group id] :as artifact}]
  (let [k [group id]
        d (delay
            (ancient/latest-version!
              artifact
              opts))]
    @(dosync
       (get
         (if (contains? @cache k)
           @cache
           (alter cache assoc k d))
         k))))

(defn check-artifact!
  "Check the given artifact data, associating `:latest` into it
   if the given version is outdated. Otherwise, `nil` will be returned."
  [options {:keys [path artifact] :as data}]
  (debugf "-- artifact %s at %s ... (%d repositories)"
          (pr-str (:form artifact))
          (pr-str path)
          (count (:repositories options)))
  (when-let [latest (latest-version-with-cache!
                      options
                      artifact)]
    (when (neg? (version/version-seq-compare
                  (:version artifact)
                  (:version latest)))
      (debugf "-- artifact %s is outdated. (latest: %s)"
              (pr-str (:form artifact))
              (pr-str (:version-string latest)))
      (assoc data :latest latest))))

(defn check-artifacts!
  "Check the given seq of artifacts using `pmap`."
  [options artifacts]
  (->> (pmap
         #(check-artifact! options %)
         artifacts)
       (filter identity)))

(defn collect-and-check-artifacts!
  "Collect and check all artifacts contained within the given data."
  [options data]
  (->> data
       (collect-artifacts options)
       (check-artifacts! options)))

(ns leiningen.ancient.artifact.check
  (:require [leiningen.ancient.verbose :refer :all]
            [ancient-clj.core :as ancient]
            [version-clj.core :as version]))

;; ## Artifact Map

(defn read-artifact
  "Combine artifact path and artifact vector to a map of `:path`/`:artifact`."
  [path artifact-vector]
  (when (and (vector? artifact-vector)
             (symbol? (first artifact-vector))
             (or (= (count artifact-vector) 1)
                 (string? (second artifact-vector))))
    {:path path
     :artifact (ancient/read-artifact artifact-vector)}))

(defn collect-artifacts
  "Collect all artifacts in the given map, based on `:allowed-keys` within
   the given options."
  ([options artifacts]
   (collect-artifacts options [] artifacts))
  ([{:keys [allowed-keys check-clojure?] :as options} path artifacts]
   (let [p (vec path)
         allowed-key? (set allowed-keys)
         allowed? (fn [k v]
                    (and (allowed-key? k)
                         (sequential? v)))]
     (if (map? artifacts)
       (->> (concat
              (for [[k data] artifacts
                    :when (allowed-key? k)]
                (collect-artifacts
                  options
                  (conj p k)
                  data))
              (for [[k data] artifacts
                    :when (map? data)
                    [k' artifacts'] data
                    :when (allowed? k' artifacts')]
                (collect-artifacts
                  options
                  (conj p k k')
                  artifacts')))
            (reduce concat))
       (cond->> (->> (map-indexed
                       (fn [i artifact-vector]
                         (read-artifact (conj p i) artifact-vector))
                       artifacts)
                     (filter identity))
         (not check-clojure?) (filter (comp not #{"clojure"} :id :artifact)))))))

;; ## Check

(defn- latest-version-with-cache!
  "Use the current cache to resolve the latest version of the given artifact."
  [{:keys [cache] :as opts}
   {:keys [group id] :as artifact}]
  (let [k [group id]
        d (delay
            (ancient/latest-version!
              artifact
              opts))]
    @(dosync
       (get
         (if (contains? @cache k)
           @cache
           (alter cache assoc k d))
         k))))

(defn check-artifact!
  "Check the given artifact data, associating `:latest` into it
   if the given version is outdated. Otherwise, `nil` will be returned."
  [options {:keys [path artifact] :as data}]
  (debugf "-- artifact %s at %s ... (%d repositories)"
          (pr-str (:form artifact))
          (pr-str path)
          (count (:repositories options)))
  (when-let [latest (latest-version-with-cache!
                      options
                      artifact)]
    (when (neg? (version/version-seq-compare
                  (:version artifact)
                  (:version latest)))
      (debugf "-- artifact %s is outdated. (latest: %s)"
              (pr-str (:form artifact))
              (pr-str (:version-string latest)))
      (assoc data :latest latest))))

(defn check-artifacts!
  "Check the given seq of artifacts using `pmap`."
  [options artifacts]
  (->> (pmap
         #(check-artifact! options %)
         artifacts)
       (filter identity)))

(defn collect-and-check-artifacts!
  "Collect and check all artifacts contained within the given data."
  [options data]
  (->> data
       (collect-artifacts options)
       (check-artifacts! options)))

fix artifact collection.

fix artifact collection.


(ns braid.ui.styles.header
  (:require [garden.units :refer [em px]]
            [garden.arithmetic :as m]
            [braid.ui.styles.vars :as vars]
            [braid.ui.styles.mixins :as mixins]))

(def header-height vars/avatar-size)

(defn header [pad]
  [:.app
   [:.main
    ["> .header"

     [:.right

      [".bar:hover + .options"
       ".options:hover"
       {:display "inline-block"}]

      [:.bar
       {:position "absolute"
        :right vars/pad
        :top vars/pad
        :z-index 100
        :background "black"
        :color "white"
        :height header-height
        :border-radius vars/border-radius
        :overflow "hidden"}
       (mixins/box-shadow)

       [:.user-info
        {:display "inline-block"
         :vertical-align "top"}

        [:&:hover
         :&.active
         {:background "rgba(0,0,0,0.25)"}]

        [:.name
         {:color "white"
          :padding [[0 (m/* vars/pad 0.75)]]
          :text-transform "uppercase"
          :letter-spacing "0.1em"
          :display "inline-block"
          :text-decoration "none"
          :vertical-align "top"
          :font-weight "bold"
          :line-height header-height
          :-webkit-font-smoothing "antialiased"}]

        [:.avatar
         {:height header-height
          :width header-height
          :background "white"}]]

       [:.more
        {:display "inline-block"
         :line-height header-height
         :vertical-align "top"
         :height header-height
         :width header-height
         :text-align "center"}

        [:&:after
         {:-webkit-font-smoothing "antialiased"}
         (mixins/fontawesome \uf078)]]]

      [:.options
       {:background "white"
        :padding [[(m/* vars/pad 0.75)]]
        :border-radius vars/border-radius
        :position "absolute"
        :top vars/pad
        :right vars/pad
        :margin-top header-height
        :z-index 110
        :display "none"}
       (mixins/box-shadow)

       ; little arrow above options box
       [:&:before
        {:position "absolute"
         :top "-0.65em"
         :right (m/* vars/pad 0.70)
         :color "white"
         :font-size "1.5em"}
         (mixins/fontawesome \uf0d8)]

        [:a
         {:display "block"
          :color "black"
          :text-align "right"
          :text-decoration "none"
          :line-height "1.85em"}

          [:&:hover
           {:color "#666"}]

          [:&:after
           {:margin-left "0.5em"}]

          [:&.subscriptions:after
           (mixins/fontawesome \uf02c)]

          [:&.invite-friend:after
           (mixins/fontawesome \uf1e0)]

          [:&.edit-profile:after
           (mixins/fontawesome \uf007)]

          [:&.settings:after
           (mixins/fontawesome \uf013)]]]]]

      [:.left
       {:position "absolute"
        :left vars/sidebar-width
        :top vars/pad
        :z-index 100
        :margin-left vars/pad
        :border-radius vars/border-radius
        :overflow "hidden"
        :height header-height
        :background "black"}
       (mixins/box-shadow)

       [:.group-name
        :a
        {:color "white"
         :display "inline-block"
         :vertical-align "top"
         :height header-height
         :line-height header-height
         :-webkit-font-smoothing "antialiased"}]

       [:.group-name
        {:text-transform "uppercase"
         :padding-right (m/* vars/pad 0.25)
         :padding-left (m/* vars/pad 0.75)
         :letter-spacing "0.1em"
         :font-weight "bold"}]

       [:a
        {:width header-height
         :text-align "center"
         :text-decoration "none"}

        [:&:hover
         :&.active
         {:background "rgba(0,0,0,0.25)"}]

        [:&.inbox:after
         (mixins/fontawesome \uf01c)]

        [:&.recent:after
         (mixins/fontawesome \uf1da)]]

       [:.search-bar
        {:display "inline-block"
         :position "relative"}

        [:input
         {:border 0
          :padding-left vars/pad
          :min-width "15em"
          :width "25vw"
          :height header-height
          :outline "none"}]

        [:.action
         [:&:after
          {:top 0
           :right (m/* vars/pad 0.75)
           :height header-height
           :line-height header-height
           :position "absolute"
           :cursor "pointer"}]

         [:&.search:after
          {:color "#ccc"
           :pointer-events "none"}
          (mixins/fontawesome \uf002)]

         [:&.clear:after
          (mixins/fontawesome \uf057)]]]]]])

(ns braid.ui.styles.header
  (:require [garden.units :refer [em px]]
            [garden.arithmetic :as m]
            [braid.ui.styles.vars :as vars]
            [braid.ui.styles.mixins :as mixins]))

(def header-height vars/avatar-size)

(defn header [pad]
  [:.app
   [:.main
    ["> .header"

     [:.right

      [".bar:hover + .options"
       ".options:hover"
       {:display "inline-block"}]

      [:.bar
       {:position "absolute"
        :right vars/pad
        :top vars/pad
        :z-index 100
        :background "black"
        :color "white"
        :height header-height
        :border-radius vars/border-radius
        :overflow "hidden"}
       (mixins/box-shadow)

       [:.user-info
        {:display "inline-block"
         :vertical-align "top"}

        [:&:hover
         :&.active
         {:background "rgba(0,0,0,0.25)"}]

        [:.name
         {:color "white"
          :padding [[0 (m/* vars/pad 0.75)]]
          :text-transform "uppercase"
          :letter-spacing "0.1em"
          :display "inline-block"
          :text-decoration "none"
          :vertical-align "top"
          :font-weight "bold"
          :line-height header-height
          :-webkit-font-smoothing "antialiased"}]

        [:.avatar
         {:height header-height
          :width header-height
          :background "white"}]]

       [:.more
        {:display "inline-block"
         :line-height header-height
         :vertical-align "top"
         :height header-height
         :width header-height
         :text-align "center"}

        [:&:after
         {:-webkit-font-smoothing "antialiased"}
         (mixins/fontawesome \uf078)]]]

      [:.options
       {:background "white"
        :padding [[(m/* vars/pad 0.75)]]
        :border-radius vars/border-radius
        :position "absolute"
        :top vars/pad
        :right vars/pad
        :margin-top header-height
        :z-index 110
        :display "none"}
       (mixins/box-shadow)

       ; little arrow above options box
       [:&:before
        {:position "absolute"
         :top "-0.65em"
         :right (m/* vars/pad 0.70)
         :color "white"
         :font-size "1.5em"}
         (mixins/fontawesome \uf0d8)]

        [:a
         {:display "block"
          :color "black"
          :text-align "right"
          :text-decoration "none"
          :line-height "1.85em"}

          [:&:hover
           {:color "#666"}]

          [:&:after
           {:margin-left "0.5em"}]

          [:&.subscriptions:after
           (mixins/fontawesome \uf02c)]

          [:&.invite-friend:after
           (mixins/fontawesome \uf1e0)]

          [:&.edit-profile:after
           (mixins/fontawesome \uf007)]

          [:&.settings:after
           (mixins/fontawesome \uf013)]]]]]

      [:.left
       {:position "absolute"
        :left vars/sidebar-width
        :top vars/pad
        :z-index 100
        :margin-left vars/pad
        :border-radius vars/border-radius
        :overflow "hidden"
        :height header-height
        :background "black"}
       (mixins/box-shadow)

       [:.group-name
        :a
        {:color "white"
         :display "inline-block"
         :vertical-align "top"
         :height header-height
         :line-height header-height
         :-webkit-font-smoothing "antialiased"}]

       [:.group-name
        {:text-transform "uppercase"
         :min-width (em 5)
         :padding [[0 (m/* vars/pad 0.75)]]
         :letter-spacing "0.1em"
         :font-weight "bold"}]

       [:a
        {:width header-height
         :text-align "center"
         :text-decoration "none"}

        [:&:hover
         :&.active
         {:background "rgba(0,0,0,0.25)"}]

        [:&.inbox:after
         (mixins/fontawesome \uf01c)]

        [:&.recent:after
         (mixins/fontawesome \uf1da)]]

       [:.search-bar
        {:display "inline-block"
         :position "relative"}

        [:input
         {:border 0
          :padding-left vars/pad
          :min-width "15em"
          :width "25vw"
          :height header-height
          :outline "none"}]

        [:.action
         [:&:after
          {:top 0
           :right (m/* vars/pad 0.75)
           :height header-height
           :line-height header-height
           :position "absolute"
           :cursor "pointer"}]

         [:&.search:after
          {:color "#ccc"
           :pointer-events "none"}
          (mixins/fontawesome \uf002)]

         [:&.clear:after
          (mixins/fontawesome \uf057)]]]]]])

Set a min-width on group-name in left-header


(ns catalog.routes.home
  (:require [catalog.layout :as layout]
            [catalog.middleware :as middleware]
            [catalog.web.views :as views]
            [clojure.java.io :as io]
            [clojure.spec.alpha :as s]
            [reitit.coercion.spec :as spec-coercion]
            [reitit.ring.middleware.multipart :as multipart]
            [reitit.ring.coercion :as coercion]
            [ring.util.codec :refer [url-encode]]))

(s/def ::year (s/and pos-int? #(<= 1900 % 2100)))
(s/def ::issue (s/and pos-int? #{1 2 3 4 5 6}))

(def hörbuch-encoded (url-encode "hörbuch"))

(defn home-routes []
  [ ""
   {:middleware [middleware/wrap-csrf
                 middleware/wrap-formats]}
   ["/" {:get {:handler (fn [_] (views/home))}}]
   ["/:year/:issue"
    {:coercion spec-coercion/coercion
     :middleware [coercion/coerce-request-middleware
                  multipart/multipart-middleware]}

    ["" {:get {:parameters {:path {:year ::year :issue ::issue}}
               :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                          (views/home year issue))}}]

    ;; Neu im Sortiment
    ["/neu-im-sortiment.pdf"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                       (views/neu-im-sortiment year issue))}}]

    ;; Neu in Grossdruck
    ["/neu-in-grossdruck.pdf"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                       (views/neu-in-grossdruck year issue))}}]

    ;; Neu in Braille
    ["/neu-in-braille.xml"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                       (views/neu-in-braille year issue))}}]

    ;; Neu als Hörbuch
    [(format "/neu-als-%s.pdf" hörbuch-encoded)
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                       (views/neu-als-hörbuch year issue))}}]

    [(format "/neu-als-%s.ncc" hörbuch-encoded)
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                       (views/neu-als-hörbuch-ncc year issue))}}]

    ;; Editorials
    ["/editorial"
     ["/grossdruck"
      {:get {:parameters {:path {:year ::year :issue ::issue}}
             :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                        (views/editorial-form r :grossdruck year issue))}}]
     ["/braille"
      {:get {:parameters {:path {:year ::year :issue ::issue}}
             :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                        (views/editorial-form r :braille year issue))}}]
     [(format "/%s" hörbuch-encoded)
      {:get {:parameters {:path {:year ::year :issue ::issue}}
             :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                        (views/editorial-form r :hörbuch year issue))}}]]

    ;; Upload catalog data
    ["/upload"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                       (views/upload-form r year issue))}}]
    ["/upload-full"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                       (views/upload-full-form r year issue))}}]

    ["/:fmt/upload-confirm"
     {:post {:parameters {:path {:year ::year :issue ::issue :fmt keyword?}
                          :multipart {:file any?}}
             :handler (fn [{{{:keys [year issue fmt]} :path
                             {:keys [file]} :multipart} :parameters :as r}]
                        (views/upload-confirm r year issue fmt file))}}]
    ["/:fmt/upload"
     {:post {:parameters {:path {:year ::year :issue ::issue :fmt keyword?}
                          :multipart {:items any?}}
             :handler (fn [{{{:keys [year issue fmt]} :path
                             {:keys [items]} :multipart} :parameters :as r}]
                        (views/upload r year issue fmt items))}}]

    ;; Full catalogs
    ["/full"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                       (views/full-catalogs r year issue))}}]

    ;; Custom catalogs
    ["/custom"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                       (views/custom-form r year issue))}
      :post {:parameters {:path {:year ::year :issue ::issue}
                          :multipart {:query string?
                                      :customer string?
                                      :fmt keyword?
                                      :items any?}}
             :handler (fn [{{{:keys [year issue]} :path
                             {:keys [query customer fmt items]} :multipart} :parameters :as r}]
                        (views/custom r year issue query customer fmt items))}}]
    ["/custom-confirm"
     {:post {:parameters {:path {:year ::year :issue ::issue}
                          :multipart {:query string?
                                      :customer string?
                                      :fmt keyword?
                                      :file any?}}
             :handler (fn [{{{:keys [year issue]} :path
                             {:keys [query customer fmt file]} :multipart} :parameters :as r}]
                        (views/custom-confirm r year issue query customer fmt file))}}]

    ]
   ["/:year/full"
    {:coercion spec-coercion/coercion
     :middleware [coercion/coerce-request-middleware]}
    [(format "/%s-in-der-sbs.pdf" (url-encode "hörfilme"))
     {:name ::hörfilme
      :get {:parameters {:path {:year ::year}}
            :handler (fn [{{{:keys [year]} :path} :parameters}]
                       (views/hörfilme year))}}]
    ["/spiele-in-der-sbs.pdf"
     {:name ::spiele
      :get {:parameters {:path {:year ::year}}
            :handler (fn [{{{:keys [year]} :path} :parameters}]
                       (views/spiele year))}}]
    [(format "/taktile-%s-der-sbs.pdf" (url-encode "kinderbücher"))
     {:name ::kinderbücher
      :get {:parameters {:path {:year ::year}}
            :handler (fn [{{{:keys [year]} :path} :parameters}]
                       (views/taktile-bücher year))}}]
    [(format "/print-und-braille-%s-in-der-sbs.pdf" (url-encode "bücher"))
     {:name ::print-and-braille-bücher
      :get {:parameters {:path {:year ::year}}
            :handler (fn [{{{:keys [year]} :path} :parameters}]
                       (views/print-and-braille-bücher year))}}]
    ]
   ])

(ns catalog.routes.home
  (:require [catalog.layout :as layout]
            [catalog.middleware :as middleware]
            [catalog.web.views :as views]
            [clojure.java.io :as io]
            [clojure.spec.alpha :as s]
            [reitit.coercion.spec :as spec-coercion]
            [reitit.ring.middleware.multipart :as multipart]
            [reitit.ring.coercion :as coercion]
            [ring.util.codec :refer [url-encode]]))

(s/def ::year (s/and pos-int? #(<= 1900 % 2100)))
(s/def ::issue (s/and pos-int? #{1 2 3 4 5 6}))
(s/def ::format (s/and string? #{"grossdruck" "braille" "hörbuch"}))

(def hörbuch-encoded (url-encode "hörbuch"))

(defn home-routes []
  [ ""
   {:middleware [middleware/wrap-csrf
                 middleware/wrap-formats]}
   ["/" {:get {:handler (fn [_] (views/home))}}]
   ["/:year/:issue"
    {:coercion spec-coercion/coercion
     :middleware [coercion/coerce-request-middleware
                  multipart/multipart-middleware]}

    ["" {:get {:parameters {:path {:year ::year :issue ::issue}}
               :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                          (views/home year issue))}}]

    ;; Neu im Sortiment
    ["/neu-im-sortiment.pdf"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                       (views/neu-im-sortiment year issue))}}]

    ;; Neu in Grossdruck
    ["/neu-in-grossdruck.pdf"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                       (views/neu-in-grossdruck year issue))}}]

    ;; Neu in Braille
    ["/neu-in-braille.xml"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                       (views/neu-in-braille year issue))}}]

    ;; Neu als Hörbuch
    [(format "/neu-als-%s.pdf" hörbuch-encoded)
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                       (views/neu-als-hörbuch year issue))}}]

    [(format "/neu-als-%s.ncc" hörbuch-encoded)
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters}]
                       (views/neu-als-hörbuch-ncc year issue))}}]

    ;; Editorials
    ["/editorial/:fmt"
      {:get {:parameters {:path {:year ::year :issue ::issue :fmt ::format}}
             :handler (fn [{{{:keys [year issue fmt]} :path} :parameters :as r}]
                        (views/editorial-form r fmt year issue))}}]

    ;; Upload catalog data
    ["/upload"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                       (views/upload-form r year issue))}}]
    ["/upload-full"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                       (views/upload-full-form r year issue))}}]

    ["/:fmt/upload-confirm"
     {:post {:parameters {:path {:year ::year :issue ::issue :fmt keyword?}
                          :multipart {:file any?}}
             :handler (fn [{{{:keys [year issue fmt]} :path
                             {:keys [file]} :multipart} :parameters :as r}]
                        (views/upload-confirm r year issue fmt file))}}]
    ["/:fmt/upload"
     {:post {:parameters {:path {:year ::year :issue ::issue :fmt keyword?}
                          :multipart {:items any?}}
             :handler (fn [{{{:keys [year issue fmt]} :path
                             {:keys [items]} :multipart} :parameters :as r}]
                        (views/upload r year issue fmt items))}}]

    ;; Full catalogs
    ["/full"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                       (views/full-catalogs r year issue))}}]

    ;; Custom catalogs
    ["/custom"
     {:get {:parameters {:path {:year ::year :issue ::issue}}
            :handler (fn [{{{:keys [year issue]} :path} :parameters :as r}]
                       (views/custom-form r year issue))}
      :post {:parameters {:path {:year ::year :issue ::issue}
                          :multipart {:query string?
                                      :customer string?
                                      :fmt keyword?
                                      :items any?}}
             :handler (fn [{{{:keys [year issue]} :path
                             {:keys [query customer fmt items]} :multipart} :parameters :as r}]
                        (views/custom r year issue query customer fmt items))}}]
    ["/custom-confirm"
     {:post {:parameters {:path {:year ::year :issue ::issue}
                          :multipart {:query string?
                                      :customer string?
                                      :fmt keyword?
                                      :file any?}}
             :handler (fn [{{{:keys [year issue]} :path
                             {:keys [query customer fmt file]} :multipart} :parameters :as r}]
                        (views/custom-confirm r year issue query customer fmt file))}}]

    ]
   ["/:year/full"
    {:coercion spec-coercion/coercion
     :middleware [coercion/coerce-request-middleware]}
    [(format "/%s-in-der-sbs.pdf" (url-encode "hörfilme"))
     {:name ::hörfilme
      :get {:parameters {:path {:year ::year}}
            :handler (fn [{{{:keys [year]} :path} :parameters}]
                       (views/hörfilme year))}}]
    ["/spiele-in-der-sbs.pdf"
     {:name ::spiele
      :get {:parameters {:path {:year ::year}}
            :handler (fn [{{{:keys [year]} :path} :parameters}]
                       (views/spiele year))}}]
    [(format "/taktile-%s-der-sbs.pdf" (url-encode "kinderbücher"))
     {:name ::kinderbücher
      :get {:parameters {:path {:year ::year}}
            :handler (fn [{{{:keys [year]} :path} :parameters}]
                       (views/taktile-bücher year))}}]
    [(format "/print-und-braille-%s-in-der-sbs.pdf" (url-encode "bücher"))
     {:name ::print-and-braille-bücher
      :get {:parameters {:path {:year ::year}}
            :handler (fn [{{{:keys [year]} :path} :parameters}]
                       (views/print-and-braille-bücher year))}}]
    ]
   ])

Simplify editorial routes

Simplify editorial routes


(ns comic-reader.scrape
  (:require [clojure.string :as s]
            [net.cgrand.enlive-html :as html])
  (:import java.net.URL))

(defn fetch-feed [url]
  (html/xml-resource (java.net.URL. url)))

(defn fetch-url [url]
  (html/html-resource (java.net.URL. url)))

(defn fetch-list [{:keys [url selector normalize]}]
  (when (every? (complement nil?) [url selector normalize])
    (map normalize (html/select (fetch-url url) selector))))

(defn enlive->hiccup [{:keys [tag attrs content]}]
  (if (nil? content)
    [tag attrs]
    [tag attrs content]))

(defn clean-image-tag [[tag attrs & content]]
  [tag (select-keys attrs [:alt :src])])

(defn extract-image-tag [html selector]
  (-> html
      (html/select selector)
      first
      enlive->hiccup
      clean-image-tag))

(defn fetch-image-tag [{:keys [url selector]}]
  (when (every? (complement nil?) [url selector])
    (-> (fetch-url url)
        extract-image-tag)))

(ns comic-reader.scrape
  (:require [clojure.string :as s]
            [net.cgrand.enlive-html :as html])
  (:import java.net.URL))

(defn fetch-url [url]
  (html/html-resource (java.net.URL. url)))

(defn fetch-list [{:keys [url selector normalize]}]
  (when (every? (complement nil?) [url selector normalize])
    (map normalize (html/select (fetch-url url) selector))))

(defn enlive->hiccup [{:keys [tag attrs content]}]
  (if (nil? content)
    [tag attrs]
    [tag attrs content]))

(defn clean-image-tag [[tag attrs & content]]
  [tag (select-keys attrs [:alt :src])])

(defn extract-image-tag [html selector]
  (-> html
      (html/select selector)
      first
      enlive->hiccup
      clean-image-tag))

(defn fetch-image-tag [{:keys [url selector]}]
  (when (every? (complement nil?) [url selector])
    (-> (fetch-url url)
        extract-image-tag)))

Remove unused fetch-feed function

Remove unused fetch-feed function


(ns quil.snippets.macro
  (:require [quil.util :as u]))

(defmacro defsnippet
  "Defines a snippet. A snippet is a small example showing how to use a specific
  Quil function. Snippets are used in Quil docs and for release testing.

  If a snippet is intended for a clj-only or cljs-only function, use reader conditionals
  (https://clojure.org/reference/reader#_reader_conditionals) to define the snippet only in
  clj or cljs. You can also use reader conditionals inside the snippet itself if needed.

  If the snippet is not trivial and has multiple parts it should have comments. These
  comments will help Quil users to understand better what the snippet does when they read
  the Quil API. Since standard clojure comments (the one that start with ;) are stripped in
  macros, comments should be added using (comment) macro like this:

  ```
  (comment \"Do foo bar\")
  (foo-bar 123)
  ```

  All `(comment)` forms will be converted into `;` comments when generating the
  documentation.

  Snippets are stored in https://github.com/quil/quil/tree/master/src/cljc/quil/snippets

  More info about snippets: https://github.com/quil/quil/wiki/Snippets

  This macro works by storing its parts in a list [[quil.snippets.all-snippets/all-snippets]]
  that is later used to generate tests for release testing or generate documentation.

  Parameters:
    * `name` - Name of snippet. Usually the same as the function
               the snippet tests. Needs to be unique in the current file.
    * `fns`  - Name of the Quil function that the snippet tests. Used to
               to find all snippets for each function when generating documentation.
               Supports a collection of names if snippet tests few related Quil
               functions at the same time.
    * `opts` - Map of extra options. Supported options:
      - `:renderer` - Renderer to use for the snippet, if not default.
      - `:setup`    - Setup function to use. Default is empty.
    * `body` - The body of the `draw` function of the snippet."
  [snip-name fns opts & body]
  (let [setup (:setup opts '())
        mouse-clicked (:mouse-clicked opts '())]
    `(swap! quil.snippets.all-snippets-internal/all-snippets
            conj
            {:name (name '~snip-name)
             :fns ~(if (string? fns) [fns] fns)
             :opts ~(dissoc opts :setup :mouse-clicked)
             :setup (fn [] ~setup)
             :setup-str ~(pr-str setup)
             :body (fn [] ~@body)
             :body-str ~(pr-str body)
             :mouse-clicked (fn [] ~mouse-clicked)
             :mouse-clicked-str ~(pr-str mouse-clicked)
             :ns ~(str (ns-name *ns*))})))

(ns quil.snippets.macro
  (:require [quil.util :as u]))

(defmacro defsnippet
  "Defines a snippet. A snippet is a small example showing how to use a specific
  Quil function. Snippets are used in Quil docs and for release testing.

  If a snippet is intended for a clj-only or cljs-only function, use
  [reader
  conditionals](https://clojure.org/reference/reader#_reader_conditionals)
  to define the snippet only in clj or cljs. You can also use reader
  conditionals inside the snippet itself if needed.

  If the snippet is not trivial and has multiple parts it should have
  comments. These comments will help Quil users to better understand what the
  snippet does when they read the Quil API. Since standard Clojure comments (the
  ones that start with `;`) are stripped in macros, comments should be added
  using the `(comment)` macro like this:

  ```
  (comment \"Do foo bar\")
  (foo-bar 123)
  ```

  All `(comment)` forms will be converted into `;` comments when generating the
  documentation.

  Snippets are stored in
  https://github.com/quil/quil/tree/master/src/cljc/quil/snippets

  More info about snippets: https://github.com/quil/quil/wiki/Snippets

  This macro works by storing its parts in a
  list [[quil.snippets.all-snippets/all-snippets]] that is later used to
  generate tests for release testing or generate documentation.

  Parameters:
    * `name` - Name of snippet. Usually the same as the function
               the snippet tests. Needs to be unique in the current file.
    * `fns`  - Name of the Quil function that the snippet tests. Used to
               to find all snippets for each function when generating
               documentation. Supports a collection of names if the
               snippet tests a few related Quil functions at the same time.
    * `opts` - Map of extra options. Supported options:
      - `:renderer` - Renderer to use for the snippet, if not default.
      - `:setup`    - Setup function to use. Default is empty.
    * `body` - The body of the `draw` function of the snippet."
  [snip-name fns opts & body]
  (let [setup (:setup opts '())
        mouse-clicked (:mouse-clicked opts '())]
    `(swap! quil.snippets.all-snippets-internal/all-snippets
            conj
            {:name (name '~snip-name)
             :fns ~(if (string? fns) [fns] fns)
             :opts ~(dissoc opts :setup :mouse-clicked)
             :setup (fn [] ~setup)
             :setup-str ~(pr-str setup)
             :body (fn [] ~@body)
             :body-str ~(pr-str body)
             :mouse-clicked (fn [] ~mouse-clicked)
             :mouse-clicked-str ~(pr-str mouse-clicked)
             :ns ~(str (ns-name *ns*))})))

Improve documentation in `quil.snippets.macro`.


(ns frontend.routes
  (:require [cljs.core.async :as async :refer [>! <! alts! chan sliding-buffer close!]]
            [clojure.string :as str]
            [frontend.async :refer [put!]]
            [frontend.utils :as utils :include-macros true]
            [goog.events :as events]
            [secretary.core :as secretary :include-macros true :refer-macros [defroute]])
  (:require-macros [cljs.core.async.macros :as am :refer [go go-loop alt!]]))

(defn define-spec-routes! [nav-ch]
  (defroute trailing-slash #"(.+)/$" [path]
    (put! nav-ch [:navigate! {:path path :replace-token? true}]))
  (defroute not-found "*" []
    (put! nav-ch [:error {:status 404}])))

(defn define-user-routes! [nav-ch]
  (defroute root "/" [{:keys [query-params]}]
    (put! nav-ch [:landing {:query-params query-params}]))
  (defroute home "/home" [{:keys [query-params]}]
    (put! nav-ch [:landing {:query-params query-params}]))
  (defroute pricing "/pricing" [{:keys [query-params]}]
    (put! nav-ch [:pricing {:query-params query-params}]))

  ;; TODO: remove these when backend deploys, only here for backwards compatibility
  (defroute early-access "/early-access" [{:keys [query-params]}]
    (put! nav-ch [:navigate! {:path "/trial/team"
                              :replace-token? true}]))
  (defroute early-access-type "/early-access/:type" [type {:keys [query-params]}]
    (put! nav-ch [:navigate! {:path (str "/trial/" type)
                              :replace-token? true}]))

  (defroute trial "/trial/:type" [type {:keys [query-params]}]
    (put! nav-ch [:trial {:query-params query-params
                          :trial-type type}]))

  (defroute new-doc "/new" [{:keys [query-params]}]
    (put! nav-ch [:new {:query-params query-params}]))

  (defroute document #"/document/(\d+)" [doc-id {:keys [query-params]}]
    (put! nav-ch [:document {:document/id (long doc-id)
                             :query-params query-params}])))

(defn define-routes! [state]
  (let [nav-ch (get-in @state [:comms :nav])]
    (define-user-routes! nav-ch)
    (define-spec-routes! nav-ch)))

(ns frontend.routes
  (:require [cljs.core.async :as async :refer [>! <! alts! chan sliding-buffer close!]]
            [clojure.string :as str]
            [frontend.async :refer [put!]]
            [frontend.utils :as utils :include-macros true]
            [goog.events :as events]
            [secretary.core :as secretary :include-macros true :refer-macros [defroute]])
  (:require-macros [cljs.core.async.macros :as am :refer [go go-loop alt!]]))

(defn define-spec-routes! [nav-ch]
  (defroute trailing-slash #"(.+)/$" [path]
    (put! nav-ch [:navigate! {:path path :replace-token? true}]))
  (defroute not-found "*" []
    (put! nav-ch [:error {:status 404}])))

(defn define-user-routes! [nav-ch]
  (defroute root "/" {:keys [query-params]}
    (put! nav-ch [:landing {:query-params query-params}]))

  (defroute home "/home" {:keys [query-params]}
    (put! nav-ch [:landing {:query-params query-params}]))

  (defroute pricing "/pricing" {:keys [query-params]}
    (put! nav-ch [:pricing {:query-params query-params}]))

  ;; TODO: remove these when backend deploys, only here for backwards compatibility
  (defroute early-access "/early-access" {:keys [query-params]}
    (put! nav-ch [:navigate! {:path "/trial/team"
                              :replace-token? true}]))

  (defroute early-access-type "/early-access/:type" {:keys [type query-params]}
    (put! nav-ch [:navigate! {:path (str "/trial/" type)
                              :replace-token? true}]))

  (defroute trial "/trial/:type" {:keys [type query-params]}
    (put! nav-ch [:trial {:query-params query-params
                          :trial-type type}]))

  (defroute new-doc "/new" {:keys [query-params]}
    (put! nav-ch [:new {:query-params query-params}]))

  (defroute document ["/document/:doc-id" :doc-id #"\d+"] {:keys [doc-id query-params]}
    (put! nav-ch [:document {:document/id (long doc-id)
                             :query-params query-params}])))

(defn define-routes! [state]
  (let [nav-ch (get-in @state [:comms :nav])]
    (define-user-routes! nav-ch)
    (define-spec-routes! nav-ch)))

fix how args are destructured in routes

fix how args are destructured in routes