-
Notifications
You must be signed in to change notification settings - Fork 0
/
model.clj
138 lines (103 loc) · 2.86 KB
/
model.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
(ns automigrate.util.model
(:require [clojure.spec.alpha :as s]
[spec-dict :as d]
[automigrate.util.spec :as spec-util]
[clojure.string :as str]))
(def EMPTY-OPTION :EMPTY)
(def OPTION-KEY-FORWARD :to)
(def OPTION-KEY-BACKWARD :from)
(s/def ::option-key
#{OPTION-KEY-FORWARD OPTION-KEY-BACKWARD})
(defn kw->vec
[kw]
(when (qualified-keyword? kw)
(mapv keyword
((juxt namespace name) kw))))
(defn kw->map
[kw]
(when-let [[model-name field-name] (kw->vec kw)]
{:model-name model-name
:field-name field-name}))
(defn- kw->name
"Convert full qualified keyword to keywordized name."
[kw]
(-> kw name keyword))
(defn- kw->kebab-case
[kw]
(-> kw
(name)
(str/replace #"_" "-")
(keyword)))
(defn map-kw-keys->kebab-case
[map-kw]
(reduce-kv
(fn [m k v]
(assoc m (kw->kebab-case k) v))
{}
map-kw))
(defn kw->snake-case
[kw]
(-> kw
(name)
(str/replace #"-" "_")
(keyword)))
(defn kw->snake-case-str
[kw]
(str/replace (name kw) #"-" "_"))
(defn- remove-empty-option
"Remove option key if value by `option-key` direction is empty."
[option-key m k v]
(if (not= EMPTY-OPTION (get v option-key))
(assoc m k (get v option-key))
m))
(defn changes-to-add
([changes]
(changes-to-add changes OPTION-KEY-FORWARD))
([changes option-key]
{:pre [(spec-util/assert! ::option-key option-key)]}
(reduce-kv (partial remove-empty-option option-key) {} changes)))
(defn changes-to-drop
([changes]
(changes-to-drop changes OPTION-KEY-FORWARD))
([changes option-key]
{:pre [(spec-util/assert! ::option-key option-key)]}
(->> changes
(filter #(= EMPTY-OPTION (get (val %) option-key)))
(map key)
(set))))
(defn check-option-state
[value]
(not= (get value OPTION-KEY-BACKWARD) (get value OPTION-KEY-FORWARD)))
(defn option-states
[field-spec]
(s/and
(d/dict*
^:opt {OPTION-KEY-BACKWARD (s/and (s/or :empty #{EMPTY-OPTION}
:value field-spec)
(s/conformer peek))
OPTION-KEY-FORWARD (s/and (s/or :empty #{EMPTY-OPTION}
:value field-spec)
(s/conformer peek))})
check-option-state))
(defn generate-changes
[option-specs]
{:pre [(spec-util/assert! (s/coll-of qualified-keyword?) option-specs)]}
(reduce #(assoc %1
(kw->name %2)
(option-states %2))
{}
option-specs))
(defn generate-type-option
[type-spec]
{:type (s/and
(d/dict*
^:opt {OPTION-KEY-BACKWARD type-spec
OPTION-KEY-FORWARD type-spec})
check-option-state)})
(defn has-duplicates?
"Check duplicated items in collection."
[items]
(->> items
(frequencies)
(vals)
(every? #(= 1 %))))