Use Taskchampion to store Taskwarrior data

This replaces the TF2 task files with a TaskChampion replica.
This commit is contained in:
Dustin J. Mitchell
2022-12-15 02:52:47 +00:00
committed by Dustin J. Mitchell
parent 4b814bc602
commit 5bb9857984
24 changed files with 537 additions and 1362 deletions

View File

@@ -560,8 +560,8 @@ int Context::initialize (int argc, const char** argv)
if (taskdata_overridden && verbose ("override"))
header (format ("TASKDATA override: {1}", data_dir._data));
tdb2.set_location (data_dir);
createDefaultConfig ();
bool create_if_missing = !config.getBoolean ("exit.on.missing.db");
tdb2.open_replica (data_dir, create_if_missing);
////////////////////////////////////////////////////////////////////////////
//
@@ -1254,23 +1254,6 @@ void Context::createDefaultConfig ()
if (! File::write (rc_file._data, contents.str ()))
throw format ("Could not write to '{1}'.", rc_file._data);
}
// Create data location, if necessary.
Directory d (data_dir);
if (! d.exists ())
{
if (config.getBoolean ("exit.on.missing.db"))
throw std::string ("Error: rc.data.location does not exist - exiting according to rc.exit.on.missing.db setting.");
d.create ();
if (config.has ("hooks.location"))
d = Directory (config.get ("hooks.location"));
else
d += "hooks";
d.create ();
}
}
////////////////////////////////////////////////////////////////////////////////

File diff suppressed because it is too large Load Diff

View File

@@ -35,71 +35,8 @@
#include <stdio.h>
#include <FS.h>
#include <Task.h>
// TF2 Class represents a single file in the task database.
class TF2
{
public:
TF2 ();
~TF2 ();
void target (const std::string&);
const std::vector <Task>& get_tasks ();
const std::vector <std::string>& get_lines ();
bool get (int, Task&);
bool get (const std::string&, Task&);
bool has (const std::string&);
void add_task (Task&);
bool modify_task (const Task&);
bool purge_task (const Task&);
void add_line (const std::string&);
void clear_tasks ();
void clear_lines ();
void commit ();
Task load_task (const std::string&);
void load_gc (Task&);
void load_tasks (bool from_gc = false);
void load_lines ();
// ID <--> UUID mapping.
std::string uuid (int);
int id (const std::string&);
void has_ids ();
void auto_dep_scan ();
void clear ();
const std::string dump ();
void dependency_scan ();
bool _read_only;
bool _dirty;
bool _loaded_tasks;
bool _loaded_lines;
bool _has_ids;
bool _auto_dep_scan;
std::vector <Task> _tasks;
// _tasks_map was introduced mainly for speeding up "task import".
// Iterating over all _tasks for each imported task is slow, making use of
// appropriate data structures is fast.
std::unordered_map <std::string, Task> _tasks_map;
std::vector <Task> _added_tasks;
std::vector <Task> _modified_tasks;
std::unordered_set <std::string> _purged_tasks;
std::vector <std::string> _lines;
std::vector <std::string> _added_lines;
File _file;
private:
std::unordered_map <int, std::string> _I2U; // ID -> UUID map
std::unordered_map <std::string, int> _U2I; // UUID -> ID map
};
#include <tc/WorkingSet.h>
#include <tc/Replica.h>
// TDB2 Class represents all the files in the task database.
class TDB2
@@ -109,14 +46,13 @@ public:
TDB2 ();
void set_location (const std::string&);
void open_replica (const std::string&, bool create_if_missing);
void add (Task&, bool add_to_backlog = true);
void modify (Task&, bool add_to_backlog = true);
void commit ();
void get_changes (std::vector <Task>&);
void revert ();
void gc ();
int next_id ();
int latest_id ();
// Generalized task accessors.
@@ -139,28 +75,11 @@ public:
void dump ();
private:
void gather_changes ();
void update (Task&, const bool, const bool addition = false);
bool verifyUniqueUUID (const std::string&);
tc::Replica replica;
std::optional<tc::WorkingSet> _working_set;
const tc::WorkingSet &working_set ();
void show_diff (const std::string&, const std::string&, const std::string&);
void revert_undo (std::vector <std::string>&, std::string&, std::string&, std::string&, std::string&);
void revert_pending (std::vector <std::string>&, const std::string&, const std::string&);
void revert_completed (std::vector <std::string>&, std::vector <std::string>&, const std::string&, const std::string&);
void revert_backlog (std::vector <std::string>&, const std::string&, const std::string&, const std::string&);
protected:
friend class TF2; // TF2 reaches into TDB2 internals for gc
TF2 pending;
TF2 completed;
friend class CmdSync; // CmdSync accesses the backlog directly
TF2 backlog;
TF2 undo;
private:
std::string _location;
int _id;
std::vector <Task> _changes;
};
#endif

View File

