 (ns fhofherr.clj-db-util.transactions
   (:require [clojure.java.jdbc :as jdbc]))
 
+;; Wrapper for a transaction. `op` is a function of two arguments returning
+;; a map {::connection con ::value v}. The first argument to `op` is the
+;; dialect currently in use, the second the connection.
 (defrecord Transaction [op])
 
-(defn tx-return [v] (Transaction. (fn [dialect con]
-                                    {::connection con ::value v})))
+(alter-meta! #'->Transaction assoc :no-doc true)
+(alter-meta! #'map->Transaction assoc :no-doc true)
 
-(defn tx-bind [tx f]
+(defn tx-return
+  "Take an arbitrary value `v` and wrap it into a transaction.
+
+  *Parameters*:
+
+  - `v` an arbitrary value to wrap into a transaction."
+  [v]
+  (Transaction. (fn [dialect con] {::connection con ::value v})))
+
+(defn tx-bind
+  "Take a transacton `tx` containting a value `v` and a function `f` of
+  one argument returning another transaction. Apply `f` to `v` and return
+  the resulting transaction.
+
+  *Parameters*:
+
+  - `tx` a transaction.
+  - `f` a function expecting one argument and returning a transaction."
+  [tx f]
   (Transaction. (fn [dialect con]
                   (let [{con* ::connection v ::value} ((:op tx) dialect con)
                         tx* (f v)]
                     ((:op tx*) dialect con*)))))
 
 (defmacro deftxfn
+  "Define a function that creates a transaction. The first entry of the
+  definitions argument vector will be bound to the dialect used during the
+  execution of the transaction. The second entry of the argument vector
+  will be bound to the current database connection. Further arguments are
+  optional."
+  {:arglists '([fn-name [dialect db-spec & args] & body]
+               [fn-name doc-string [dialect db-spec & args] & body])}
   [fn-name & body]
   (let [has-doc? (string? (first body))
         doc-string (if has-doc? (first body) nil)

         ~@(emit-tx-steps reordered-bindings))))

(defn tx-exec
  [dialect db-spec tx]
  (jdbc/with-db-transaction [con db-spec]
    (::value ((:op tx) dialect con))))

(defmacro tx-exec->
  [dialect db-spec & bindings]
  `(tx-exec ~dialect ~db-spec (tx-> ~@bindings)))

                        ::value (do ~@decls)})))))

(defmacro deftxfn-
  "Same as [[deftxfn]] but yielding a non-public def."
  {:arglists '([fn-name [dialect db-spec & args] & body]
               [fn-name doc-string [dialect db-spec & args] & body])}
  [fn-name & body]
  `(deftxfn ~(with-meta fn-name (assoc (meta fn-name) :private true))
     ~@body))
