blob: 97bf63abcf8bc3ed8d975de5c7911857a22d044e [file] [log] [blame]
// Copyright 2016 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.
#include "peridot/examples/todo_cpp/todo.h"
#include <fuchsia/ledger/internal/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/component/cpp/connect.h>
#include <lib/fit/function.h>
#include <lib/fsl/vmo/strings.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <algorithm>
#include <iostream>
#include "src/lib/fxl/logging.h"
#include "src/lib/fxl/strings/string_printf.h"
#include "src/lib/fxl/time/time_delta.h"
namespace todo {
namespace {
const double kMeanListSize = 7.0;
const double kListSizeStdDev = 2.0;
const int kMinDelaySeconds = 1;
const int kMaxDelaySeconds = 5;
std::string ToString(const fuchsia::mem::Buffer& vmo) {
std::string ret;
if (!fsl::StringFromVmo(vmo, &ret)) {
FXL_DCHECK(false);
}
return ret;
}
fidl::VectorPtr<uint8_t> ToArray(const std::string& val) {
auto ret = fidl::VectorPtr<uint8_t>::New(val.size());
memcpy(ret->data(), val.data(), val.size());
return ret;
}
Key MakeKey() {
return ToArray(fxl::StringPrintf("%120ld-%u", time(nullptr), rand()));
}
fit::function<void(zx_status_t)> NewErrorHandler(fit::closure quit_callback,
std::string description) {
return [description, &quit_callback](zx_status_t status) {
FXL_LOG(ERROR) << description << " diconnected: " << status;
quit_callback();
};
}
void GetEntries(
fuchsia::ledger::PageSnapshotPtr snapshot,
std::vector<fuchsia::ledger::Entry> entries,
std::unique_ptr<fuchsia::ledger::Token> token,
fit::function<void(std::vector<fuchsia::ledger::Entry>)> callback) {
fuchsia::ledger::PageSnapshot* snapshot_ptr = snapshot.get();
snapshot_ptr->GetEntries(
fidl::VectorPtr<uint8_t>::New(0), std::move(token),
[snapshot = std::move(snapshot), entries = std::move(entries),
callback = std::move(callback)](fuchsia::ledger::IterationStatus status,
auto new_entries,
auto next_token) mutable {
for (size_t i = 0; i < new_entries.size(); ++i) {
entries.push_back(std::move(new_entries.at(i)));
}
if (status == fuchsia::ledger::IterationStatus::OK) {
callback(std::move(entries));
return;
}
GetEntries(std::move(snapshot), std::move(entries),
std::move(next_token), std::move(callback));
});
}
void GetEntries(
fuchsia::ledger::PageSnapshotPtr snapshot,
fit::function<void(std::vector<fuchsia::ledger::Entry>)> callback) {
GetEntries(std::move(snapshot), {}, nullptr, std::move(callback));
}
} // namespace
TodoApp::TodoApp(async::Loop* loop)
: loop_(loop),
rng_(time(nullptr)),
size_distribution_(kMeanListSize, kListSizeStdDev),
delay_distribution_(kMinDelaySeconds, kMaxDelaySeconds),
generator_(&rng_),
context_(component::StartupContext::CreateFromStartupInfo()),
page_watcher_binding_(this) {
context_->ConnectToEnvironmentService(module_context_.NewRequest());
module_context_->GetComponentContext(component_context_.NewRequest());
ledger_.set_error_handler(
NewErrorHandler([this] { loop_->Quit(); }, "Ledger"));
component_context_->GetLedger(ledger_.NewRequest());
ledger_->GetRootPage(page_.NewRequest());
fuchsia::ledger::PageSnapshotPtr snapshot;
page_->GetSnapshot(snapshot.NewRequest(), fidl::VectorPtr<uint8_t>::New(0),
page_watcher_binding_.NewBinding());
List(std::move(snapshot));
async::PostTask(loop_->dispatcher(), [this] { Act(); });
}
void TodoApp::Terminate() { loop_->Quit(); }
void TodoApp::OnChange(fuchsia::ledger::PageChange /*page_change*/,
fuchsia::ledger::ResultState result_state,
OnChangeCallback callback) {
if (result_state != fuchsia::ledger::ResultState::PARTIAL_STARTED &&
result_state != fuchsia::ledger::ResultState::COMPLETED) {
// Only request the entries list once, on the first OnChange call.
callback(nullptr);
return;
}
fuchsia::ledger::PageSnapshotPtr snapshot;
callback(snapshot.NewRequest());
List(std::move(snapshot));
}
void TodoApp::List(fuchsia::ledger::PageSnapshotPtr snapshot) {
GetEntries(std::move(snapshot), [this](auto entries) {
std::cout << "--- To Do ---" << std::endl;
for (auto& entry : entries) {
std::cout << (entry.value ? ToString(*entry.value) : "<empty>")
<< std::endl;
}
std::cout << "---" << std::endl;
});
}
void TodoApp::GetKeys(fit::function<void(std::vector<Key>)> callback) {
fuchsia::ledger::PageSnapshotPtr snapshot;
page_->GetSnapshot(snapshot.NewRequest(), {}, nullptr);
fuchsia::ledger::PageSnapshot* snapshot_ptr = snapshot.get();
snapshot_ptr->GetKeys(
{}, nullptr,
[snapshot = std::move(snapshot), callback = std::move(callback)](
fuchsia::ledger::IterationStatus status, auto keys, auto next_token) {
callback(std::move(keys));
});
}
void TodoApp::AddNew() {
page_->Put(MakeKey(), ToArray(generator_.Generate()));
}
void TodoApp::DeleteOne(std::vector<Key> keys) {
FXL_DCHECK(keys.size());
std::uniform_int_distribution<> distribution(0, keys.size() - 1);
page_->Delete(std::move(keys.at(distribution(rng_))));
}
void TodoApp::Act() {
GetKeys([this](std::vector<Key> keys) {
size_t target_size = std::round(size_distribution_(rng_));
if (keys.size() > std::max(static_cast<size_t>(0), target_size)) {
DeleteOne(std::move(keys));
} else {
AddNew();
}
});
zx::duration delay = zx::sec(delay_distribution_(rng_));
async::PostDelayedTask(
loop_->dispatcher(), [this] { Act(); }, delay);
}
} // namespace todo
int main(int /*argc*/, const char** /*argv*/) {
async::Loop loop(&kAsyncLoopConfigAttachToThread);
todo::TodoApp app(&loop);
loop.Run();
return 0;
}