; A macro for doing CAS updates to multiple documents
(defmacro update!
 [[node-binding get-node
   & bindings]
  & body]
 (let [[entity-binding get-entities] (take-last 2 bindings)
       bindings (drop-last 2 bindings)]
   `(let [node# ~get-node
          ~node-binding node#]
      (loop [count# 0]
        (if (>= count# *max-update!-count*)
          (throw (ex-info "Exhausted attempts at CAS" {:count count#}))
          (let [~@bindings
                entities# ~get-entities
                ~entity-binding entities#
                txs# (mapv
                       (fn [old-ent# ent#]
                         [:crux.tx/cas
                          old-ent#
                          ent#])
                       entities#
                       (do ~@body))]
            (let [submitted-tx# (crux.api/submit-tx
                                  node#
                                  txs#)]
              (if (txs-succeeded? node# txs# submitted-tx#)
                submitted-tx#
                (recur (inc count#))))))))))
;; usage for a single entity
(let [eid #uuid "6f0232d0-f3f9-4020-a75f-17b067f41203"]
   (update!
     [node (::standalone dev-extras/node)
      [entity] [(crux.api/entity (crux.api/db node) eid)]]
     [(update entity :magic-value inc)]))
;; usage for a set of entities returned by a query
(update!
  [node (::standalone dev-extras/node)
   db (crux.api/db node)
   q (crux.api/q
       db
       '{:find [?e]
         :where [[?e :name _]]})
   entities (map #(crux.api/entity db %) (map first q))]
  entities)
; Works like `let`
; This macro is not ideal as it requires you to return the list of entities in
; the same order that you query them in. It is still somewhat complex in that
; it requires the first binding to be the node, and the last binding to be
; your entities (and everything in the middle will be rerun on CAS failure).
; The middle is a good place for running `(crux/db)`.
; by @SevereOverfl0w