 (defn put-item
   "Adds an item (Clojure map) to a table with options:
     :return   - e/o #{:none :all-old :updated-old :all-new :updated-new}.
-    :expected - a map of item attribute/condition pairs, all of which must be
+    :expected - A map of item attribute/condition pairs, all of which must be
                 met for the operation to succeed. e.g.:
                   {<attr> <expected-value> ...}
                   {<attr> false ...} ; Attribute must not exist"

(defn query
  "Retries items from a table (indexed) with options:
    prim-key-conds - {<key-attr> [<comparison-operator> <values>] ...}.
    :last-prim-kvs - primary key-val from which to eval, useful for paging.
    :return        - e/o #{:all-attributes :all-projected-attributes :count
                           [<attr> ...]}.
    :index         - name of a secondary index to query.
    :order         - index scaning order e/o #{:asc :desc}.
    :limit         - max num >=1 of items to eval (≠ num of matching items).
    :consistent?   - use strongly (rather than eventually) consistent reads?

  comparison-operators e/o #{:eq :le :lt :ge :gt :begins-with :between}.

  For unindexed item retrievel see `scan`."
  [creds table prim-key-conds
   & [{:keys [last-prim-kvs return index order limit consistent? return-cc?]
       :or   {order :asc}}]]
  (as-map
   (.query (db-client creds)
     (doto-maybe (QueryRequest.) g
       :always (.setTableName        (name table))
       :always (.setKeyConditions    (query|scan-conditions prim-key-conds))
       :always (.setScanIndexForward (case order :asc true :desc false))
       last-prim-kvs   (.setExclusiveStartKey (clj-item->db-item g))
       limit           (.setLimit    (long g))
       index           (.setIndexName      g)
       consistent?     (.setConsistentRead g)
       (coll?* return) (.setAttributesToGet (mapv name return))
       return-cc? (.setReturnConsumedCapacity (utils/enum :total))
       (and return (not (coll?* return))) (.setSelect (utils/enum return))))))

(defn scan
  "Retrieves items from a table (unindexed) with options:
    :attr-conds     - {<attr> [<comparison-operator> <values>] ...}.
    :limit          - max num >=1 of items to eval (≠ num of matching items).
    :last-prim-kvs  - primary key-val from which to eval, useful for paging.
    :return         - e/o #{:all-attributes :all-projected-attributes :count
                            [<attr> ...]}.
    :total-segments - total number of parallel scan segments.
    :segment        - calling worker's segment number (>=0, <=total-segments).

  comparison-operators e/o #{:eq :le :lt :ge :gt :begins-with :between :ne
                             :not-null :null :contains :not-contains :in}.


(defmacro ^:private am-query|scan-result [result & [meta]]
  `(let [result# ~result]
     (merge {:items (mapv db-item->clj-item (.getItems result#))
             :count (.getCount result#)
             :consumed-capacity (cc-units (.getConsumedCapacity result#))
             :last-prim-kvs (as-map (.getLastEvaluatedKey result#))}
            ~meta)))

(extend-protocol AsMap
  nil                 (as-map [_] nil)
