refactor: Rename sydnix → sydnix-cli
This commit is contained in:
89
scripts/sydnix-cli/src/asciidoc/render.clj
Normal file
89
scripts/sydnix-cli/src/asciidoc/render.clj
Normal file
@@ -0,0 +1,89 @@
|
||||
(ns asciidoc.render
|
||||
(:require
|
||||
[clojure.pprint :refer [pprint]]
|
||||
[asciidoc.types :as types]
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.zip :as zip]))
|
||||
|
||||
(defn- block-zip [root-block]
|
||||
(zip/zipper #(s/conform ::block %)
|
||||
:content
|
||||
(fn [block children] (assoc block :content children))
|
||||
root-block))
|
||||
|
||||
(defn- escape-line [line]
|
||||
;; TODO
|
||||
line)
|
||||
|
||||
(declare render*)
|
||||
|
||||
(defn- render-attributes [attributes]
|
||||
(let [sorted-attributes (->> attributes
|
||||
seq
|
||||
(sort-by first))]
|
||||
(doseq [[k v] sorted-attributes]
|
||||
(printf ":%s: %s\n" (name k) v))))
|
||||
|
||||
(defn- render-document-header [depth {:keys [arguments] :as block}]
|
||||
(println "=" (:title arguments))
|
||||
(when-let [author (:author arguments)]
|
||||
(println author)
|
||||
(when-let [version (:version arguments)]
|
||||
(println version)))
|
||||
(when-let [attributes (:a arguments)]
|
||||
(render-attributes attributes))
|
||||
(println)
|
||||
(run! #(render* (inc depth) %) (:content block)))
|
||||
|
||||
(defn- render-p [block]
|
||||
(apply println (:content block)))
|
||||
|
||||
(defn- render-description [depth {{:keys [described]} :arguments :as block}]
|
||||
(printf "%s::\n" described)
|
||||
(run! #(render* (inc depth) %) (:content block)))
|
||||
|
||||
(defn- render-section [depth {:keys [arguments] :as block}]
|
||||
(print (apply str (repeat (inc depth) \=))
|
||||
(:title arguments)
|
||||
"\n\n")
|
||||
(run! #(render* (inc depth) %) (:content block))
|
||||
(println))
|
||||
|
||||
(defn- render* [depth block]
|
||||
(case (:context block)
|
||||
:document (do (assert (zero? depth)
|
||||
"Document block should only occur as root node.")
|
||||
(render-document-header depth block))
|
||||
:section (render-section depth block)
|
||||
:p (render-p block)
|
||||
:description (render-description depth block)
|
||||
:<> (run! #(render* (inc depth) %) (apply concat (:content block)))
|
||||
(throw (ex-info "no case" {:for (:context block)
|
||||
:block block}))))
|
||||
|
||||
(def my-manpage
|
||||
[:document {:title "sydnix(1)"
|
||||
:author "Madeleine Sydney Ślaga"
|
||||
:a {:doctype "manpage"
|
||||
:manmanual "SYDNIX"
|
||||
:mansource "SYDNIX"}}
|
||||
[:section {:title "Name"}
|
||||
[:p "sydnix - Inspect and operate upon the system."]]
|
||||
[:section {:title "Synopsis"}
|
||||
[:p "*sydnix* [_COMMAND_]... [_OPTION_]... [_FILE_]..."]]
|
||||
[:section {:title "Options"}
|
||||
[:description {:described "*--flake=URI*"}
|
||||
"UJri is a flake"]]])
|
||||
|
||||
(defn conform! [spec x]
|
||||
(let [x' (s/conform spec x)]
|
||||
(if (= x' :clojure.spec.alpha/invalid)
|
||||
(throw (ex-info "invalid" (s/explain-data spec x)))
|
||||
x')))
|
||||
|
||||
;; Currently unrelentingly recursive. We'll fix this if/when it bnecomes a
|
||||
;; problem. Shame Clojure skimps out on general TCO. }:(
|
||||
(defn render
|
||||
"Render an AsciiDoc block to `*out*`."
|
||||
[block]
|
||||
(render* 0 (conform! :asciidoc.types/document block)))
|
||||
57
scripts/sydnix-cli/src/asciidoc/types.clj
Normal file
57
scripts/sydnix-cli/src/asciidoc/types.clj
Normal file
@@ -0,0 +1,57 @@
|
||||
(ns asciidoc.types
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[spec-dict.main :refer [->opt dict]]))
|
||||
|
||||
(defn- make-block-arguments [req opt]
|
||||
(let [args (dict req
|
||||
(->opt opt)
|
||||
^:opt {:a map?})]
|
||||
(if (empty? req)
|
||||
(s/? (s/and map? args))
|
||||
(s/& (s/? map?) args))))
|
||||
|
||||
(defn- make-block
|
||||
[& {:keys [name required-args optional-args content-spec]
|
||||
:or {required-args {}
|
||||
optional-args {}
|
||||
content-spec (s/* ::block)}}]
|
||||
(s/and seqable?
|
||||
(s/cat :context (s/and keyword? #{name})
|
||||
:arguments (make-block-arguments required-args optional-args)
|
||||
:content content-spec)
|
||||
;; Set arguments to {} if none were provided.
|
||||
(s/conformer #(if (contains? % :arguments)
|
||||
%
|
||||
(assoc % :arguments {})))))
|
||||
|
||||
(s/def ::document
|
||||
(make-block :name :document
|
||||
:required-args {:title string?}
|
||||
:optional-args {:author string?
|
||||
:version string?}))
|
||||
|
||||
(s/def ::p
|
||||
(make-block :name :p
|
||||
:content-spec (s/* string?)))
|
||||
|
||||
(s/def ::description
|
||||
(make-block :name :description
|
||||
:required-opts {:described string?}))
|
||||
|
||||
(s/def ::section
|
||||
(make-block :name :section
|
||||
:required-args {:title string?}))
|
||||
|
||||
(s/def ::<>
|
||||
(make-block :name :<>
|
||||
:content (s/coll-of ::block)))
|
||||
|
||||
(s/def ::block
|
||||
(s/and (s/or :document ::document
|
||||
:section ::section
|
||||
:description ::description
|
||||
:<> ::<>
|
||||
:p ::p)
|
||||
;; `s/or` provides tagging that `make-block` already does.
|
||||
(s/conformer #(nth % 1))))
|
||||
Reference in New Issue
Block a user