     (.listObjectsV2 s3 (clj->js opts) cb)
     chan))
 
-(defn list-objects [s3]
+(defn s3-list-objects [s3]
   (go
     (loop [acc {}
            continuationToken nil]
-      (let [[err data] (<! (list-objects-1000 s3 continuationToken))]
-        (if err {:err err}
-                (let [{contents :Contents isTruncated :IsTruncated nextToken :NextContinuationToken} (js->clj data :keywordize-keys true)
-                      acc (->> contents
-                               (reduce (fn [coll {key :Key etag :ETag}] (assoc coll key etag)) acc))]
-                  (if isTruncated
-                    (recur acc nextToken)
-                    acc)))))))
+      (let [[err data] (<! (s3-list-objects-1000 s3 continuationToken))]
+        (if err
+          [err nil]
+          (let [{contents :Contents isTruncated :IsTruncated nextToken :NextContinuationToken} (js->clj data :keywordize-keys true)
+                acc (->> contents
+                         (reduce (fn [coll {key :Key etag :ETag}] (assoc coll key etag)) acc))]
+            (if isTruncated
+              (recur acc nextToken)
+              [nil acc])))))))
 
-(defn make-s3 [profile region bucket]
+(defn make-s3-client [profile region bucket]
   (let [cfg
           (cond-> {:region region
                    :sslEnabled true


(ns biblecli.commands.bucketsync
  (:require-macros [cljs.core.async.macros :refer [go]])
  (:require [cljs.core.async :refer [to-chan chan put! tap mult <!]]))

(def AWS (js/require "aws-sdk"))

(defn list-objects-1000 [s3 continuationToken]
  (let [chan (chan)
        opts (cond-> {}
               continuationToken (assoc :ContinuationToken continuationToken))

              (assoc :credentials (AWS.SharedIniFileCredentials. (clj->js {:profile profile}))))]
    (AWS.S3. (clj->js cfg))))

(defn partition-files [s3keys files]
  [(->> s3keys (filter (fn [[key _]] (not (contains? files key)))) (vec))
   (->> files (filter (fn [[key _]] (not (contains? s3keys key)))) (vec))
   (->> files
        (map (fn [[key path]] (let [etag (get s3keys key :notfound)] [key path etag])))
        (filter (fn [[_ _ etag]] (not= etag :notfound)))
        (vec))])

(defn quit-on-error [err]
  (println err)
  (.exit process 1))

(defn quit-on-success []
  (.exit process 0))

(defn sync! [dir profile region bucket]
  (go
    (let [s3 (make-s3-client profile region bucket)
          s3ObjectsTask (s3-list-objects s3)
          filesTask (readdir-recursive dir)
          [err1 s3keys] (<! s3ObjectsTask)
          [err2 files] (<! filesTask)]
      (if err1 (quit-on-error err1))
      (if err2 (quit-on-error err2))
      (let [[onlyremote onlylocal both] (partition-files s3keys files)]
        (println "Remote: " (s/join onlyremote " | "))
        (println "Local: " (s/join onlylocal " | "))
        (println "Both: " (s/join both " | "))
        (quit-on-success)))))