-
Notifications
You must be signed in to change notification settings - Fork 0
/
core.clj
119 lines (109 loc) 路 4.26 KB
/
core.clj
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
(ns epsilon.core
(:gen-class)
(:require [puget.printer :refer [pprint]]
[clojure.tools.cli :as cli]
[clojure.string :as string]
[epsilon.generator :refer [generate-all validate-all]]
[me.raynes.fs :as fs]
[taoensso.timbre :as log]))
(def cli-options
[["-d" "--dir DIR" "Template directory. Can be relative or absolute."
:id :template-dir
:validate [#(and (fs/exists? %) (fs/directory? %)) "Directory must be valid."]]
["-m" "--model MODEL" "Path to XML model to use. Can be relative or absolute."
:id :model-paths
:default []
:validate [#(and (fs/exists? %) (fs/file? %)) "Model must be valid."]
:assoc-fn (fn [opts opt v] (update opts opt conj v))]
["-o" "--output DIR" "Where to output the templates. Can be relative or absolute."
:id :output-path]
["-v" nil "Verbosity level; may be specified up to 2 times. Levels: INFO -> DEBUG -> TRACE"
;; If no long-option is specified, an option :id must be given
:id :min-level
:default 0
:update-fn inc
:validate [#(<= % 2) "Verbosity level must not exceed 2."]
:post-validation true]
["-w" "--watch" "Watch the given template directory"
:id :watch?
:default false]
["-h" "--help" "Display this message"]])
(defn usage [options-summary]
(->> ["Usage: program-name [options] action"
""
"Options:"
options-summary
""
"Actions:"
" generate Generate everything inside the provided template directory. This will validate first."
" validate Validate everything inside the provided template directory"
""
"Please refer to the manual page for more information."]
(string/join \newline)))
(defn error-msg [errors]
(str "The following errors occurred while parsing your command:\n\n"
(string/join \newline errors)))
(defn validate-args
"Validate command line arguments. Either return a map indicating the program
should exit (with a error message, and optional ok status), or a map
indicating the action the program should take and the options provided."
[args]
(let [{:keys [options arguments errors summary]} (cli/parse-opts args cli-options)]
(cond
(:help options)
{:exit-message (usage summary) :ok? true}
errors
{:exit-message (error-msg errors)}
(not= (count arguments) 1)
{:exit-message "Only allow one argument."}
(#{"generate" "validate"} (first arguments))
{:action (keyword (first arguments)) :options options}
:else
{:exit-message (usage summary)})))
(defn exit [status msg]
(println msg)
(System/exit status))
(def actions-map
{:generate generate-all
:validate validate-all})
(defn add-shutdown-hook [handler]
"Add shutdown hook so we can properly exit all the file watchers."
(-> (Runtime/getRuntime)
(.addShutdownHook
(new Thread
(fn []
(println "Exiting. Cleaning up all watchers.")
(handler))))))
(defn filtered-log-middleware
"This middleware will replace normal info logging with typical println so the user will get normal prompts in the
CLI. Only works for info, will ignore any other types. Once the user turns on verbosity, will revert back to normal
logging."
[appender-data]
(let [{:keys [level vargs]} appender-data]
(if (or (= :info level) (= :error level))
(println (string/join " " vargs))
appender-data)))
(defn config-log
"Configure Timbre. Will merge instead of replacing the default config."
[{:keys [min-level]}]
(let [log-level (case min-level
0 :info
1 :debug
2 :trace)
middleware (if (= min-level 0)
[filtered-log-middleware]
[])]
(log/merge-config! {:min-level log-level
:middleware middleware})))
(defn -main [& args]
(let [{:keys [action options exit-message ok?]} (validate-args args)]
(if exit-message
(exit (if ok? 0 1) exit-message)
(do
(config-log options)
(log/info "Welcome!")
(let [{:keys [handler future]} ((get actions-map action) options)]
(if (:watch? options)
(do
(add-shutdown-hook handler)
(.get future))))))))