@@ -144,6 +144,19 @@ Task::Task (const json::object* obj)
parseJSON (obj);
}
////////////////////////////////////////////////////////////////////////////////
Task::Task (tc::Task obj)
{
id = 0;
urgency_value = 0.0;
recalc_urgency = true;
is_blocked = false;
is_blocking = false;
annotation_count = 0;
parseTC (obj);
}
////////////////////////////////////////////////////////////////////////////////
Task::status Task::textToStatus (const std::string& input)
{
@@ -895,6 +908,29 @@ void Task::parseJSON (const json::object* root_obj)
}
}
////////////////////////////////////////////////////////////////////////////////
// Note that all fields undergo encode/decode.
void Task::parseTC (const tc::Task& task)
{
data = task.get_taskmap ();
// count annotations
annotation_count = 0;
for (auto i : data)
{
if (isAnnotationAttr (i.first))
{
++annotation_count;
}
}
data["uuid"] = task.get_uuid ();
id = Context::getContext ().tdb2.id (data["uuid"]);
is_blocking = task.is_blocking();
is_blocked = task.is_blocked();
}
////////////////////////////////////////////////////////////////////////////////
// No legacy formats are currently supported as of 2.4.0.
void Task::parseLegacy (const std::string& line)
@@ -1711,6 +1747,9 @@ void Task::substitute (
// 1) To provide missing attributes where possible
// 2) To provide suitable warnings about odd states
// 3) To generate errors when the inconsistencies are not fixable
// 4) To update status depending on other attributes
//
// Critically, note that despite the name this is not a read-only function.
//
void Task::validate (bool applyDefault /* = true */)
{
@@ -1938,6 +1977,31 @@ 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)
{

View File

@@ -35,6 +35,7 @@
#include <JSON.h>
#include <Table.h>
#include <Datetime.h>
#include <tc/Task.h>
class Task
{
@@ -65,6 +66,7 @@ public:
bool operator!= (const Task&);
Task (const std::string&);
Task (const json::object*);
Task (tc::Task);
void parse (const std::string&);
std::string composeF4 () const;
@@ -87,6 +89,8 @@ public:
// 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;
@@ -178,10 +182,12 @@ public:
Table diffForUndoSide (const Task& after) const;
Table diffForUndoPatch (const Task& after, const Datetime& lastChange) const;
private:
int determineVersion (const std::string&);
void parseJSON (const std::string&);
void parseJSON (const json::object*);
void parseTC (const tc::Task&);
void parseLegacy (const std::string&);
void validate_before (const std::string&, const std::string&);
const std::string encode (const std::string&) const;

View File

@@ -112,15 +112,17 @@ int CmdExport::execute (std::string& output)
}
else
{
// There is a sortOrder, so sorting will take place, which means the initial
// order of sequence is ascending.
// sort_tasks requires the order array initially be identity
for (unsigned int i = 0; i < filtered.size (); ++i)
sequence.push_back (i);
// Sort the tasks.
if (sortOrder.size ()) {
sort_tasks (filtered, sequence, reportSort);
// if no sort order, sort by id
if (!sortOrder.size ()) {
reportSort = "id";
}
// Sort the tasks.
sort_tasks (filtered, sequence, reportSort);
}
// Export == render.

View File

@@ -90,7 +90,6 @@ void handleRecurrence ()
Task rec (t); // Clone the parent.
rec.setStatus (Task::pending); // Change the status.
rec.id = Context::getContext ().tdb2.next_id (); // New ID.
rec.set ("uuid", uuid ()); // New UUID.
rec.set ("parent", t.get ("uuid")); // Remember mom.
rec.setAsNow ("entry"); // New entry date.

View File

@@ -33,6 +33,25 @@
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 ()
{
@@ -113,6 +132,45 @@ tc::Task tc::Replica::new_task (tc::Status status, const std::string &descriptio
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::undo (int32_t *undone_out)
{
auto res = tc_replica_undo (&*inner, undone_out);
if (res != TC_RESULT_OK) {
throw replica_error ();
}
}
////////////////////////////////////////////////////////////////////////////////
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::Task> tc::Replica::all_tasks ()
{
@@ -134,14 +192,19 @@ std::vector<tc::Task> tc::Replica::all_tasks ()
}
////////////////////////////////////////////////////////////////////////////////
void tc::Replica::rebuild_working_set ()
void tc::Replica::rebuild_working_set (bool force)
{
auto res = tc_replica_rebuild_working_set (&*inner, true);
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));

View File

@@ -45,6 +45,28 @@ namespace tc {
tc::ffi::TCReplica,
std::function<void(tc::ffi::TCReplica*)>>;
// 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_..`.
@@ -67,12 +89,20 @@ namespace tc {
tc::WorkingSet working_set ();
std::optional<tc::Task> 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);
// 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);
void undo (int32_t *undone_out);
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 ();
private:
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
@@ -82,5 +112,6 @@ namespace tc {
};
}
#endif
////////////////////////////////////////////////////////////////////////////////

View File

@@ -60,6 +60,18 @@ tc::Task& tc::Task::operator= (Task &&other) noexcept
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
{
@@ -100,6 +112,15 @@ std::string tc::Task::get_description () const
}
////////////////////////////////////////////////////////////////////////////////
std::optional<std::string> 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);
@@ -123,6 +144,40 @@ 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<std::string> 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);

View File

@@ -31,10 +31,12 @@
#include <functional>
#include <memory>
#include <map>
#include <optional>
#include "tc/ffi.h"
namespace tc {
class Replica;
class ReplicaGuard;
enum Status {
Pending = tc::ffi::TC_STATUS_PENDING,
@@ -57,10 +59,16 @@ namespace tc {
class Task
{
protected:
// Tasks may only be created by tc::Replica
// 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;
@@ -70,12 +78,11 @@ namespace tc {
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 <std::string, std::string> get_taskmap() const;
std::string get_description() const;
std::optional<std::string> 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);
@@ -90,11 +97,12 @@ namespace tc {
// 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);
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<std::string> 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);
// TODO: TCResult tc_task_set_modified(struct TCTask *task, time_t modified);
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);