diff --git a/doc/devel/contrib/rust-and-c++.md b/doc/devel/contrib/rust-and-c++.md index 5317985e3..6f2ee8116 100644 --- a/doc/devel/contrib/rust-and-c++.md +++ b/doc/devel/contrib/rust-and-c++.md @@ -7,6 +7,7 @@ Taskwarrior has historically been a C++ project, but as of taskwarrior-3.0.0, th TaskChampion implements storage and access to "replicas" containing a user's tasks. It defines an abstract model for this data, and also provides a simple Rust API for manipulating replicas. It also defines a method of synchronizing replicas and provides an implementation of that method in the form of a sync server. +TODO: update this TaskChampion provides a C interface via the `taskchampion-lib` crate, at `src/tc/lib`. Other applications, besides Taskwarrior, can use TaskChampion to manage tasks. @@ -17,5 +18,6 @@ Taskwarrior is just one application using the TaskChampion interface. Taskwarrior's interface to TaskChampion has a few layers: * A Rust library, `takschampion-lib`, that presents `extern "C"` functions for use from C++, essentially defining a C interface to TaskChampion. +TODO: update this * C++ wrappers for the types from `taskchampion-lib`, defined in [`src/tc`](../../src/tc), ensuring memory safety (with `unique_ptr`) and adding methods corresponding to the Rust API's methods. The wrapper types are in the C++ namespace, `tc`. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ec9bdb772..325a6e48b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,13 +1,16 @@ cmake_minimum_required (VERSION 3.22) +# TODO: explicit path shouldn't be required include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/src/tc - ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src + ${CMAKE_SOURCE_DIR}/build/src/tc/corrosion_generated/cxxbridge/taskchampion-cpp/include ${TASK_INCLUDE_DIRS}) +# TODO: temporary +add_definitions(-w) + add_library (task STATIC CLI2.cpp CLI2.h Context.cpp Context.h DOM.cpp DOM.h @@ -53,11 +56,11 @@ add_executable (calc_executable calc.cpp) add_executable (lex_executable lex.cpp) # Yes, 'task' (and hence libshared) is included twice, otherwise linking fails on assorted OSes. -# Similarly for `tc`. -target_link_libraries (task_executable task tc commands tc columns libshared task libshared ${TASK_LIBRARIES}) +# Similarly for `taskchampion-cpp`. +target_link_libraries (task_executable task taskchampion-cpp commands taskchampion-cpp columns libshared task libshared ${TASK_LIBRARIES}) target_link_libraries (tmp_executable taskchampion-cpp ${TASK_LIBRARIES}) -target_link_libraries (calc_executable task tc commands tc columns libshared task libshared ${TASK_LIBRARIES}) -target_link_libraries (lex_executable task tc commands tc columns libshared task libshared ${TASK_LIBRARIES}) +target_link_libraries (calc_executable task taskchampion-cpp commands taskchampion-cpp columns libshared task libshared ${TASK_LIBRARIES}) +target_link_libraries (lex_executable task taskchampion-cpp commands taskchampion-cpp columns libshared task libshared ${TASK_LIBRARIES}) if (DARWIN) # SystemConfiguration is required by Rust libraries like reqwest, to get proxy configuration. target_link_libraries (task_executable "-framework CoreFoundation -framework Security -framework SystemConfiguration") diff --git a/src/TDB2.cpp b/src/TDB2.cpp index d175a61ff..449180143 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -46,26 +46,19 @@ #include #include -#include "tc/Server.h" -#include "tc/util.h" - bool TDB2::debug_mode = false; static void dependency_scan(std::vector&); -//////////////////////////////////////////////////////////////////////////////// -TDB2::TDB2() - : replica{tc::Replica()} // in-memory Replica - , - _working_set{} {} - //////////////////////////////////////////////////////////////////////////////// void TDB2::open_replica(const std::string& location, bool create_if_missing) { - replica = tc::Replica(location, create_if_missing); + _replica = tc::new_replica_on_disk(location, create_if_missing); } //////////////////////////////////////////////////////////////////////////////// // Add the new task to the replica. void TDB2::add(Task& task) { + // TODO + /* // Ensure the task is consistent, and provide defaults if necessary. // bool argument to validate() is "applyDefault", to apply default values for // properties not otherwise given. @@ -80,7 +73,7 @@ void TDB2::add(Task& task) { auto innertask = replica.import_task_with_uuid(uuid); { - auto guard = replica.mutate_task(innertask); + auto guard = replica().mutate_task(innertask); // add the task attributes for (auto& attr : task.all()) { @@ -120,6 +113,7 @@ void TDB2::add(Task& task) { if (id.has_value()) { task.id = id.value(); } + */ } //////////////////////////////////////////////////////////////////////////////// @@ -138,6 +132,8 @@ void TDB2::add(Task& task) { // to the user. This is especially unlikely since tasks are only deleted when // they have been unmodified for a long time. void TDB2::modify(Task& task) { + // TODO + /* // All locally modified tasks are timestamped, implicitly overwriting any // changes the user or hooks tried to apply to the "modified" attribute. task.setAsNow("modified"); @@ -191,18 +187,28 @@ void TDB2::modify(Task& task) { tctask.set_value(kv.first, {}); } } + */ } //////////////////////////////////////////////////////////////////////////////// void TDB2::purge(Task& task) { + // TODO + /* auto uuid = task.get("uuid"); replica.delete_task(uuid); + */ } //////////////////////////////////////////////////////////////////////////////// -const tc::WorkingSet& TDB2::working_set() { +rust::Box& TDB2::replica() { + assert(_replica.has_value()); + return _replica.value(); +} + +//////////////////////////////////////////////////////////////////////////////// +const rust::Box& TDB2::working_set() { if (!_working_set.has_value()) { - _working_set = std::make_optional(replica.working_set()); + _working_set = std::make_optional(replica()->working_set()); } return _working_set.value(); } @@ -217,6 +223,8 @@ void TDB2::get_changes(std::vector& changes) { //////////////////////////////////////////////////////////////////////////////// void TDB2::revert() { + // TODO + /* auto undo_ops = replica.get_undo_ops(); if (undo_ops.len == 0) { std::cout << "No operations to undo."; @@ -224,15 +232,18 @@ void TDB2::revert() { } if (confirm_revert(undo_ops)) { // Has the side-effect of freeing undo_ops. - replica.commit_undo_ops(undo_ops, NULL); + replica().commit_undo_ops(undo_ops, NULL); } else { - replica.free_replica_ops(undo_ops); + replica().free_replica_ops(undo_ops); } replica.rebuild_working_set(false); + */ } //////////////////////////////////////////////////////////////////////////////// -bool TDB2::confirm_revert(struct tc::ffi::TCReplicaOpList undo_ops) { +bool TDB2::confirm_revert(rust::Vec) { + // TODO + /* // TODO Use show_diff rather than this basic listing of operations, though // this might be a worthy undo.style itself. std::cout << "The following " << undo_ops.len << " operations would be reverted:\n"; @@ -241,10 +252,10 @@ bool TDB2::confirm_revert(struct tc::ffi::TCReplicaOpList undo_ops) { tc::ffi::TCReplicaOp op = undo_ops.items[i]; switch (op.operation_type) { case tc::ffi::TCReplicaOpType::Create: - std::cout << "Create " << replica.get_op_uuid(op); + std::cout << "Create " << replica().get_op_uuid(op); break; case tc::ffi::TCReplicaOpType::Delete: - std::cout << "Delete " << replica.get_op_old_task_description(op); + std::cout << "Delete " << replica().get_op_old_task_description(op); break; case tc::ffi::TCReplicaOpType::Update: std::cout << "Update " << replica.get_op_uuid(op) << "\n"; @@ -265,6 +276,8 @@ bool TDB2::confirm_revert(struct tc::ffi::TCReplicaOpList undo_ops) { confirm( "The undo command is not reversible. Are you sure you want to revert to the previous " "state?"); + */ + return false; } //////////////////////////////////////////////////////////////////////////////// @@ -305,33 +318,39 @@ void TDB2::gc() { // Allowed as an override, but not recommended. if (Context::getContext().config.getBoolean("gc")) { - replica.rebuild_working_set(true); + replica()->rebuild_working_set(true); } Context::getContext().time_gc_us += timer.total_us(); } //////////////////////////////////////////////////////////////////////////////// -void TDB2::expire_tasks() { replica.expire_tasks(); } +void TDB2::expire_tasks() { replica()->expire_tasks(); } //////////////////////////////////////////////////////////////////////////////// // Latest ID is that of the last pending task. int TDB2::latest_id() { - const tc::WorkingSet& ws = working_set(); - return (int)ws.largest_index(); + auto& ws = working_set(); + return (int)ws->largest_index(); } //////////////////////////////////////////////////////////////////////////////// const std::vector TDB2::all_tasks() { + // TODO + /* auto all_tctasks = replica.all_tasks(); std::vector all; for (auto& tctask : all_tctasks) all.push_back(Task(std::move(tctask))); return all; + */ + return {}; } //////////////////////////////////////////////////////////////////////////////// const std::vector TDB2::pending_tasks() { + // TODO + /* const tc::WorkingSet& ws = working_set(); auto largest_index = ws.largest_index(); @@ -349,10 +368,14 @@ const std::vector TDB2::pending_tasks() { dependency_scan(result); return result; + */ + return {}; } //////////////////////////////////////////////////////////////////////////////// const std::vector TDB2::completed_tasks() { + // TODO + /* auto all_tctasks = replica.all_tasks(); const tc::WorkingSet& ws = working_set(); @@ -365,20 +388,25 @@ const std::vector TDB2::completed_tasks() { } return result; + */ + return {}; } //////////////////////////////////////////////////////////////////////////////// // Locate task by ID, wherever it is. bool TDB2::get(int id, Task& task) { + // TODO + /* const tc::WorkingSet& ws = working_set(); const auto maybe_uuid = ws.by_index(id); if (maybe_uuid) { - auto maybe_task = replica.get_task(*maybe_uuid); + auto maybe_task = replica().get_task(*maybe_uuid); if (maybe_task) { task = Task{std::move(*maybe_task)}; return true; } } + */ return false; } @@ -386,6 +414,8 @@ bool TDB2::get(int id, Task& task) { //////////////////////////////////////////////////////////////////////////////// // Locate task by UUID, including by partial ID, wherever it is. bool TDB2::get(const std::string& uuid, Task& task) { + // TOOD + /* // try by raw uuid, if the length is right if (uuid.size() == 36) { try { @@ -406,6 +436,7 @@ bool TDB2::get(const std::string& uuid, Task& task) { return true; } } + */ return false; } @@ -444,24 +475,25 @@ const std::vector TDB2::siblings(Task& task) { const std::vector TDB2::children(Task& parent) { // scan _pending_ tasks for those with `parent` equal to this task std::vector results; + // TODO + /* std::string this_uuid = parent.get("uuid"); - const tc::WorkingSet& ws = working_set(); - size_t end_idx = ws.largest_index(); + auto& ws = working_set(); + size_t end_idx = ws->largest_index(); for (size_t i = 0; i <= end_idx; i++) { - auto uuid_opt = ws.by_index(i); - if (!uuid_opt) { + auto uuid = ws->by_index(i); + if (uuid.is_nil()) { continue; } - auto uuid = uuid_opt.value(); // skip self-references - if (uuid == this_uuid) { + if (uuid.to_string() == this_uuid) { continue; } - auto task_opt = replica.get_task(uuid_opt.value()); + auto task_opt = replica.get_task(uuid.value()); if (!task_opt) { continue; } @@ -477,31 +509,41 @@ const std::vector TDB2::children(Task& parent) { results.push_back(Task(std::move(task))); } } - +*/ return results; } //////////////////////////////////////////////////////////////////////////////// std::string TDB2::uuid(int id) { + // TODO + /* const tc::WorkingSet& ws = working_set(); return ws.by_index((size_t)id).value_or(""); + */ + return ""; } //////////////////////////////////////////////////////////////////////////////// int TDB2::id(const std::string& uuid) { + // TODO + /* const tc::WorkingSet& ws = working_set(); return (int)ws.by_uuid(uuid).value_or(0); + */ } //////////////////////////////////////////////////////////////////////////////// -int TDB2::num_local_changes() { return (int)replica.num_local_operations(); } +int TDB2::num_local_changes() { return (int)replica()->num_local_operations(); } //////////////////////////////////////////////////////////////////////////////// -int TDB2::num_reverts_possible() { return (int)replica.num_undo_points(); } +int TDB2::num_reverts_possible() { return (int)replica()->num_undo_points(); } //////////////////////////////////////////////////////////////////////////////// -void TDB2::sync(tc::Server server, bool avoid_snapshots) { - replica.sync(std::move(server), avoid_snapshots); +void TDB2::sync(bool avoid_snapshots) { + // TODO + /* + replica()->sync(std::move(server), avoid_snapshots); + */ } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/TDB2.h b/src/TDB2.h index 03c8dca5b..f8895d3bf 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -30,8 +30,7 @@ #include #include #include -#include -#include +#include #include #include @@ -39,16 +38,12 @@ #include #include -namespace tc { -class Server; -} - // TDB2 Class represents all the files in the task database. class TDB2 { public: static bool debug_mode; - TDB2(); + TDB2() = default; void open_replica(const std::string &, bool create_if_missing); void add(Task &); @@ -79,17 +74,18 @@ class TDB2 { void dump(); - void sync(tc::Server server, bool avoid_snapshots); - bool confirm_revert(struct tc::ffi::TCReplicaOpList); + void sync(bool avoid_snapshots); + bool confirm_revert(rust::Vec); private: - tc::Replica replica; - std::optional _working_set; + std::optional > _replica; + std::optional > _working_set; // UUID -> Task containing all tasks modified in this invocation. std::map changes; - const tc::WorkingSet &working_set(); + rust::Box &replica(); + const rust::Box &working_set(); static std::string option_string(std::string input); static void show_diff(const std::string &, const std::string &, const std::string &); }; diff --git a/src/Task.cpp b/src/Task.cpp index a62533cf7..93b15a978 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -138,7 +138,7 @@ Task::Task(const json::object* obj) { } //////////////////////////////////////////////////////////////////////////////// -Task::Task(tc::Task obj) { +Task::Task(rust::Box obj) { id = 0; urgency_value = 0.0; recalc_urgency = true; @@ -146,7 +146,7 @@ Task::Task(tc::Task obj) { is_blocking = false; annotation_count = 0; - parseTC(obj); + parseTC(std::move(obj)); } //////////////////////////////////////////////////////////////////////////////// @@ -717,7 +717,9 @@ void Task::parseJSON(const json::object* root_obj) { //////////////////////////////////////////////////////////////////////////////// // Note that all fields undergo encode/decode. -void Task::parseTC(const tc::Task& task) { +void Task::parseTC(rust::Box task) { + // TODO + /* data = task.get_taskmap(); // count annotations @@ -733,6 +735,7 @@ void Task::parseTC(const tc::Task& task) { is_blocking = task.is_blocking(); is_blocked = task.is_blocked(); +*/ } //////////////////////////////////////////////////////////////////////////////// @@ -1602,40 +1605,6 @@ const std::string Task::decode(const std::string& value) const { return str_replace(modified, "&close;", "]"); } -//////////////////////////////////////////////////////////////////////////////// -tc::Status Task::status2tc(const Task::status status) { - switch (status) { - case Task::pending: - return tc::Status::Pending; - case Task::completed: - return tc::Status::Completed; - case Task::deleted: - return tc::Status::Deleted; - case Task::waiting: - return tc::Status::Pending; // waiting is no longer a status - case Task::recurring: - return tc::Status::Recurring; - default: - return tc::Status::Unknown; - } -} - -//////////////////////////////////////////////////////////////////////////////// -Task::status Task::tc2status(const tc::Status status) { - switch (status) { - case tc::Status::Pending: - return Task::pending; - case tc::Status::Completed: - return Task::completed; - case tc::Status::Deleted: - return Task::deleted; - case tc::Status::Recurring: - return Task::recurring; - default: - return Task::pending; - } -} - //////////////////////////////////////////////////////////////////////////////// int Task::determineVersion(const std::string& line) { // Version 2 looks like: diff --git a/src/Task.h b/src/Task.h index 28acb5c08..08d508f0a 100644 --- a/src/Task.h +++ b/src/Task.h @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include @@ -66,7 +66,7 @@ class Task { bool operator!=(const Task&); Task(const std::string&); Task(const json::object*); - Task(tc::Task); + Task(rust::Box); void parse(const std::string&); std::string composeJSON(bool decorate = false) const; @@ -88,8 +88,6 @@ class Task { // Series of helper functions. static status textToStatus(const std::string&); static std::string statusToText(status); - static tc::Status status2tc(const Task::status); - static Task::status tc2status(const tc::Status); void setAsNow(const std::string&); bool has(const std::string&) const; @@ -186,7 +184,7 @@ class Task { int determineVersion(const std::string&); void parseJSON(const std::string&); void parseJSON(const json::object*); - void parseTC(const tc::Task&); + void parseTC(rust::Box); void parseLegacy(const std::string&); void validate_before(const std::string&, const std::string&); const std::string encode(const std::string&) const; diff --git a/src/columns/CMakeLists.txt b/src/columns/CMakeLists.txt index ced88cb77..0235a0732 100644 --- a/src/columns/CMakeLists.txt +++ b/src/columns/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required (VERSION 3.22) +# TODO: explicit path shouldn't be required include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/tc @@ -6,6 +7,7 @@ include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src + ${CMAKE_SOURCE_DIR}/build/src/tc/corrosion_generated/cxxbridge/taskchampion-cpp/include ${TASK_INCLUDE_DIRS}) set (columns_SRCS Column.cpp Column.h diff --git a/src/commands/CMakeLists.txt b/src/commands/CMakeLists.txt index 16b8025af..b6cac39da 100644 --- a/src/commands/CMakeLists.txt +++ b/src/commands/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required (VERSION 3.22) +# TODO: explicit path shouldn't be required include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/src/tc - ${CMAKE_SOURCE_DIR}/src/tc/lib + ${CMAKE_SOURCE_DIR}/build/src/tc/corrosion_generated/cxxbridge/taskchampion-cpp/include ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index 6245b5acc..9f28f51c4 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -39,8 +39,6 @@ #include -#include "tc/Server.h" - //////////////////////////////////////////////////////////////////////////////// CmdSync::CmdSync() { _keyword = "synchronize"; @@ -59,6 +57,8 @@ CmdSync::CmdSync() { //////////////////////////////////////////////////////////////////////////////// int CmdSync::execute(std::string& output) { int status = 0; + // TODO + /* tc::Server server; std::string server_ident; @@ -115,6 +115,7 @@ int CmdSync::execute(std::string& output) { } output = out.str(); + */ return status; } diff --git a/src/tc/CMakeLists.txt b/src/tc/CMakeLists.txt index 25d00ddb6..26512808b 100644 --- a/src/tc/CMakeLists.txt +++ b/src/tc/CMakeLists.txt @@ -16,18 +16,5 @@ corrosion_add_cxxbridge(taskchampion-cpp include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/src/tc - ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/libshared/src ${TASK_INCLUDE_DIRS}) - -set (tc_SRCS - ffi.h - util.cpp util.h - Replica.cpp Replica.h - Server.cpp Server.h - WorkingSet.cpp WorkingSet.h - Task.cpp Task.h) - -add_library (tc STATIC ${tc_SRCS}) -target_link_libraries(tc taskchampion-lib taskchampion-cpp) diff --git a/src/tc/Replica.cpp b/src/tc/Replica.cpp deleted file mode 100644 index a90ac2e0b..000000000 --- a/src/tc/Replica.cpp +++ /dev/null @@ -1,273 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#include -// cmake.h include header must come first - -#include - -#include - -#include "tc/Replica.h" -#include "tc/Server.h" -#include "tc/Task.h" -#include "tc/WorkingSet.h" -#include "tc/util.h" - -using namespace tc::ffi; - -//////////////////////////////////////////////////////////////////////////////// -tc::ReplicaGuard::ReplicaGuard(Replica &replica, Task &task) : replica(replica), task(task) { - // "steal" the reference from the Replica and store it locally, so that any - // attempt to use the Replica will fail - tcreplica = replica.inner.release(); - task.to_mut(tcreplica); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::ReplicaGuard::~ReplicaGuard() { - task.to_immut(); - // return the reference to the Replica. - replica.inner.reset(tcreplica); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Replica::Replica() { - inner = unique_tcreplica_ptr(tc_replica_new_in_memory(), - [](TCReplica *rep) { tc_replica_free(rep); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Replica::Replica(Replica &&other) noexcept { - // move inner from other - inner = unique_tcreplica_ptr(other.inner.release(), [](TCReplica *rep) { tc_replica_free(rep); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Replica &tc::Replica::operator=(Replica &&other) noexcept { - if (this != &other) { - // move inner from other - inner = - unique_tcreplica_ptr(other.inner.release(), [](TCReplica *rep) { tc_replica_free(rep); }); - } - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Replica::Replica(const std::string &dir, bool create_if_missing) { - TCString path = tc_string_borrow(dir.c_str()); - TCString error; - auto tcreplica = tc_replica_new_on_disk(path, create_if_missing, &error); - if (!tcreplica) { - auto errmsg = format("Could not create replica at {1}: {2}", dir, tc_string_content(&error)); - tc_string_free(&error); - throw errmsg; - } - inner = unique_tcreplica_ptr(tcreplica, [](TCReplica *rep) { tc_replica_free(rep); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet tc::Replica::working_set() { - TCWorkingSet *tcws = tc_replica_working_set(&*inner); - if (!tcws) { - throw replica_error(); - } - return WorkingSet{tcws}; -} - -//////////////////////////////////////////////////////////////////////////////// -std::optional tc::Replica::get_task(const std::string &uuid) { - TCTask *tctask = tc_replica_get_task(&*inner, uuid2tc(uuid)); - if (!tctask) { - auto error = tc_replica_error(&*inner); - if (error.ptr) { - throw replica_error(error); - } else { - return std::nullopt; - } - } - return std::make_optional(Task(tctask)); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Task tc::Replica::new_task(tc::Status status, const std::string &description) { - TCTask *tctask = tc_replica_new_task(&*inner, (tc::ffi::TCStatus)status, string2tc(description)); - if (!tctask) { - throw replica_error(); - } - return Task(tctask); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Task tc::Replica::import_task_with_uuid(const std::string &uuid) { - TCTask *tctask = tc_replica_import_task_with_uuid(&*inner, uuid2tc(uuid)); - if (!tctask) { - throw replica_error(); - } - return Task(tctask); -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::delete_task(const std::string &uuid) { - auto res = tc_replica_delete_task(&*inner, uuid2tc(uuid)); - if (res != TC_RESULT_OK) { - throw replica_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::expire_tasks() { - auto res = tc_replica_expire_tasks(&*inner); - if (res != TC_RESULT_OK) { - throw replica_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::sync(Server server, bool avoid_snapshots) { - // The server remains owned by this function, per tc_replica_sync docs. - auto res = tc_replica_sync(&*inner, server.inner.get(), avoid_snapshots); - if (res != TC_RESULT_OK) { - throw replica_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -TCReplicaOpList tc::Replica::get_undo_ops() { return tc_replica_get_undo_ops(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::commit_undo_ops(TCReplicaOpList tc_undo_ops, int32_t *undone_out) { - auto res = tc_replica_commit_undo_ops(&*inner, tc_undo_ops, undone_out); - if (res != TC_RESULT_OK) { - throw replica_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::free_replica_ops(TCReplicaOpList tc_undo_ops) { - tc_replica_op_list_free(&tc_undo_ops); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_uuid(TCReplicaOp &tc_replica_op) const { - TCString uuid = tc_replica_op_get_uuid(&tc_replica_op); - return tc2string(uuid); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_property(TCReplicaOp &tc_replica_op) const { - TCString property = tc_replica_op_get_property(&tc_replica_op); - return tc2string(property); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_value(TCReplicaOp &tc_replica_op) const { - TCString value = tc_replica_op_get_value(&tc_replica_op); - return tc2string(value); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_old_value(TCReplicaOp &tc_replica_op) const { - TCString old_value = tc_replica_op_get_old_value(&tc_replica_op); - return tc2string(old_value); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_timestamp(TCReplicaOp &tc_replica_op) const { - TCString timestamp = tc_replica_op_get_timestamp(&tc_replica_op); - return tc2string(timestamp); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_old_task_description(TCReplicaOp &tc_replica_op) const { - TCString description = tc_replica_op_get_old_task_description(&tc_replica_op); - return tc2string(description); -} - -//////////////////////////////////////////////////////////////////////////////// -int64_t tc::Replica::num_local_operations() { - auto num = tc_replica_num_local_operations(&*inner); - if (num < 0) { - throw replica_error(); - } - return num; -} - -//////////////////////////////////////////////////////////////////////////////// -int64_t tc::Replica::num_undo_points() { - auto num = tc_replica_num_undo_points(&*inner); - if (num < 0) { - throw replica_error(); - } - return num; -} - -//////////////////////////////////////////////////////////////////////////////// -std::vector tc::Replica::all_tasks() { - TCTaskList tasks = tc_replica_all_tasks(&*inner); - if (!tasks.items) { - throw replica_error(); - } - - std::vector all; - all.reserve(tasks.len); - for (size_t i = 0; i < tasks.len; i++) { - auto tctask = tc_task_list_take(&tasks, i); - if (tctask) { - all.push_back(Task(tctask)); - } - } - - return all; -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::rebuild_working_set(bool force) { - auto res = tc_replica_rebuild_working_set(&*inner, force); - if (res != TC_RESULT_OK) { - throw replica_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -tc::ReplicaGuard tc::Replica::mutate_task(tc::Task &task) { return ReplicaGuard(*this, task); } - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::replica_error() { return replica_error(tc_replica_error(&*inner)); } - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::replica_error(TCString error) { - std::string errmsg; - if (!error.ptr) { - errmsg = std::string("Unknown TaskChampion error"); - } else { - errmsg = std::string(tc_string_content(&error)); - } - tc_string_free(&error); - return errmsg; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Replica.h b/src/tc/Replica.h deleted file mode 100644 index 8dd3ef188..000000000 --- a/src/tc/Replica.h +++ /dev/null @@ -1,127 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_REPLICA -#define INCLUDED_TC_REPLICA - -#include -#include -#include -#include -#include - -#include "tc/Task.h" -#include "tc/ffi.h" - -namespace tc { -class Task; -class WorkingSet; -class Server; - -// a unique_ptr to a TCReplica which will automatically free the value when -// it goes out of scope. -using unique_tcreplica_ptr = - std::unique_ptr>; - -// ReplicaGuard uses RAII to ensure that a Replica is not accessed while it -// is mutably borrowed (specifically, to make a task mutable). -class ReplicaGuard { - protected: - friend class Replica; - explicit ReplicaGuard(Replica &, Task &); - - public: - ~ReplicaGuard(); - - // No moving or copying allowed - ReplicaGuard(const ReplicaGuard &) = delete; - ReplicaGuard &operator=(const ReplicaGuard &) = delete; - ReplicaGuard(ReplicaGuard &&) = delete; - ReplicaGuard &operator=(Replica &&) = delete; - - private: - Replica &replica; - tc::ffi::TCReplica *tcreplica; - Task &task; -}; - -// Replica wraps the TCReplica type, managing its memory, errors, and so on. -// -// Except as noted, method names match the suffix to `tc_replica_..`. -class Replica { - public: - Replica(); // tc_replica_new_in_memory - Replica(const std::string &dir, bool create_if_missing); // tc_replica_new_on_disk - - // This object "owns" inner, so copy is not allowed. - Replica(const Replica &) = delete; - Replica &operator=(const Replica &) = delete; - - // Explicit move constructor and assignment - Replica(Replica &&) noexcept; - Replica &operator=(Replica &&) noexcept; - - std::vector all_tasks(); - // TODO: struct TCUuidList tc_replica_all_task_uuids(struct TCReplica *rep); - tc::WorkingSet working_set(); - std::optional get_task(const std::string &uuid); - tc::Task new_task(Status status, const std::string &description); - tc::Task import_task_with_uuid(const std::string &uuid); - void delete_task(const std::string &uuid); - // TODO: struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid - // tcuuid); - void expire_tasks(); - void sync(Server server, bool avoid_snapshots); - tc::ffi::TCReplicaOpList get_undo_ops(); - void commit_undo_ops(tc::ffi::TCReplicaOpList tc_undo_ops, int32_t *undone_out); - void free_replica_ops(tc::ffi::TCReplicaOpList tc_undo_ops); - std::string get_op_uuid(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_property(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_value(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_old_value(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_timestamp(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_old_task_description(tc::ffi::TCReplicaOp &tc_replica_op) const; - int64_t num_local_operations(); - int64_t num_undo_points(); - // TODO: TCResult tc_replica_add_undo_point(struct TCReplica *rep, bool force); - void rebuild_working_set(bool force); - - ReplicaGuard mutate_task(tc::Task &); - void immut_task(tc::Task &); - - protected: - friend class ReplicaGuard; - unique_tcreplica_ptr inner; - - // construct an error message from tc_replica_error, or from the given - // string retrieved from tc_replica_error. - std::string replica_error(); - std::string replica_error(tc::ffi::TCString string); -}; -} // namespace tc - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Server.cpp b/src/tc/Server.cpp deleted file mode 100644 index e5adef9dd..000000000 --- a/src/tc/Server.cpp +++ /dev/null @@ -1,109 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#include -// cmake.h include header must come first - -#include - -#include "tc/Server.h" -#include "tc/util.h" - -using namespace tc::ffi; - -//////////////////////////////////////////////////////////////////////////////// -tc::Server tc::Server::new_local(const std::string &server_dir) { - TCString tc_server_dir = tc_string_borrow(server_dir.c_str()); - TCString error; - auto tcserver = tc_server_new_local(tc_server_dir, &error); - if (!tcserver) { - std::string errmsg = format("Could not configure local server at {1}: {2}", server_dir, - tc_string_content(&error)); - tc_string_free(&error); - throw errmsg; - } - return Server(unique_tcserver_ptr(tcserver, [](TCServer *rep) { tc_server_free(rep); })); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Server tc::Server::new_sync(const std::string &url, const std::string &client_id, - const std::string &encryption_secret) { - TCString tc_url = tc_string_borrow(url.c_str()); - TCString tc_client_id = tc_string_borrow(client_id.c_str()); - TCString tc_encryption_secret = tc_string_borrow(encryption_secret.c_str()); - - TCUuid tc_client_uuid; - if (tc_uuid_from_str(tc_client_id, &tc_client_uuid) != TC_RESULT_OK) { - tc_string_free(&tc_url); - tc_string_free(&tc_encryption_secret); - throw format("client_id '{1}' is not a valid UUID", client_id); - } - - TCString error; - auto tcserver = tc_server_new_sync(tc_url, tc_client_uuid, tc_encryption_secret, &error); - if (!tcserver) { - std::string errmsg = format("Could not configure connection to server at {1}: {2}", url, - tc_string_content(&error)); - tc_string_free(&error); - throw errmsg; - } - return Server(unique_tcserver_ptr(tcserver, [](TCServer *rep) { tc_server_free(rep); })); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Server tc::Server::new_gcp(const std::string &bucket, const std::string &credential_path, - const std::string &encryption_secret) { - TCString tc_bucket = tc_string_borrow(bucket.c_str()); - TCString tc_encryption_secret = tc_string_borrow(encryption_secret.c_str()); - TCString tc_credential_path = tc_string_borrow(credential_path.c_str()); - - TCString error; - auto tcserver = tc_server_new_gcp(tc_bucket, tc_credential_path, tc_encryption_secret, &error); - if (!tcserver) { - std::string errmsg = format("Could not configure connection to GCP bucket {1}: {2}", bucket, - tc_string_content(&error)); - tc_string_free(&error); - throw errmsg; - } - return Server(unique_tcserver_ptr(tcserver, [](TCServer *rep) { tc_server_free(rep); })); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Server::Server(tc::Server &&other) noexcept { - // move inner from other - inner = unique_tcserver_ptr(other.inner.release(), [](TCServer *rep) { tc_server_free(rep); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Server &tc::Server::operator=(tc::Server &&other) noexcept { - if (this != &other) { - // move inner from other - inner = unique_tcserver_ptr(other.inner.release(), [](TCServer *rep) { tc_server_free(rep); }); - } - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Server.h b/src/tc/Server.h deleted file mode 100644 index 489d73425..000000000 --- a/src/tc/Server.h +++ /dev/null @@ -1,85 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_SERVER -#define INCLUDED_TC_SERVER - -#include -#include -#include -#include -#include - -#include "tc/ffi.h" - -namespace tc { -// a unique_ptr to a TCServer which will automatically free the value when -// it goes out of scope. -using unique_tcserver_ptr = - std::unique_ptr>; - -// Server wraps the TCServer type, managing its memory, errors, and so on. -// -// Except as noted, method names match the suffix to `tc_server_..`. -class Server { - public: - // Construct a null server - Server() = default; - - // Construct a local server (tc_server_new_local). - static Server new_local(const std::string &server_dir); - - // Construct a remote server (tc_server_new_sync). - static Server new_sync(const std::string &url, const std::string &client_id, - const std::string &encryption_secret); - - // Construct a GCP server (tc_server_new_gcp). - static Server new_gcp(const std::string &bucket, const std::string &credential_path, - const std::string &encryption_secret); - - // This object "owns" inner, so copy is not allowed. - Server(const Server &) = delete; - Server &operator=(const Server &) = delete; - - // Explicit move constructor and assignment - Server(Server &&) noexcept; - Server &operator=(Server &&) noexcept; - - protected: - Server(unique_tcserver_ptr inner) : inner(std::move(inner)) {}; - - unique_tcserver_ptr inner; - - // Replica accesses the inner pointer to call tc_replica_sync - friend class Replica; - - // construct an error message from the given string. - std::string server_error(tc::ffi::TCString string); -}; -} // namespace tc - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Task.cpp b/src/tc/Task.cpp deleted file mode 100644 index 5b5e5fd9a..000000000 --- a/src/tc/Task.cpp +++ /dev/null @@ -1,162 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#include -// cmake.h include header must come first - -#include - -#include "tc/Task.h" -#include "tc/util.h" - -using namespace tc::ffi; - -//////////////////////////////////////////////////////////////////////////////// -tc::Task::Task(TCTask* tctask) { - inner = unique_tctask_ptr(tctask, [](TCTask* task) { tc_task_free(task); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Task::Task(Task&& other) noexcept { - // move inner from other - inner = unique_tctask_ptr(other.inner.release(), [](TCTask* task) { tc_task_free(task); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Task& tc::Task::operator=(Task&& other) noexcept { - if (this != &other) { - // move inner from other - inner = unique_tctask_ptr(other.inner.release(), [](TCTask* task) { tc_task_free(task); }); - } - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Task::to_mut(TCReplica* replica) { tc_task_to_mut(&*inner, replica); } - -//////////////////////////////////////////////////////////////////////////////// -void tc::Task::to_immut() { tc_task_to_immut(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Task::get_uuid() const { - auto uuid = tc_task_get_uuid(&*inner); - return tc2uuid(uuid); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Status tc::Task::get_status() const { - auto status = tc_task_get_status(&*inner); - return tc::Status(status); -} - -//////////////////////////////////////////////////////////////////////////////// -std::map tc::Task::get_taskmap() const { - TCKVList kv = tc_task_get_taskmap(&*inner); - if (!kv.items) { - throw task_error(); - } - - std::map taskmap; - for (size_t i = 0; i < kv.len; i++) { - auto k = tc2string_clone(kv.items[i].key); - auto v = tc2string_clone(kv.items[i].value); - taskmap[k] = v; - } - - return taskmap; -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Task::get_description() const { - auto desc = tc_task_get_description(&*inner); - return tc2string(desc); -} - -//////////////////////////////////////////////////////////////////////////////// -std::optional tc::Task::get_value(std::string property) const { - auto maybe_desc = tc_task_get_value(&*inner, string2tc(property)); - if (maybe_desc.ptr == NULL) { - return std::nullopt; - } - return std::make_optional(tc2string(maybe_desc)); -} - -bool tc::Task::is_waiting() const { return tc_task_is_waiting(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -bool tc::Task::is_active() const { return tc_task_is_active(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -bool tc::Task::is_blocked() const { return tc_task_is_blocked(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -bool tc::Task::is_blocking() const { return tc_task_is_blocking(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -void tc::Task::set_status(tc::Status status) { - TCResult res = tc_task_set_status(&*inner, (TCStatus)status); - if (res != TC_RESULT_OK) { - throw task_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Task::set_value(std::string property, std::optional value) { - TCResult res; - if (value.has_value()) { - res = tc_task_set_value(&*inner, string2tc(property), string2tc(value.value())); - } else { - TCString nullstr; - nullstr.ptr = NULL; - res = tc_task_set_value(&*inner, string2tc(property), nullstr); - } - if (res != TC_RESULT_OK) { - throw task_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Task::set_modified(time_t modified) { - TCResult res = tc_task_set_modified(&*inner, modified); - if (res != TC_RESULT_OK) { - throw task_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Task::task_error() const { - TCString error = tc_task_error(&*inner); - std::string errmsg; - if (!error.ptr) { - errmsg = std::string("Unknown TaskChampion error"); - } else { - errmsg = std::string(tc_string_content(&error)); - } - tc_string_free(&error); - return errmsg; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Task.h b/src/tc/Task.h deleted file mode 100644 index d087dc810..000000000 --- a/src/tc/Task.h +++ /dev/null @@ -1,130 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell, Tomas Babej, Paul Beckingham, Federico Hernandez. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_TASK -#define INCLUDED_TC_TASK - -#include -#include -#include -#include -#include - -#include "tc/ffi.h" - -namespace tc { -class Replica; -class ReplicaGuard; - -enum Status { - Pending = tc::ffi::TC_STATUS_PENDING, - Completed = tc::ffi::TC_STATUS_COMPLETED, - Deleted = tc::ffi::TC_STATUS_DELETED, - Recurring = tc::ffi::TC_STATUS_RECURRING, - Unknown = tc::ffi::TC_STATUS_UNKNOWN, -}; - -// a unique_ptr to a TCReplica which will automatically free the value when -// it goes out of scope. -using unique_tctask_ptr = std::unique_ptr>; - -// Task wraps the TCTask type, managing its memory, errors, and so on. -// -// Except as noted, method names match the suffix to `tc_task_..`. -class Task { - protected: - // Tasks may only be created and made mutable/immutable - // by tc::Replica - friend class tc::Replica; - explicit Task(tc::ffi::TCTask *); - - // RplicaGuard handles mut/immut - friend class tc::ReplicaGuard; - void to_mut(tc::ffi::TCReplica *); - void to_immut(); - - public: - // This object "owns" inner, so copy is not allowed. - Task(const Task &) = delete; - Task &operator=(const Task &) = delete; - - // Explicit move constructor and assignment - Task(Task &&) noexcept; - Task &operator=(Task &&) noexcept; - - std::string get_uuid() const; - Status get_status() const; - std::map get_taskmap() const; - std::string get_description() const; - std::optional get_value(std::string property) const; - // TODO: time_t tc_task_get_entry(struct TCTask *task); - // TODO: time_t tc_task_get_wait(struct TCTask *task); - // TODO: time_t tc_task_get_modified(struct TCTask *task); - bool is_waiting() const; - bool is_active() const; - bool is_blocked() const; - bool is_blocking() const; - // TODO: bool tc_task_has_tag(struct TCTask *task, struct TCString tag); - // TODO: struct TCStringList tc_task_get_tags(struct TCTask *task); - // TODO: struct TCAnnotationList tc_task_get_annotations(struct TCTask *task); - // TODO: struct TCString tc_task_get_uda(struct TCTask *task, struct TCString ns, struct TCString - // key); - // TODO: struct TCString tc_task_get_legacy_uda(struct TCTask *task, struct TCString key); - // TODO: struct TCUdaList tc_task_get_udas(struct TCTask *task); - // TODO: struct TCUdaList tc_task_get_legacy_udas(struct TCTask *task); - void set_status(Status status); - // TODO: TCResult tc_task_set_description(struct TCTask *task, struct TCString description); - void set_value(std::string property, std::optional value); - // TODO: TCResult tc_task_set_entry(struct TCTask *task, time_t entry); - // TODO: TCResult tc_task_set_wait(struct TCTask *task, time_t wait); - void set_modified(time_t modified); - // TODO: TCResult tc_task_start(struct TCTask *task); - // TODO: TCResult tc_task_stop(struct TCTask *task); - // TODO: TCResult tc_task_done(struct TCTask *task); - // TODO: TCResult tc_task_delete(struct TCTask *task); - // TODO: TCResult tc_task_add_tag(struct TCTask *task, struct TCString tag); - // TODO: TCResult tc_task_remove_tag(struct TCTask *task, struct TCString tag); - // TODO: TCResult tc_task_add_annotation(struct TCTask *task, struct TCAnnotation *annotation); - // TODO: TCResult tc_task_remove_annotation(struct TCTask *task, int64_t entry); - // TODO: TCResult tc_task_set_uda(struct TCTask *task, - // TODO: TCResult tc_task_remove_uda(struct TCTask *task, struct TCString ns, struct TCString - // key); - // TODO: TCResult tc_task_set_legacy_uda(struct TCTask *task, struct TCString key, struct TCString - // value); - // TODO: TCResult tc_task_remove_legacy_uda(struct TCTask *task, struct TCString key); - - private: - unique_tctask_ptr inner; - - std::string task_error() const; // tc_task_error -}; -} // namespace tc - -// TODO: struct TCTask *tc_task_list_take(struct TCTaskList *tasks, size_t index); -// TODO: void tc_task_list_free(struct TCTaskList *tasks); - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/WorkingSet.cpp b/src/tc/WorkingSet.cpp deleted file mode 100644 index 1adeb8f9d..000000000 --- a/src/tc/WorkingSet.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#include -// cmake.h include header must come first - -#include - -#include "tc/Task.h" -#include "tc/WorkingSet.h" -#include "tc/util.h" - -using namespace tc::ffi; - -//////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet::WorkingSet(WorkingSet&& other) noexcept { - // move inner from other - inner = unique_tcws_ptr(other.inner.release(), [](TCWorkingSet* ws) { tc_working_set_free(ws); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet& tc::WorkingSet::operator=(WorkingSet&& other) noexcept { - if (this != &other) { - // move inner from other - inner = - unique_tcws_ptr(other.inner.release(), [](TCWorkingSet* ws) { tc_working_set_free(ws); }); - } - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet::WorkingSet(tc::ffi::TCWorkingSet* tcws) { - inner = unique_tcws_ptr(tcws, [](TCWorkingSet* ws) { tc_working_set_free(ws); }); -} - -//////////////////////////////////////////////////////////////////////////////// -size_t tc::WorkingSet::len() const noexcept { return tc_working_set_len(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -size_t tc::WorkingSet::largest_index() const noexcept { - return tc_working_set_largest_index(&*inner); -} - -//////////////////////////////////////////////////////////////////////////////// -std::optional tc::WorkingSet::by_index(size_t index) const noexcept { - TCUuid uuid; - if (tc_working_set_by_index(&*inner, index, &uuid)) { - return std::make_optional(tc2uuid(uuid)); - } else { - return std::nullopt; - } -} - -//////////////////////////////////////////////////////////////////////////////// -std::optional tc::WorkingSet::by_uuid(const std::string& uuid) const noexcept { - auto index = tc_working_set_by_uuid(&*inner, uuid2tc(uuid)); - if (index > 0) { - return std::make_optional(index); - } else { - return std::nullopt; - } -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/WorkingSet.h b/src/tc/WorkingSet.h deleted file mode 100644 index e9a3a5d5c..000000000 --- a/src/tc/WorkingSet.h +++ /dev/null @@ -1,74 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_WORKINGSET -#define INCLUDED_TC_WORKINGSET - -#include -#include -#include -#include - -#include "tc/Task.h" -#include "tc/ffi.h" - -namespace tc { -class Task; - -// a unique_ptr to a TCWorkingSet which will automatically free the value when -// it goes out of scope. -using unique_tcws_ptr = - std::unique_ptr>; - -// WorkingSet wraps the TCWorkingSet type, managing its memory, errors, and so on. -// -// Except as noted, method names match the suffix to `tc_working_set_..`. -class WorkingSet { - protected: - friend class tc::Replica; - WorkingSet(tc::ffi::TCWorkingSet *); // via tc_replica_working_set - - public: - // This object "owns" inner, so copy is not allowed. - WorkingSet(const WorkingSet &) = delete; - WorkingSet &operator=(const WorkingSet &) = delete; - - // Explicit move constructor and assignment - WorkingSet(WorkingSet &&) noexcept; - WorkingSet &operator=(WorkingSet &&) noexcept; - - size_t len() const noexcept; // tc_working_set_len - size_t largest_index() const noexcept; // tc_working_set_largest_index - std::optional by_index(size_t index) const noexcept; // tc_working_set_by_index - std::optional by_uuid(const std::string &index) const noexcept; // tc_working_set_by_uuid - - private: - unique_tcws_ptr inner; -}; -} // namespace tc - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/ffi.h b/src/tc/ffi.h deleted file mode 100644 index 213fc4074..000000000 --- a/src/tc/ffi.h +++ /dev/null @@ -1,36 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell, Tomas Babej, Paul Beckingham, Federico Hernandez. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_FFI -#define INCLUDED_TC_FFI - -// The entire FFI API is embedded in the `tc::ffi` namespace -namespace tc::ffi { -#include -} - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/util.cpp b/src/tc/util.cpp deleted file mode 100644 index 3339615ce..000000000 --- a/src/tc/util.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#include -// cmake.h include header must come first - -#include -#include - -#include "tc/Replica.h" -#include "tc/Task.h" - -using namespace tc::ffi; - -namespace tc { -//////////////////////////////////////////////////////////////////////////////// -TCString string2tc(const std::string& str) { - return tc_string_clone_with_len(str.data(), str.size()); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc2string_clone(const TCString& str) { - size_t len; - auto ptr = tc_string_content_with_len(&str, &len); - auto rv = std::string(ptr, len); - return rv; -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc2string(TCString& str) { - auto rv = tc2string_clone(str); - tc_string_free(&str); - return rv; -} - -//////////////////////////////////////////////////////////////////////////////// -TCUuid uuid2tc(const std::string& str) { - TCString tcstr = tc_string_borrow(str.c_str()); - TCUuid rv; - if (TC_RESULT_OK != tc_uuid_from_str(tcstr, &rv)) { - throw std::string("invalid UUID"); - } - return rv; -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc2uuid(TCUuid& uuid) { - char s[TC_UUID_STRING_BYTES]; - tc_uuid_to_buf(uuid, s); - std::string str; - str.assign(s, TC_UUID_STRING_BYTES); - return str; -} - -//////////////////////////////////////////////////////////////////////////////// -} // namespace tc diff --git a/src/tc/util.h b/src/tc/util.h deleted file mode 100644 index 87d54cd16..000000000 --- a/src/tc/util.h +++ /dev/null @@ -1,52 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_UTIL -#define INCLUDED_TC_UTIL - -#include - -#include "tc/ffi.h" - -namespace tc { -// convert a std::string into a TCString, copying the contained data -tc::ffi::TCString string2tc(const std::string&); - -// convert a TCString into a std::string, leaving the TCString as-is -std::string tc2string_clone(const tc::ffi::TCString&); - -// convert a TCString into a std::string, freeing the TCString -std::string tc2string(tc::ffi::TCString&); - -// convert a TCUuid into a std::string -std::string tc2uuid(tc::ffi::TCUuid&); - -// parse a std::string into a TCUuid (throwing if parse fails) -tc::ffi::TCUuid uuid2tc(const std::string&); -} // namespace tc - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tmp.cpp b/src/tmp.cpp index 60d67161b..16264d664 100644 --- a/src/tmp.cpp +++ b/src/tmp.cpp @@ -24,7 +24,7 @@ // //////////////////////////////////////////////////////////////////////////////// -#include "taskchampion-cpp/lib.h" +#include #include #include #include diff --git a/test/tc.test.cpp b/test/tc.test.cpp index e3351e101..34d3c8593 100644 --- a/test/tc.test.cpp +++ b/test/tc.test.cpp @@ -42,6 +42,8 @@ int main(int, char**) { UnitTest t(23); + // TODO: replace + // This function contains unit tests for the various bits of the wrappers for // taskchampion-lib (that is, for `src/tc/*.cpp`).