[ledger_client] Add fit::promise<> wrappers for some common Ledger API calls.

These wrappers turn various Page and PageSnapshot calls into
fit::promise<> equivalents. Where a ledger.Status is returned, an OK
status is translated into an ok() result, and any other value is
translated into an error() result.

TEST=future CLs exercise these methods

Change-Id: I556cb1267edad4d0284a16c9e3b07def71837dc2
diff --git a/lib/ledger_client/BUILD.gn b/lib/ledger_client/BUILD.gn
index 5a92952..b23fdd6 100644
--- a/lib/ledger_client/BUILD.gn
+++ b/lib/ledger_client/BUILD.gn
@@ -7,6 +7,7 @@
     ":constants",
     ":operations",
     ":page_client",
+    ":promise",
     ":types",
   ]
 }
@@ -57,6 +58,17 @@
   ]
 }
 
+source_set("promise") {
+  sources = [
+    "promise.h",
+  ]
+
+  public_deps = [
+    "//peridot/lib/fidl:array_to_string",
+    "//peridot/public/fidl/fuchsia.ledger",
+  ]
+}
+
 source_set("status") {
   sources = [
     "status.cc",
diff --git a/lib/ledger_client/promise.h b/lib/ledger_client/promise.h
new file mode 100644
index 0000000..4947d75
--- /dev/null
+++ b/lib/ledger_client/promise.h
@@ -0,0 +1,136 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <fuchsia/ledger/cpp/fidl.h>
+#include <lib/fit/bridge.h>
+#include <lib/fit/promise.h>
+
+#include "peridot/lib/fidl/array_to_string.h"  // for to_string(), to_array()
+
+namespace modular {
+
+// fit::promise wrapper functions for fuchsia.ledger.Page.
+//
+// These methods match the signatures in fuchsia.ledger.Page with the exception
+// that the first parameter is always a fuchsia::ledger::Page*.
+class PagePromise {
+ public:
+  static fit::promise<> StartTransaction(fuchsia::ledger::Page* page) {
+    fit::bridge<> bridge;
+    page->StartTransaction([completer = std::move(bridge.completer)](
+                               fuchsia::ledger::Status status) mutable {
+      if (status == fuchsia::ledger::Status::OK) {
+        completer.complete_ok();
+      } else {
+        completer.complete_error();
+      }
+    });
+    return bridge.consumer.promise();
+  }
+
+  static fit::promise<> Commit(fuchsia::ledger::Page* page) {
+    fit::bridge<> bridge;
+    page->Commit([completer = std::move(bridge.completer)](
+                     fuchsia::ledger::Status status) mutable {
+      if (status == fuchsia::ledger::Status::OK) {
+        completer.complete_ok();
+      } else {
+        completer.complete_error();
+      }
+    });
+    return bridge.consumer.promise();
+  }
+
+  static fit::promise<> Rollback(fuchsia::ledger::Page* page) {
+    fit::bridge<> bridge;
+    page->Commit([completer = std::move(bridge.completer)](
+                     fuchsia::ledger::Status status) mutable {
+      if (status == fuchsia::ledger::Status::OK) {
+        completer.complete_ok();
+      } else {
+        completer.complete_error();
+      }
+    });
+    return bridge.consumer.promise();
+  }
+
+  static fit::promise<> GetSnapshot(
+      fuchsia::ledger::Page* page,
+      fidl::InterfaceRequest<fuchsia::ledger::PageSnapshot> request) {
+    fit::bridge<> bridge;
+    page->GetSnapshot(std::move(request),
+                      fidl::VectorPtr<uint8_t>::New(0) /* key_prefix */,
+                      nullptr /* watcher */,
+                      [completer = std::move(bridge.completer)](
+                          fuchsia::ledger::Status status) mutable {
+                        if (status == fuchsia::ledger::Status::OK) {
+                          completer.complete_ok();
+                        } else {
+                          completer.complete_error();
+                        }
+                      });
+    return bridge.consumer.promise();
+  }
+
+  static fit::promise<> Put(fuchsia::ledger::Page* page, std::string key,
+                            std::vector<uint8_t> value) {
+    fit::bridge<> bridge;
+    page->Put(to_array(key), fidl::VectorPtr<uint8_t>(std::move(value)),
+              [completer = std::move(bridge.completer)](
+                  fuchsia::ledger::Status status) mutable {
+                if (status == fuchsia::ledger::Status::OK) {
+                  completer.complete_ok();
+                } else {
+                  completer.complete_error();
+                }
+              });
+    return bridge.consumer.promise();
+  }
+};
+
+// fit::promise wrapper functions for fuchsia.ledger.Page.
+//
+// These methods match the signatures in fuchsia.ledger.Page with the exception
+// that the first parameter is always a fuchsia::ledger::Page*.
+class PageSnapshotPromise {
+ public:
+  // fit::promise wrapper function for PageSnapshot.GetInline().
+  //
+  // Falls back to PageSnapshot::Get() if the value is too large.
+  // TODO(thatguy): Implement the fallback.
+  static fit::promise<std::unique_ptr<std::vector<uint8_t>>> GetInline(
+      fuchsia::ledger::PageSnapshot* snapshot, std::string key) {
+    fit::bridge<std::unique_ptr<std::vector<uint8_t>>> bridge;
+    snapshot->GetInline(
+        to_array(key), [completer = std::move(bridge.completer)](
+                           fuchsia::ledger::Status status,
+                           fuchsia::ledger::InlinedValuePtr value) mutable {
+          switch (status) {
+            case fuchsia::ledger::Status::OK:
+            case fuchsia::ledger::Status::KEY_NOT_FOUND:
+              if (value) {
+                // Convert the result to a unique_ptr instead of a VectorPtr.
+                auto ret =
+                    std::make_unique<std::vector<uint8_t>>(value->value.take());
+                completer.complete_ok(std::move(ret));
+              } else {
+                completer.complete_ok(nullptr);
+              }
+              break;
+            case fuchsia::ledger::Status::VALUE_TOO_LARGE:
+              // TODO(thatguy): Handle a too-large value.
+              FXL_LOG(FATAL) << "TODO: fallback to PageSnapshot_Get().";
+            default:
+              FXL_LOG(ERROR) << "PageSnapshotPromise::GetInline() failed with "
+                << fidl::ToUnderlying(status);
+              completer.complete_error();
+          }
+        });
+    return bridge.consumer.promise();
+  }
+};
+
+}  // namespace modular