From 8c30400af3e85b08978525582ecba947e3dd2db3 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sat, 12 Mar 2022 22:07:42 +0000 Subject: [PATCH] Add a C++ wrapper around TC FFI This uses CMake to build a simple Rust library (in `src/tc/rust`) that just re-exports everything from the `taskchampion-lib` crate. The C++ wrappers then wrap this into C++ objects with proper lifecycle maintenance, in the `tc` namespace. The C++ wrappers are incomplete, and missing methods are tagged with "TODO". These will be added as needed. --- .gitignore | 1 + .gitmodules | 6 + CMakeLists.txt | 13 +- Cargo.toml | 3 + README.md | 3 +- cmake/CMakeRust | 1 + docs/contrib/branching.md | 2 +- docs/contrib/rust-and-c++.md | 29 ++ docs/index.md | 35 +- rust/lib/Cargo.toml | 6 +- src/CMakeLists.txt | 8 +- src/columns/CMakeLists.txt | 2 + src/commands/CMakeLists.txt | 2 + src/taskchampion | 1 + src/tc/CMakeLists.txt | 16 + src/tc/Replica.cpp | 162 ++++++++ src/tc/Replica.h | 85 +++++ src/tc/Task.cpp | 115 ++++++ src/tc/Task.h | 119 ++++++ src/tc/WorkingSet.cpp | 98 +++++ src/tc/WorkingSet.h | 75 ++++ src/tc/ffi.h | 36 ++ src/tc/rust/.gitignore | 3 + src/tc/rust/CMakeLists.txt | 1 + src/tc/rust/Cargo.lock | 708 +++++++++++++++++++++++++++++++++++ src/tc/rust/Cargo.toml | 9 + src/tc/rust/src/lib.rs | 2 + src/tc/util.cpp | 81 ++++ src/tc/util.h | 51 +++ test/.gitignore | 1 + test/CMakeLists.txt | 5 +- test/tc.t.cpp | 111 ++++++ 32 files changed, 1760 insertions(+), 30 deletions(-) create mode 160000 cmake/CMakeRust create mode 100644 docs/contrib/rust-and-c++.md create mode 160000 src/taskchampion create mode 100644 src/tc/CMakeLists.txt create mode 100644 src/tc/Replica.cpp create mode 100644 src/tc/Replica.h create mode 100644 src/tc/Task.cpp create mode 100644 src/tc/Task.h create mode 100644 src/tc/WorkingSet.cpp create mode 100644 src/tc/WorkingSet.h create mode 100644 src/tc/ffi.h create mode 100644 src/tc/rust/.gitignore create mode 100644 src/tc/rust/CMakeLists.txt create mode 100644 src/tc/rust/Cargo.lock create mode 100644 src/tc/rust/Cargo.toml create mode 100644 src/tc/rust/src/lib.rs create mode 100644 src/tc/util.cpp create mode 100644 src/tc/util.h create mode 100644 test/tc.t.cpp diff --git a/.gitignore b/.gitignore index 425a4922b..c14ced1f8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ src/task src/taskd src/libtask.a src/commands/libcommands.a +src/tc/libtc.a src/columns/libcolumns.a *~ .*.swp diff --git a/.gitmodules b/.gitmodules index fed3de512..cb3fc7bae 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ [submodule "src/libshared"] path = src/libshared url = https://github.com/GothenburgBitFactory/libshared.git +[submodule "cmake/CMakeRust"] + path = cmake/CMakeRust + url = https://github.com/Devolutions/CMakeRust +[submodule "src/taskchampion"] + path = src/taskchampion + url = https://github.com/taskchampion/taskchampion diff --git a/CMakeLists.txt b/CMakeLists.txt index cc8005aa0..079160b7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required (VERSION 3.0) -set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") +set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_SOURCE_DIR}/cmake/CMakeRust/cmake") +enable_language (Rust) +include (CMakeCargo) include (CheckFunctionExists) include (CheckStructHasMember) @@ -66,6 +68,9 @@ SET (TASK_DOCDIR share/doc/task CACHE STRING "Installation directory for doc fi SET (TASK_RCDIR "${TASK_DOCDIR}/rc" CACHE STRING "Installation directory for configuration files") SET (TASK_BINDIR bin CACHE STRING "Installation directory for the binary") +# rust libs require these +set (TASK_LIBRARIES dl pthread) + if (USE_GNUTLS) message ("-- Looking for GnuTLS") find_package (GnuTLS) @@ -150,6 +155,8 @@ configure_file ( add_subdirectory (src) add_subdirectory (src/commands) +add_subdirectory (src/tc) +add_subdirectory (src/tc/rust) add_subdirectory (src/columns) add_subdirectory (doc) add_subdirectory (scripts) @@ -185,7 +192,7 @@ set (CPACK_SOURCE_IGNORE_FILES "CMakeCache" "CMakeFiles" "CPackConfig" "CPackSo "_CPack_Packages" "cmake_install" "install_manifest" "Makefile$" "test" "package-config" "misc/*" "src/task$" "src/calc$" "performance" "src/libtask.a" "src/columns/libcolumns.a" "src/commands/libcommands.a" - "swp$" "src/lex$" "task-.*.tar.gz" "commit.h" "cmake.h$" "\\\\.gitmodules" - "src/libshared/\\\\.git" ".github/" ".*\\\\.gitignore$" + "src/tc/libtc.a" "swp$" "src/lex$" "task-.*.tar.gz" "commit.h" "cmake.h$" + "\\\\.gitmodules" "src/libshared/\\\\.git" ".github/" ".*\\\\.gitignore$" "src/liblibshared.a" "docker-compose.yml" "\\\\.git/") include (CPack) diff --git a/Cargo.toml b/Cargo.toml index 51108cbb5..94ef9f137 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,6 @@ members = [ "rust/integration-tests", "rust/xtask", ] + +# src/tc/rust is just part of the TW build and not a public crate +exclude = [ "src/tc/rust" ] diff --git a/README.md b/README.md index 76f142315..57d197f20 100644 --- a/README.md +++ b/README.md @@ -112,9 +112,10 @@ Then build: Your contributions are especially welcome. Whether it comes in the form of code patches, ideas, discussion, bug reports, encouragement or criticism, your input is needed. - Visit [Github](https://github.com/GothenburgBitFactory/taskwarrior) and participate in the future of Taskwarrior. +See further development documentation in [`docs/`](./docs). + ## Sponsoring [![GitHub Sponsors](https://img.shields.io/github/sponsors/GothenburgBitFactory?color=green)](https://github.com/sponsors/GothenburgBitFactory/) diff --git a/cmake/CMakeRust b/cmake/CMakeRust new file mode 160000 index 000000000..9a4a7a19b --- /dev/null +++ b/cmake/CMakeRust @@ -0,0 +1 @@ +Subproject commit 9a4a7a19b41e58deef5efc9a7ba53a69bc40449b diff --git a/docs/contrib/branching.md b/docs/contrib/branching.md index 15d87c0d0..ce691e370 100644 --- a/docs/contrib/branching.md +++ b/docs/contrib/branching.md @@ -11,7 +11,7 @@ Taskwarrior and Taskserver use the same branching model. ## Git Branching Git allows arbitrary and low-cost branching, which means that any branching model can be used. -A new Git repository has one branch, the default branch, named `master`, but even this is not required. +A new Git repository has one branch, the default branch, named `stable`, but even this is not required. [![master](master.png)](master.png) diff --git a/docs/contrib/rust-and-c++.md b/docs/contrib/rust-and-c++.md new file mode 100644 index 000000000..6c7840105 --- /dev/null +++ b/docs/contrib/rust-and-c++.md @@ -0,0 +1,29 @@ +# Rust and C++ + +Taskwarrior has historically been a C++ project, but as part of an [ongoing effort to replace its storage backend](https://github.com/GothenburgBitFactory/taskwarrior/issues/2770) it now includes a Rust library called TaskChampion. +To develop Taskwarrior, you will need both a [C++ compiler and tools](./development.md), and a [Rust toolchain](https://www.rust-lang.org/tools/install). +However, most tasks will only require you to be familiar with one of the two languages. + +## TaskChampion + +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. +TaskChampion provides a C interface via the `taskchampion-lib` crate. + +Other applications, besides Taskwarrior, can use TaskChampion to manage tasks. +Applications written in Rust can use the `taskchampion` crate, while those in other languages may use the `taskchampion-lib` crate. +Taskwarrior is just one application using the TaskChampion interface. + +You can build Taskchampion locally by simply running `cargo build` in the root of this repository. +The implementation, including more documentation, is in the [`rust`](../../rust) subdirectory. + +## Taskwarrior's use of TaskChampion + +Taskwarrior's interface to TaskChampion has a few laters: + +* The skeletal Rust crate in [`src/tc/rust`](../../src/tc/rust) brings the symbols from `taskchampion-lib` under CMake's management. + The corresponding header file is included from [`rust/lib`](../../rust/lib). + All of these symbols are placed in the C++ namespace, `tc::ffi`. +* C++ wrappers for the types from `taskchampion-lib` are 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/docs/index.md b/docs/index.md index d9850f408..f228bf5f7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,12 +7,13 @@ For all other documenation, see https://taskwarrior.org. ## Contributing to Taskwarrior - * [How to become an Open Source Contributor](./contrib/first_time) - * [Contributing to Taskwarrior](./contrib/contribute) - * [Developing Taskwarrior](./contrib/development) - * [Building Taskwarrior](./contrib/build) - * [Coding Style](./contrib/coding_style) - * [Branching Model](./contrib/branching) + * [How to become an Open Source Contributor](./contrib/first_time.md) + * [Contributing to Taskwarrior](./contrib/contribute.md) + * [Developing Taskwarrior](./contrib/development.md) + * [Building Taskwarrior](./contrib/build.md) + * [Coding Style](./contrib/coding_style.md) + * [Branching Model](./contrib/branching.md) + * [Rust and C++](./contrib/rust-and-c++.md) ## RFC's @@ -21,14 +22,14 @@ This is where design documents (RFCs) are kept. Although these documents are less formal than [IETF RFCs](https://www.ietf.org/rfc) they serve a similar purpose. These documents apply only to the Taskwarrior family of products, and are placed here to invite comment before designs finalize. - - [General Plans](./rfcs/plans) - - [Rules System](./rfcs/rules) - - [Full DOM Support ](./rfcs/dom) - - [Work Week Support](./rfcs/workweek) - - [Recurrence](./rfcs/recurrence) - - [Taskwarrior JSON Format](./rfcs/task) - - [CLI Updates ](./rfcs/cli) - - [Taskserver Sync Protocol](./rfcs/protocol) - - [Taskserver Message Format](./rfcs/request) - - [Taskserver Sync Algorithm](./rfcs/sync) - - [Taskserver Client](./rfcs/client) + - [General Plans](./rfcs/plans.md) + - [Rules System](./rfcs/rules.md) + - [Full DOM Support ](./rfcs/dom.md) + - [Work Week Support](./rfcs/workweek.md) + - [Recurrence](./rfcs/recurrence.md) + - [Taskwarrior JSON Format](./rfcs/task.md) + - [CLI Updates ](./rfcs/cli.md) + - [Taskserver Sync Protocol](./rfcs/protocol.md) + - [Taskserver Message Format](./rfcs/request.md) + - [Taskserver Sync Algorithm](./rfcs/sync.md) + - [Taskserver Client](./rfcs/client.md) diff --git a/rust/lib/Cargo.toml b/rust/lib/Cargo.toml index 4a5191e27..f330ba89b 100644 --- a/rust/lib/Cargo.toml +++ b/rust/lib/Cargo.toml @@ -3,9 +3,9 @@ name = "taskchampion-lib" version = "0.1.0" edition = "2018" -[lib] -name = "taskchampionlib" -crate-type = ["staticlib", "cdylib", "rlib"] +#[lib] +#name = "taskchampionlib" +#crate-type = ["staticlib", "cdylib", "rlib"] [dependencies] libc = "0.2.126" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 34cf9b487..001745075 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,11 @@ cmake_minimum_required (VERSION 3.0) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/tc ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src + ${CMAKE_SOURCE_DIR}/rust/lib ${TASK_INCLUDE_DIRS}) add_library (task STATIC CLI2.cpp CLI2.h @@ -50,9 +52,9 @@ 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. -target_link_libraries (task_executable task commands columns libshared task libshared ${TASK_LIBRARIES}) -target_link_libraries (calc_executable task commands columns libshared task libshared ${TASK_LIBRARIES}) -target_link_libraries (lex_executable task commands columns libshared task libshared ${TASK_LIBRARIES}) +target_link_libraries (task_executable task tc tc-rust commands columns libshared task libshared ${TASK_LIBRARIES}) +target_link_libraries (calc_executable task tc tc-rust commands columns libshared task libshared ${TASK_LIBRARIES}) +target_link_libraries (lex_executable task tc tc-rust commands columns libshared task libshared ${TASK_LIBRARIES}) set_property (TARGET task_executable PROPERTY OUTPUT_NAME "task") diff --git a/src/columns/CMakeLists.txt b/src/columns/CMakeLists.txt index 79fc10af0..67bb00e4a 100644 --- a/src/columns/CMakeLists.txt +++ b/src/columns/CMakeLists.txt @@ -1,9 +1,11 @@ cmake_minimum_required (VERSION 3.0) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/tc ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src + ${CMAKE_SOURCE_DIR}/rust/lib ${TASK_INCLUDE_DIRS}) set (columns_SRCS Column.cpp Column.h diff --git a/src/commands/CMakeLists.txt b/src/commands/CMakeLists.txt index 14f640ee2..2d63b6097 100644 --- a/src/commands/CMakeLists.txt +++ b/src/commands/CMakeLists.txt @@ -1,9 +1,11 @@ cmake_minimum_required (VERSION 3.0) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/tc ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src + ${CMAKE_SOURCE_DIR}/rust/lib ${TASK_INCLUDE_DIRS}) set (commands_SRCS Command.cpp Command.h diff --git a/src/taskchampion b/src/taskchampion new file mode 160000 index 000000000..1631f29b8 --- /dev/null +++ b/src/taskchampion @@ -0,0 +1 @@ +Subproject commit 1631f29b8aed9002a7cf6dd605c83d9f0181e66d diff --git a/src/tc/CMakeLists.txt b/src/tc/CMakeLists.txt new file mode 100644 index 000000000..82b07bb12 --- /dev/null +++ b/src/tc/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required (VERSION 3.0) +include_directories (${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/tc + ${CMAKE_SOURCE_DIR}/src/libshared/src + ${CMAKE_SOURCE_DIR}/rust/lib + ${TASK_INCLUDE_DIRS}) + +set (tc_SRCS + ffi.h + util.cpp util.h + Replica.cpp Replica.h + WorkingSet.cpp WorkingSet.h + Task.cpp Task.h) + +add_library (tc STATIC ${tc_SRCS}) diff --git a/src/tc/Replica.cpp b/src/tc/Replica.cpp new file mode 100644 index 000000000..08037fd5c --- /dev/null +++ b/src/tc/Replica.cpp @@ -0,0 +1,162 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include "tc/Replica.h" +#include "tc/Task.h" +#include "tc/WorkingSet.h" +#include "tc/util.h" + +using namespace tc::ffi; + +//////////////////////////////////////////////////////////////////////////////// +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) +{ + TCString path = tc_string_borrow (dir.c_str ()); + TCString error; + auto tcreplica = tc_replica_new_on_disk (path, &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); +} + +//////////////////////////////////////////////////////////////////////////////// +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 () +{ + auto res = tc_replica_rebuild_working_set (&*inner, true); + if (res != TC_RESULT_OK) { + throw replica_error (); + } +} + +//////////////////////////////////////////////////////////////////////////////// +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 new file mode 100644 index 000000000..d0650350b --- /dev/null +++ b/src/tc/Replica.h @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 "tc/ffi.h" +#include "tc/Task.h" + +namespace tc { + class Task; + class WorkingSet; + + // 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< + tc::ffi::TCReplica, + std::function>; + + // 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); // 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); +// TODO: struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid); +// TODO: TCResult tc_replica_sync(struct TCReplica *rep, struct TCServer *server, bool avoid_snapshots); +// TODO: TCResult tc_replica_undo(struct TCReplica *rep, int32_t *undone_out); +// TODO: TCResult tc_replica_add_undo_point(struct TCReplica *rep, bool force); + void rebuild_working_set (); + private: + 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); + }; +} + +#endif +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Task.cpp b/src/tc/Task.cpp new file mode 100644 index 000000000..886154dbb --- /dev/null +++ b/src/tc/Task.cpp @@ -0,0 +1,115 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 +#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; +} + +//////////////////////////////////////////////////////////////////////////////// +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::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 new file mode 100644 index 000000000..6eebb18fa --- /dev/null +++ b/src/tc/Task.h @@ -0,0 +1,119 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 "tc/ffi.h" + +namespace tc { + class Replica; + + enum Status { + Pending = tc::ffi::TC_STATUS_PENDING, + Completed = tc::ffi::TC_STATUS_COMPLETED, + Deleted = tc::ffi::TC_STATUS_DELETED, + 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< + tc::ffi::TCTask, + std::function>; + + + // 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 by tc::Replica + friend class tc::Replica; + explicit Task (tc::ffi::TCTask *); + + 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; + +// TODO: void tc_task_to_mut(struct TCTask *task, struct TCReplica *tcreplica); +// TODO: void tc_task_to_immut(struct TCTask *task); + std::string get_uuid () const; + Status get_status () const; + std::map get_taskmap() const; + std::string get_description() 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); +// TODO: bool tc_task_is_waiting(struct TCTask *task); +// TODO: bool tc_task_is_active(struct TCTask *task); +// 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); +// TODO: TCResult tc_task_set_status(struct TCTask *task, enum TCStatus status); +// TODO: TCResult tc_task_set_description(struct TCTask *task, struct TCString description); +// TODO: TCResult tc_task_set_entry(struct TCTask *task, time_t entry); +// TODO: TCResult tc_task_set_wait(struct TCTask *task, time_t wait); +// TODO: TCResult tc_task_set_modified(struct TCTask *task, 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 + }; +} + +// 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 new file mode 100644 index 000000000..4f116aaa5 --- /dev/null +++ b/src/tc/WorkingSet.cpp @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include "tc/WorkingSet.h" +#include "tc/Task.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 new file mode 100644 index 000000000..7b6c63423 --- /dev/null +++ b/src/tc/WorkingSet.h @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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/ffi.h" +#include "tc/Task.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< + tc::ffi::TCWorkingSet, + std::function>; + + // 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; + }; +} + +#endif +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/ffi.h b/src/tc/ffi.h new file mode 100644 index 000000000..213fc4074 --- /dev/null +++ b/src/tc/ffi.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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/rust/.gitignore b/src/tc/rust/.gitignore new file mode 100644 index 000000000..176bb9b27 --- /dev/null +++ b/src/tc/rust/.gitignore @@ -0,0 +1,3 @@ +target +x86_64-unknown-linux-gnu +.rustc_info.json diff --git a/src/tc/rust/CMakeLists.txt b/src/tc/rust/CMakeLists.txt new file mode 100644 index 000000000..a08539e2a --- /dev/null +++ b/src/tc/rust/CMakeLists.txt @@ -0,0 +1 @@ +cargo_build(NAME tc-rust) diff --git a/src/tc/rust/Cargo.lock b/src/tc/rust/Cargo.lock new file mode 100644 index 000000000..f6426db26 --- /dev/null +++ b/src/tc/rust/Cargo.lock @@ -0,0 +1,708 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "anyhow" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "serde", + "time", + "winapi", +] + +[[package]] +name = "chunked_transfer" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "libsqlite3-sys" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rusqlite" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "memchr", + "smallvec", +] + +[[package]] +name = "rustls" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustversion" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum_macros" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4faebde00e8ff94316c01800f9054fd2ba77d30d9e922541913051d1d978918b" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "taskchampion" +version = "0.4.1" +dependencies = [ + "anyhow", + "byteorder", + "chrono", + "flate2", + "log", + "ring", + "rusqlite", + "serde", + "serde_json", + "strum", + "strum_macros", + "thiserror", + "ureq", + "uuid", +] + +[[package]] +name = "taskchampion-lib" +version = "0.1.0" +dependencies = [ + "anyhow", + "libc", + "taskchampion", +] + +[[package]] +name = "tc-rust" +version = "0.1.0" +dependencies = [ + "taskchampion-lib", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "ureq" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9399fa2f927a3d327187cbd201480cee55bee6ac5d3c77dd27f0c6814cff16d5" +dependencies = [ + "base64", + "chunked_transfer", + "flate2", + "log", + "once_cell", + "rustls", + "url", + "webpki", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" + +[[package]] +name = "web-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/src/tc/rust/Cargo.toml b/src/tc/rust/Cargo.toml new file mode 100644 index 000000000..ec633ad44 --- /dev/null +++ b/src/tc/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "tc-rust" +version = "0.1.0" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +taskchampion-lib = {path = "../../../rust/lib"} diff --git a/src/tc/rust/src/lib.rs b/src/tc/rust/src/lib.rs new file mode 100644 index 000000000..ed0fd295c --- /dev/null +++ b/src/tc/rust/src/lib.rs @@ -0,0 +1,2 @@ +extern crate taskchampion_lib; +pub use taskchampion_lib::*; diff --git a/src/tc/util.cpp b/src/tc/util.cpp new file mode 100644 index 000000000..567c4a05b --- /dev/null +++ b/src/tc/util.cpp @@ -0,0 +1,81 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 +#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; +} + +//////////////////////////////////////////////////////////////////////////////// +} diff --git a/src/tc/util.h b/src/tc/util.h new file mode 100644 index 000000000..66ca54b2e --- /dev/null +++ b/src/tc/util.h @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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&); +} + +#endif +//////////////////////////////////////////////////////////////////////////////// diff --git a/test/.gitignore b/test/.gitignore index b3803c61f..44723cd77 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -34,6 +34,7 @@ variant_partial.t variant_subtract.t variant_xor.t view.t +tc.t tw-2689.t json_test diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 21b7dc444..75a7f601f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,9 +14,10 @@ include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src ${CMAKE_SOURCE_DIR}/test + ${CMAKE_SOURCE_DIR}/rust/lib ${TASK_INCLUDE_DIRS}) -set (test_SRCS col.t dom.t eval.t lexer.t t.t tw-2689.t tdb2.t util.t variant_add.t variant_and.t variant_cast.t variant_divide.t variant_equal.t variant_exp.t variant_gt.t variant_gte.t variant_inequal.t variant_lt.t variant_lte.t variant_match.t variant_math.t variant_modulo.t variant_multiply.t variant_nomatch.t variant_not.t variant_or.t variant_partial.t variant_subtract.t variant_xor.t view.t) +set (test_SRCS col.t dom.t eval.t lexer.t t.t tw-2689.t tdb2.t tc.t util.t variant_add.t variant_and.t variant_cast.t variant_divide.t variant_equal.t variant_exp.t variant_gt.t variant_gte.t variant_inequal.t variant_lt.t variant_lte.t variant_match.t variant_math.t variant_modulo.t variant_multiply.t variant_nomatch.t variant_not.t variant_or.t variant_partial.t variant_subtract.t variant_xor.t view.t) add_custom_target (test ./run_all --verbose DEPENDS ${test_SRCS} task_executable @@ -27,7 +28,7 @@ add_custom_target (build_tests DEPENDS ${test_SRCS} foreach (src_FILE ${test_SRCS}) add_executable (${src_FILE} "${src_FILE}.cpp" test.cpp) - target_link_libraries (${src_FILE} task commands columns libshared task commands columns libshared task commands columns libshared ${TASK_LIBRARIES}) + target_link_libraries (${src_FILE} task tc commands columns libshared task tc tc-rust commands columns libshared task commands columns libshared ${TASK_LIBRARIES}) endforeach (src_FILE) configure_file(run_all run_all COPYONLY) diff --git a/test/tc.t.cpp b/test/tc.t.cpp new file mode 100644 index 000000000..77f980cb2 --- /dev/null +++ b/test/tc.t.cpp @@ -0,0 +1,111 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include +#include +#include "test.h" +#include "tc/Replica.h" +#include "tc/WorkingSet.h" +#include "tc/Task.h" +#include "tc/util.h" + +//////////////////////////////////////////////////////////////////////////////// +int main (int, char**) +{ + UnitTest t (21); + + // This function contains unit tests for the various bits of the wrappers for + // taskchampion-lib (that is, for `src/tc/*.cpp`). + + //// util + + { + auto s1 = std::string ("a\0string!"); + auto stc = tc::string2tc (s1); + auto s2 = tc::tc2string (stc); + t.is (s1, s2, "round-trip to tc string and back (containing an embedded NUL)"); + } + + { + auto s1 = std::string ("62123ec9-c443-4f7e-919a-35362a8bef8d"); + auto tcuuid = tc::uuid2tc (s1); + auto s2 = tc::tc2uuid (tcuuid); + t.is(s1, s2, "round-trip to TCUuid and back"); + } + + //// Replica + + auto rep = tc::Replica (); + t.pass ("replica constructed"); + + auto maybe_task = rep.get_task("24478a28-4609-4257-bc19-44ec51391431"); + t.notok(maybe_task.has_value(), "task with fixed uuid does not exist"); + + auto task = rep.new_task (tc::Status::Pending, "a test"); + t.pass ("new task constructed"); + t.is (task.get_description (), std::string ("a test"), "task description round-trip"); + t.is (task.get_status (), tc::Status::Pending, "task status round-trip"); + + auto uuid = task.get_uuid(); + + auto maybe_task2 = rep.get_task (uuid); + t.ok(maybe_task2.has_value(), "task lookup by uuid finds task"); + t.is ((*maybe_task2).get_description (), std::string ("a test"), "task description round-trip"); + + rep.rebuild_working_set (); + t.pass ("rebuild_working_set"); + + auto tasks = rep.all_tasks (); + t.is ((int)tasks.size(), 1, "all_tasks returns one task"); + + //// Task + + task = std::move(tasks[0]); + + t.is (task.get_uuid(), uuid, "returned task has correct uuid"); + t.is (task.get_status(), tc::Status::Pending, "returned task is pending"); + auto map = task.get_taskmap (); + t.is (map["description"], "a test", "task description in taskmap"); + t.is (task.get_description(), "a test", "returned task has correct description"); + + //// WorkingSet + + auto ws = rep.working_set (); + + t.is (ws.len (), (size_t)1, "WorkingSet::len"); + t.is (ws.largest_index (), (size_t)1, "WorkingSet::largest_index"); + t.is (ws.by_index (1).value(), uuid, "WorkingSet::by_index"); + t.is (ws.by_index (2).has_value(), false, "WorkingSet::by_index for unknown index"); + t.is (ws.by_uuid (uuid).value (), (size_t)1, "WorkingSet::by_uuid"); + t.is (ws.by_uuid ("3e18a306-e3a8-4a53-a85c-fa7c057759a2").has_value (), false, "WorkingSet::by_uuid for unknown uuid"); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +