-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconquer.bb
More file actions
executable file
·160 lines (131 loc) · 4.78 KB
/
Copy pathconquer.bb
File metadata and controls
executable file
·160 lines (131 loc) · 4.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env bb
;; -*- mode: clojure -*-
(ns conquer
"A command line utility to generate text files from (comb) templates.
In the computer game Master of Orion II before terraforming a
planet, you had to conquer it."
(:require [shell-smith.core :as smith]
[clojure.data.csv :as csv]
[clojure.java.io :as io]
[babashka.fs :as fs]
[clj-yaml.core :as yaml]
[clojure.string :as str]
[clojure.walk :as walk]
[comb.template :as comb]
[cheshire.core :as json]))
(def ^:dynamic *config*)
(def usage "
conquer
Usage:
conquer [--template=<template>] [--update] [--path=<path>]
conquer -h | --help
conquer -v | --version
Options:
-h --help Show this screen.
-p --path=<path> Path to operate on (file or directory) [default: .]
-t --template=<template> Override the template file
-u --update Creates or updates files.
-v --version Show version.
----
conquer is a command line utility to generate text files from (comb)
templates. Takes a yml or csv files or as input. For all input files
apply the therein mentioned templates or the template given via
config. Each input file can hold a single entry or a list of entries
to be processed. The resulting file will have its basename from the
input file and its extension from the template (the first template in
case of a list).")
(defn stubborn-eval* [template bindings]
(fn []
(try
(binding [*ns* (create-ns 'conquer)]
(comb/eval template bindings)
(catch clojure.lang.ExceptionInfo e
(if-let [[_ missing] (re-matches #"Could not resolve symbol: ([\w-]+)" (ex-message e))]
;; in case it is just a missing var, add missing var to
;; bindings and re-wrap in a function for next trampoline
;; iteration
(stubborn-eval* template (assoc bindings (keyword missing) ""))
;; re-throw instead
(throw e)))))))
(defn stubborn-eval [template bindings]
(trampoline (stubborn-eval* template bindings)))
(defn conquer-entry [path {:keys [template] :as entry}]
(let [filename (or
;; if it comes from the config it is relative to cwd
(:template *config*)
;; if it comes from the template it is relative to the template
(str path "/../" template))]
(-> filename
fs/canonicalize
str
slurp
(stubborn-eval entry))))
(defn update-file [path content]
(when-not (and (fs/regular-file? path)
(= (slurp path) content))
(spit path content)
(println "Updated" path)))
(defmulti read-file (fn [file] (-> file (str/split #"\.") last keyword)))
(defmethod read-file :yml [file]
(-> file slurp yaml/parse-string))
(defmethod read-file :csv [file]
(with-open [reader (io/reader file)]
(let [[headers & rows] (csv/read-csv reader)
headers (map keyword headers)]
(mapv (partial zipmap headers) rows))))
(defn- ext
([path] (ext path 0))
([path n]
(-> path (str/split #"\.") reverse (nth n))))
(defn conquer-file [file]
(let [entries (-> file read-file vector flatten)
iext (ext file)
oext (-> (:template *config*)
(or (-> entries first :template))
(ext 1))
content (->> entries
(map (partial conquer-entry file))
(str/join "\n"))]
(if (:update *config*)
(update-file (str/replace file (re-pattern (str iext "$")) oext) content)
(println content))))
(defn conquer-files [files]
(run! conquer-file files))
(defn conquer-directory [file-or-directory]
(as-> file-or-directory %
(fs/glob % "**.{yml,yaml,csv}")
(map str %)
(conquer-files %)))
;; --------------------------------------------------------------------------------
;; shorthands for usage in templates
(def json json/generate-string)
(def prn-str-trim (comp str/trim prn-str))
(defn v [x]
(cond
(nil? x)
"nil"
(string? x)
(or (prn-str-trim (not-empty x)) "empty")
(sequential? x)
(str "[" (str/join ", " (map prn-str-trim x)) "]")
:else
(prn-str-trim x)))
;; --------------------------------------------------------------------------------
(defn -main [& args]
(binding [*config* (smith/config usage)]
(let [{:keys [path]} *config*]
(cond
(:help *config*)
(println usage)
(:version *config*)
(println "conquer 0.1.2 (OMEGALUL)")
:else
(cond
(nil? path)
(conquer-directory ".")
(fs/directory? path)
(conquer-directory path)
(fs/regular-file? path)
(conquer-files [path]))))))
(when (= *file* (System/getProperty "babashka.file"))
(apply -main *command-line-args*))