(defn
->rule
"Returns a new rule. In most cases, you should use the `ruleset` macro to define rules,\n but if you want to define rules dynamically, you can use this function instead.\n See the README section \"Defining rules dynamically\".\n The one-argument arity is only meant for internal use."
([rule-name rule]
(when
(vector? rule)
(throw
(ex-info
"The syntax for dynamic rules changed! It now should be a map, and the fns take an extra `session` arg. See the README for more."
{})))
(let
[parsed-rule
(parse :odoyle.rules/dynamic-rule rule)
parsed-rule
(cond->
{:what-block {:body (:what parsed-rule)}}
(:when parsed-rule)
(assoc-in [:when-block :body] (:when parsed-rule))
(:then parsed-rule)
(assoc-in [:then-block :body] (:then parsed-rule))
(:then-finally parsed-rule)
(assoc-in
[:then-finally-block :body]
(:then-finally parsed-rule)))
{:keys
[rule-name conditions when-body then-body then-finally-body]}
(->rule [rule-name parsed-rule])]
(->Rule
rule-name
(mapv map->Condition conditions)
nil
when-body
then-body
then-finally-body)))
([[rule-name parsed-rule]]
(let
[{:keys [what-block when-block then-block then-finally-block]}
parsed-rule
conditions
(mapv ->condition (:body what-block))
when-body
(:body when-block)
then-body
(:body then-block)
then-finally-body
(:body then-finally-block)
syms
(->>
conditions
(mapcat :bindings)
(map :sym)
(map last)
(filter simple-symbol?)
set
vec)]
{:rule-name rule-name,
:fn-name
(->
(str (namespace rule-name) "-" (name rule-name))
(str/replace "." "-")
symbol),
:conditions conditions,
:arg {:keys syms, :as 'match},
:when-body
(cond
(fn? when-body)
when-body
(> (count when-body) 1)
(cons 'and when-body)
:else
(first when-body)),
:then-body then-body,
:then-finally-body then-finally-body})))