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))))
|
||||
3
scripts/sydnix-cli/src/sydnix_cli/cli_table.clj
Normal file
3
scripts/sydnix-cli/src/sydnix_cli/cli_table.clj
Normal file
@@ -0,0 +1,3 @@
|
||||
(ns sydnix-cli.cli-table)
|
||||
|
||||
(def *cli-table (atom nil))
|
||||
151
scripts/sydnix-cli/src/sydnix_cli/commands/deps-lock.json
Normal file
151
scripts/sydnix-cli/src/sydnix_cli/commands/deps-lock.json
Normal file
@@ -0,0 +1,151 @@
|
||||
{
|
||||
"lock-version": 4,
|
||||
"git-deps": [],
|
||||
"mvn-deps": [
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.10.3/clojure-1.10.3.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-fxJHLa7Y9rUXSYqqKrE6ViR1w+31FHjkWBzHYemJeaM="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.10.3/clojure-1.10.3.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-GJwAxDNAdJai+7DsyzeQjJSVXZHq0b5IFWdE7MGBbZQ="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.11.0/clojure-1.11.0.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-PiH6daB+yd278bK1A1bPGAcQ0DmN6qT0TpHNYwRVWUc="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.11.0/clojure-1.11.0.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-SQjMS0yeYsmoFJb5PLWsb2lBd8xkXc87jOXkkavOHro="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.11.1/clojure-1.11.1.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-I4G26UI6tGUVFFWUSQPROlYkPWAGuRlK/Bv0+HEMtN4="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.11.1/clojure-1.11.1.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-IMRaGr7b2L4grvk2BQrjGgjBZ0CzL4dAuIOM3pb/y4o="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.11.2/clojure-1.11.2.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-iPqZkT1pIs+39kn1xGdQOHfLb8yMwW02948mSAhLqZc="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.11.2/clojure-1.11.2.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-FzbP/xCV4dT+/raogrut9ttB7+MV8pbw/aMtt//EExE="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.11.3/clojure-1.11.3.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-nDBUCTKOK5boXdK160t1gQxnt2unCuTQ9t3pvPtVsbc="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.11.3/clojure-1.11.3.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-DA2+Ge4NKpxXMQzr3dNWRD8NFlFMQmBHsGLjpXwNuK0="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.11.4/clojure-1.11.4.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-/H/xtmENDjSUp1zBHvgYEL2kAqwVcBL+TjuJlYbPQTM="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.11.4/clojure-1.11.4.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-a6YADmhI+Cw5y5tJqyqmo6Vi9MJNUrMeUZCuZJXwwwk="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.12.0/clojure-1.12.0.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-xFMzAGRBoFnqn9sTQfxsH0C5IaENzNgmZTEeSKA4R2M="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/clojure/1.12.0/clojure-1.12.0.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-KfRiqonLl2RXWEGKXwjUwagrc1yW569JgX0WqpuQgVA="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/core.specs.alpha/0.2.56/core.specs.alpha-0.2.56.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-/PRCveArBKhj8vzFjuaiowxM8Mlw99q4VjTwq3ERZrY="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/core.specs.alpha/0.2.56/core.specs.alpha-0.2.56.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-AarxdIP/HHSCySoHKV1+e8bjszIt9EsptXONAg/wB0A="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/core.specs.alpha/0.2.62/core.specs.alpha-0.2.62.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-Bu6owHC75FwVhWfkQ0OWgbyMRukSNBT4G/oyukLWy8g="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/core.specs.alpha/0.2.62/core.specs.alpha-0.2.62.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-F3i70Ti9GFkLgFS+nZGdG+toCfhbduXGKFtn1Ad9MA4="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/core.specs.alpha/0.4.74/core.specs.alpha-0.4.74.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-63OsCM9JuoQMiLpnvu8RM2ylVDM9lAiAjXiUbg/rnds="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/core.specs.alpha/0.4.74/core.specs.alpha-0.4.74.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-M0EOuKpz1S2Vez3G4KZfOZisBiPL2BPZDDPm5onEJCk="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/pom.contrib/0.3.0/pom.contrib-0.3.0.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-fxgrOypUPgV0YL+T/8XpzvasUn3xoTdqfZki6+ee8Rk="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/pom.contrib/1.1.0/pom.contrib-1.1.0.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-EOzku1+YKQENwWVh9C67g7ry9HYFtR+RBbkvPKoIlxU="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/pom.contrib/1.2.0/pom.contrib-1.2.0.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-CRbXpBVYuVAKQnyIb6KYJ6zlJZIGvjrTPmTilvwaYRE="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/spec.alpha/0.2.194/spec.alpha-0.2.194.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-z2iZ+YUpjGSxPqEplGrZAo3uja3w6rmuGORVAn04JJw="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/spec.alpha/0.2.194/spec.alpha-0.2.194.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-WhHw4eizwFLmUcSYxpRbRNs1Nb8sGHGf3PZd8fiLE+Y="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/spec.alpha/0.3.218/spec.alpha-0.3.218.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-Z+yJjrVcZqlXpVJ53YXRN2u5lL2HZosrDeHrO5foquA="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/spec.alpha/0.3.218/spec.alpha-0.3.218.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-bY3hTDrIdXYMX/kJVi/5hzB3AxxquTnxyxOeFp/pB1g="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/spec.alpha/0.5.238/spec.alpha-0.5.238.jar",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-lM2ZtupjlkHzevSGCmQ7btOZ7lqL5dcXz/C2Y8jXUHc="
|
||||
},
|
||||
{
|
||||
"mvn-path": "org/clojure/spec.alpha/0.5.238/spec.alpha-0.5.238.pom",
|
||||
"mvn-repo": "https://repo.maven.apache.org/maven2/",
|
||||
"hash": "sha256-PLp+DcwIXEzpLd3/6iJhJP+sF4vnm9A3m1suMKlpy+o="
|
||||
}
|
||||
]
|
||||
}
|
||||
43
scripts/sydnix-cli/src/sydnix_cli/commands/help.clj
Normal file
43
scripts/sydnix-cli/src/sydnix_cli/commands/help.clj
Normal file
@@ -0,0 +1,43 @@
|
||||
(ns sydnix-cli.commands.help
|
||||
(:require
|
||||
[babashka.process :as p]
|
||||
[sydnix-cli.mangen :as mangen]))
|
||||
|
||||
(defn adorn-with-help-option [wrapped-command-fn]
|
||||
(fn [{:keys [opts dispatch]}]
|
||||
(if (:help opts)
|
||||
(mangen/with-pipe
|
||||
(fn [man->]
|
||||
(mangen/write-man-for-command (mangen/find-dispatched dispatch)
|
||||
:out man->))
|
||||
(fn [->man]
|
||||
(p/shell {:in ->man}
|
||||
"man -l -")))
|
||||
(wrapped-command-fn opts))))
|
||||
|
||||
(defn- view-man-for-command [command-spec]
|
||||
(mangen/with-pipe
|
||||
(fn [man->]
|
||||
(mangen/write-man-for-command command-spec :out man->))
|
||||
(fn [->man]
|
||||
(p/shell {:in ->man} "man -l -"))))
|
||||
|
||||
(defn- wrap-command-fn [wrapped-fn]
|
||||
(fn [info]
|
||||
(if (:help (:opts info))
|
||||
(do (mangen/render-docs-for-command
|
||||
(mangen/find-dispatched (:dispatch info)))
|
||||
#_
|
||||
(view-man-for-command (mangen/find-dispatched (:dispatch info))))
|
||||
(when-not (nil? wrapped-fn)
|
||||
(wrapped-fn info)))))
|
||||
|
||||
(defn adorn-with-help-option* [command-spec]
|
||||
(update command-spec :fn wrap-command-fn))
|
||||
|
||||
(defn- command-fn [_opts]
|
||||
(view-man-for-command (mangen/find-dispatched [])))
|
||||
|
||||
(def command
|
||||
{:cmds ["help"]
|
||||
:fn command-fn})
|
||||
25
scripts/sydnix-cli/src/sydnix_cli/commands/rebuild.clj
Normal file
25
scripts/sydnix-cli/src/sydnix_cli/commands/rebuild.clj
Normal file
@@ -0,0 +1,25 @@
|
||||
(ns sydnix-cli.commands.rebuild
|
||||
(:require
|
||||
[clojure.java.shell :refer [sh]]
|
||||
[sydnix-cli.commands.help :refer [adorn-with-help-option*]]))
|
||||
|
||||
(def command-options-spec
|
||||
{:flake {:coerce :string
|
||||
:ref "URI"
|
||||
:desc "URI is a path to a Flake, which is passed to `nixos-rebuild
|
||||
--flake URI` as is."
|
||||
:default "path:///persist/dots"}})
|
||||
|
||||
(defn- command-fn [{:keys [args opts]}]
|
||||
(let [rebuild-cmd
|
||||
(concat ["sudo" "nixos-rebuild"]
|
||||
(or args ["switch"])
|
||||
["--flake" (:flake opts)])]
|
||||
(apply println "$" rebuild-cmd)
|
||||
(apply sh rebuild-cmd)))
|
||||
|
||||
(def commands
|
||||
[{:cmds ["rebuild"]
|
||||
:desc "Rebuild the system NixOS and Home-manager configuration"
|
||||
:fn command-fn
|
||||
:spec command-options-spec}])
|
||||
11
scripts/sydnix-cli/src/sydnix_cli/commands/status.clj
Normal file
11
scripts/sydnix-cli/src/sydnix_cli/commands/status.clj
Normal file
@@ -0,0 +1,11 @@
|
||||
(ns sydnix-cli.commands.status
|
||||
(:require
|
||||
[sydnix-cli.commands.help :refer [adorn-with-help-option*]]))
|
||||
|
||||
(defn- command-fn [opts]
|
||||
(prn opts))
|
||||
|
||||
(def commands
|
||||
[{:cmds ["status"]
|
||||
:desc "View system info"
|
||||
:fn command-fn}])
|
||||
8
scripts/sydnix-cli/src/sydnix_cli/commands/util.clj
Normal file
8
scripts/sydnix-cli/src/sydnix_cli/commands/util.clj
Normal file
@@ -0,0 +1,8 @@
|
||||
(ns sydnix-cli.commands.util
|
||||
(:require
|
||||
[sydnix-cli.commands.util.mangen :as cmd-util-mangen]))
|
||||
|
||||
(def commands
|
||||
[{:cmds ["util"]
|
||||
:desc "Infrequently-used utilities such as docgen and shell completions."}
|
||||
cmd-util-mangen/command])
|
||||
46
scripts/sydnix-cli/src/sydnix_cli/commands/util/mangen.clj
Normal file
46
scripts/sydnix-cli/src/sydnix_cli/commands/util/mangen.clj
Normal file
@@ -0,0 +1,46 @@
|
||||
(ns sydnix-cli.commands.util.mangen
|
||||
(:require
|
||||
[asciidoc.render]
|
||||
[babashka.fs :as fs]
|
||||
[babashka.process :as p]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.string :as str]
|
||||
[sydnix-cli.mangen :as mangen]
|
||||
[sydnix-cli.cli-table :refer [*cli-table]]
|
||||
[sydnix-cli.commands.help :refer [adorn-with-help-option*]]
|
||||
[sydnix-cli.prelude :as prelude]))
|
||||
|
||||
(defn command-fn [{:keys [yes output-directory]}]
|
||||
(if (or yes
|
||||
(prelude/y-or-n? (format "Write a bunch of man pages to %s?"
|
||||
output-directory)))
|
||||
(doseq [cmd-spec (deref *cli-table)]
|
||||
(let [man-file-name (fs/file
|
||||
output-directory
|
||||
(str (str/join "-" (cons "sydnix" (:cmds cmd-spec)))
|
||||
".1"))]
|
||||
(with-open [man-file (io/writer man-file-name)]
|
||||
(mangen/write-man-for-command cmd-spec :out man-file))
|
||||
(printf "wrote %s\n" man-file-name)
|
||||
(flush)))
|
||||
(System/exit 1)))
|
||||
|
||||
(def command-options-spec
|
||||
{:output-directory {:coerce :string
|
||||
:ref "DIRECTORY"
|
||||
:alias :o
|
||||
:desc "Directory to which the generated man pages will be
|
||||
written."
|
||||
:require true
|
||||
:validate fs/directory?}
|
||||
:yes {:coerce :bool
|
||||
:desc "Don't ask for confirmation."
|
||||
:default false}})
|
||||
|
||||
(def command
|
||||
(adorn-with-help-option*
|
||||
{:cmds ["util" "mangen"]
|
||||
:desc "Generate man pages for each subcommand of `sydnix`."
|
||||
:fn command-fn
|
||||
:spec command-options-spec}))
|
||||
25
scripts/sydnix-cli/src/sydnix_cli/main.clj
Executable file
25
scripts/sydnix-cli/src/sydnix_cli/main.clj
Executable file
@@ -0,0 +1,25 @@
|
||||
(ns sydnix-cli.main
|
||||
(:require
|
||||
[babashka.cli :as cli]
|
||||
[sydnix-cli.cli-table :refer [*cli-table]]
|
||||
[sydnix-cli.commands.help :as cmd-help :refer [adorn-with-help-option*]]
|
||||
[sydnix-cli.commands.rebuild :as cmd-rebuild]
|
||||
[sydnix-cli.commands.status :as cmd-status]
|
||||
[sydnix-cli.commands.util :as cmd-util])
|
||||
(:gen-class))
|
||||
|
||||
(def real-cli-table
|
||||
(map adorn-with-help-option*
|
||||
(concat cmd-status/commands
|
||||
cmd-rebuild/commands
|
||||
cmd-util/commands
|
||||
[cmd-help/command
|
||||
;; Show help when no other command matches.
|
||||
(assoc cmd-help/command :cmds [])])))
|
||||
|
||||
(defn -main [& args]
|
||||
(reset! *cli-table real-cli-table)
|
||||
(cli/dispatch @*cli-table args)
|
||||
;; Process may hang without this form. D:{
|
||||
;; https://github.com/babashka/process?tab=readme-ov-file#script-termination
|
||||
(shutdown-agents))
|
||||
94
scripts/sydnix-cli/src/sydnix_cli/mangen.clj
Normal file
94
scripts/sydnix-cli/src/sydnix_cli/mangen.clj
Normal file
@@ -0,0 +1,94 @@
|
||||
(ns sydnix-cli.mangen
|
||||
(:require
|
||||
[asciidoc.render]
|
||||
[asciidoc.types]
|
||||
[babashka.fs :as fs]
|
||||
[babashka.process :as p]
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.string :as str]
|
||||
[sydnix-cli.cli-table :refer [*cli-table]])
|
||||
(:import
|
||||
[java.io BufferedReader PipedReader PipedWriter]))
|
||||
|
||||
(defn- format-p [s]
|
||||
(-> s
|
||||
(str/replace #"^ +" " ")
|
||||
(str/replace #"\n +" "\n")))
|
||||
|
||||
(defn- docs-for-command [command-spec]
|
||||
(let [command (cons "sydnix" (:cmds command-spec))]
|
||||
[:document {:title (str (str/join "-" command)
|
||||
"(1)")
|
||||
:author "Madeleine Sydney Ślaga"
|
||||
:a {:doctype "manpage"
|
||||
:manmanual "SYDNIX"
|
||||
:mansource "SYDNIX"}}
|
||||
[:section {:title "Name"}
|
||||
[:p (format "%s - %s"
|
||||
(str/join " " command)
|
||||
(:desc command-spec))]]
|
||||
[:section {:title "Synopsis"}
|
||||
[:p (format "%s [_option_…]"
|
||||
(str/join " " command))]]
|
||||
(concat [:section {:title "Options"}]
|
||||
(for [[opt opt-spec] (:spec command-spec)]
|
||||
[:description {:described (format "*--%s%s*"
|
||||
(name opt)
|
||||
(if-let [ref (:ref opt-spec)]
|
||||
(str "=" ref)
|
||||
""))}
|
||||
[:p (format-p (:desc opt-spec))]]))]))
|
||||
|
||||
(defn render-docs-for-command [command-spec]
|
||||
(try (asciidoc.render/render (docs-for-command command-spec))
|
||||
(catch Exception e
|
||||
(prn e)
|
||||
(s/explain :asciidoc.types/document command-spec)
|
||||
(throw e))))
|
||||
|
||||
(defn- find-satisfying [p xs]
|
||||
(->> xs (filter p) first))
|
||||
|
||||
(defn find-dispatched [dispatch]
|
||||
(find-satisfying #(= dispatch (:cmds %)) (deref *cli-table)))
|
||||
|
||||
(defn- find-adoc-processor
|
||||
"Looks for a suitable AsciiDoc processor. In order of precedence, it will try
|
||||
`$ADOC_PROCESSOR`, then `asciidoctor`."
|
||||
[& {:keys [throw?]}]
|
||||
(letfn [(suitable [x]
|
||||
(when (and x (fs/executable? x))
|
||||
x))]
|
||||
(or (suitable (System/getenv "ADOC_PROCESSOR"))
|
||||
(suitable (fs/which "asciidoctor"))
|
||||
(when throw?
|
||||
(throw (ex-info "Can't find a suitable AsciiDoc processor" {}))))))
|
||||
|
||||
(defn asciidoctor
|
||||
"Shell out to AsciiDoctor (`$ADOC_PROCESSOR`, actually) to create ROFF markup
|
||||
for man pages."
|
||||
[& {:keys [asciidoctor-bin in out err]
|
||||
:or {asciidoctor-bin (find-adoc-processor :throw? true)
|
||||
in *in*
|
||||
out *out*
|
||||
err *err*}}]
|
||||
(p/shell {:in in :out out :err err}
|
||||
asciidoctor-bin "-o" "-" "-b" "manpage" "-"))
|
||||
|
||||
(defn with-pipe [with-out with-in]
|
||||
(let [out (PipedWriter.)
|
||||
in (PipedReader. out)]
|
||||
(future (with-out out)
|
||||
(.close out))
|
||||
(with-in (BufferedReader. in))))
|
||||
|
||||
(defn write-man-for-command
|
||||
[command-spec & {:keys [out err]
|
||||
:or {out *out*
|
||||
err *err*}}]
|
||||
(with-pipe
|
||||
(fn [adoc->]
|
||||
(binding [*out* adoc->]
|
||||
(render-docs-for-command command-spec)))
|
||||
(fn [->adoc]
|
||||
(asciidoctor :in ->adoc :out out :err err))))
|
||||
21
scripts/sydnix-cli/src/sydnix_cli/prelude.clj
Normal file
21
scripts/sydnix-cli/src/sydnix_cli/prelude.clj
Normal file
@@ -0,0 +1,21 @@
|
||||
(ns sydnix-cli.prelude
|
||||
(:require
|
||||
[clojure.core.match :refer [match]]))
|
||||
|
||||
(defn y-or-n?
|
||||
[prompt
|
||||
& {:keys [default]
|
||||
:or {default :n}
|
||||
:as opts}]
|
||||
(let [y-n-indicator (case default
|
||||
:y " [Y/n] "
|
||||
:n " [y/N] ")]
|
||||
(print (str prompt y-n-indicator))
|
||||
(flush))
|
||||
(let [line (read-line)]
|
||||
(match line
|
||||
"" (case default :y true :n false)
|
||||
(:or "y" "Y") true
|
||||
(:or "n" "N") false
|
||||
_ (do (println "Please enter y or n.")
|
||||
(recur prompt opts)))))
|
||||
Reference in New Issue
Block a user