Merge branch 'develop' into feature/sync-feedback

This commit is contained in:
Dustin J. Mitchell 2025-05-07 16:54:43 -04:00
commit e2305e703d
No known key found for this signature in database
117 changed files with 1995 additions and 861 deletions

1
.dockerignore Normal file
View file

@ -0,0 +1 @@
target

View file

@ -5,6 +5,11 @@ updates:
directory: "/"
schedule:
interval: "weekly"
# Enable version updates for git submodules
- package-ecosystem: "gitsubmodule"
directory: "/"
schedule:
interval: "daily"
# Enable updates for Rust packages
- package-ecosystem: "cargo"
directory: "/" # Location of package manifests

View file

@ -15,25 +15,25 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@master
id: toolchain
with:
# If this version is old enough to cause errors, or older than the
# TaskChampion MSRV, bump it to the MSRV of the currently-required
# TaskChampion package; if necessary, bump that version as well.
toolchain: "1.81.0" # MSRV
override: true
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1.0.3
with:
@ -53,12 +53,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@master
id: toolchain
with:
profile: minimal
components: rustfmt
toolchain: stable
override: true
toolchain: "stable"
components: "rustfmt"
- uses: actions-rs/cargo@v1.0.3
with:
@ -71,12 +70,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@master
id: toolchain
with:
profile: minimal
components: rustfmt
toolchain: stable
override: true
toolchain: "stable"
components: "rustfmt"
- name: "Check metadata"
run: ".github/workflows/metadata-check.sh"

View file

@ -33,10 +33,10 @@ jobs:
submodules: "recursive"
- name: Install cosign
uses: sigstore/cosign-installer@v3.7.0
uses: sigstore/cosign-installer@v3.8.2
- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3.3.0
uses: docker/login-action@v3.4.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
@ -44,7 +44,7 @@ jobs:
- name: Build and push Taskwarrior Docker image
id: build-and-push
uses: docker/build-push-action@v6.11.0
uses: docker/build-push-action@v6.16.0
with:
context: .
file: "./docker/task.dockerfile"

View file

@ -6,16 +6,14 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
id: toolchain
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/toolchain@v1
with:
toolchain: "stable"
override: true
key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- name: Install uuid-dev
run: sudo apt install uuid-dev

View file

@ -43,22 +43,20 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
id: toolchain
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-stable-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-stable-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/toolchain@v1
with:
toolchain: "stable"
override: true
key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- name: Test MacOS
run: bash test/scripts/test_macos.sh
@ -72,22 +70,20 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
id: toolchain
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-stable-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-stable-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/toolchain@v1
with:
toolchain: "stable"
override: true
key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- name: Test MacOS
run: bash test/scripts/test_macos.sh
@ -101,26 +97,25 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
id: toolchain
with:
# If this version is old enough to cause errors, or older than the
# TaskChampion MSRV, bump it to the MSRV of the currently-required
# TaskChampion package; if necessary, bump that version as well.
toolchain: "1.81.0" # MSRV
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/toolchain@v1
with:
# If this version is old enough to cause errors, or older than the
# TaskChampion MSRV, bump it to the MSRV of the currently-required
# TaskChampion package; if necessary, bump that version as well.
# This should match the MSRV in `src/taskchampion-cpp/Cargo.toml`.
toolchain: "1.81.0" # MSRV
override: true
key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1.0.3
with:

View file

@ -9,11 +9,11 @@ repos:
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v19.1.6
rev: v20.1.3
hooks:
- id: clang-format
types_or: [c++, c]
- repo: https://github.com/psf/black
rev: 24.10.0
rev: 25.1.0
hooks:
- id: black

View file

@ -4,7 +4,7 @@ enable_testing()
set (CMAKE_EXPORT_COMPILE_COMMANDS ON)
project (task
VERSION 3.3.0
VERSION 3.4.1
DESCRIPTION "Taskwarrior - a command-line TODO list manager"
HOMEPAGE_URL https://taskwarrior.org/)
@ -162,6 +162,6 @@ endforeach (doc_FILE)
set (CPACK_SOURCE_GENERATOR "TGZ")
set (CPACK_SOURCE_PACKAGE_FILE_NAME ${PACKAGE_NAME}-${PACKAGE_VERSION})
set (CPACK_SOURCE_IGNORE_FILES "build" "test" "misc/*" "performance" "swp$" "src/lex$" "task-.*.tar.gz"
set (CPACK_SOURCE_IGNORE_FILES "build" "target" "test" "misc/*" "performance" "swp$" "src/lex$" "task-.*.tar.gz"
"commit.h" "cmake.h$" "\\\\.gitmodules" "src/libshared/\\\\.git" ".github/" ".*\\\\.gitignore$" "docker-compose.yml" "\\\\.git/")
include (CPack)

1
CONTRIBUTING.md Symbolic link
View file

@ -0,0 +1 @@
doc/devel/contrib/development.md

1161
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,36 @@
------ current release ---------------------------
3.4.1 -
- The nagging to read `task news` has been fixed.
------ old releases ------------------------------
3.4.0 -
- Where possible, the task DB is now opened in read-only mode, which improves
performance. This is the case for reports (task lists) only when the `gc`
config is false.
- An updated version of corrosion fixes build errors trying to find the Rust
toolchain.
Thanks to the following people for contributions to this release:
- Dustin J. Mitchell
- Kalle Kietäväinen
- Karl
- Matthew
- Tejada-Omar
- Tobias Predel
- Yong Li
- jrmarino
3.3.0 -
- Sync now supports AWS S3 as a backend.
- A new `task import-v2` command allows importing Taskwarrior-2.x
data files directly.
3.3.0 -
Thanks to the following people for contributions to this release:
- Chongyun Lee
@ -18,8 +43,6 @@ Thanks to the following people for contributions to this release:
- Scott Mcdermott
- Thomas Lauf
------ old releases ------------------------------
3.2.0 -
- Support for the journal in `task info` has been restored (#3671) and the

View file

@ -34,7 +34,7 @@ Briefly, these shell commands will unpack, build and install Taskwarrior:
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release . [3]
$ cmake --build build [4]
$ sudo cmake --install build [5]
$ cd .. ; rm -r task-X.Y.Z [6]
$ cd .. ; rm -r task-X.Y.Z [6] (see: Uninstallation)
These commands are explained below:
@ -103,6 +103,13 @@ There is no uninstall option in CMake makefiles. This is a manual process.
To uninstall Taskwarrior, remove the files listed in the install_manifest.txt
file that was generated when you built Taskwarrior.
```sh
cd task-X.Y.Z
sudo xargs rm < build/install_manifest.txt
```
If you want to uninstall this way, you will need to omit step [6] above and
retain the source folder after installation.
Taskwarrior Build Notes
-----------------------

1
RELEASING.md Symbolic link
View file

@ -0,0 +1 @@
doc/devel/contrib/releasing.md

View file

@ -24,3 +24,6 @@ To release Taskwarrior, follow this process:
- Add a new item in `content/news`
- Update `data/projects.json` with the latest version and a fake next version for "devel"
- Update `data/releases.json` with the new version, and copy the tarball into `content/download`.
- Update various things, in a new PR:
- `cargo update`
- `git submodule update --remote --merge`

View file

@ -25,7 +25,7 @@ In brief: "MUST" (or "REQUIRED") means that the item is an absolute requirement
## General Format
The format is JSON, specifically a JSON object as a single line of text, terminated by a newline (U+000D).
The format is JSON, specifically a JSON object as a single line of text, terminated by a line feed (U+000A).
The JSON looks like this:

View file

@ -143,7 +143,9 @@ Then configure Taskwarrior with:
To synchronize your tasks to AWS, select a region near you and use the AWS
console to create a new S3 bucket. The default settings for the bucket are
adequate.
adequate. In particular, ensure that no lifecycle policies are enabled, as they
may automatically delete or transition objects, potentially impacting data
availability.
You will also need an AWS IAM user with the following policy, where BUCKETNAME
is the name of the bucket. The same user can be configured for multiple

View file

@ -1037,7 +1037,7 @@ modifier requires that the attribute contain the whole word specified, such
that this:
.nf
task description.word:bar list
task description.word:foo list
.fi
Will match the description 'foo bar baz' but does not match 'dog food'.

View file

@ -212,6 +212,9 @@ This is a path to the hook scripts directory. By default it is ~/.task/hooks.
.TP
.B gc=1
Can be used to temporarily suspend rebuilding, so that task IDs don't change.
Rebuilding requires read/write access to the database, so disabling `gc` may
result in better performance.
Note that this should be used in the form of a command line override (task
rc.gc=0 ...), and not permanently used in the .taskrc file, as this
significantly affects performance in the long term.
@ -1381,7 +1384,7 @@ if you define a UDA named 'estimate', Taskwarrior will not know that this value
is weeks, hours, minutes, money, or some other resource count.
.TP
.B uda.<name>.type=string|numeric|date|duration
.B uda.<name>.type=string|numeric|uuid|date|duration
.RS
Defines a UDA called '<name>', of the specified type.
.RE

View file

@ -6,20 +6,20 @@ include_directories (${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src/libshared/src
${TASK_INCLUDE_DIRS})
add_library (task STATIC CLI2.cpp CLI2.h
Context.cpp Context.h
DOM.cpp DOM.h
Eval.cpp Eval.h
Filter.cpp Filter.h
Hooks.cpp Hooks.h
Lexer.cpp Lexer.h
Operation.cpp Operation.h
TF2.cpp TF2.h
TDB2.cpp TDB2.h
Task.cpp Task.h
Variant.cpp Variant.h
Version.cpp Version.h
ViewTask.cpp ViewTask.h
add_library (task STATIC CLI2.cpp
Context.cpp
DOM.cpp
Eval.cpp
Filter.cpp
Hooks.cpp
Lexer.cpp
Operation.cpp
TF2.cpp
TDB2.cpp
Task.cpp
Variant.cpp
Version.cpp
ViewTask.cpp
dependency.cpp
feedback.cpp
legacy.cpp
@ -27,26 +27,25 @@ add_library (task STATIC CLI2.cpp CLI2.h
recur.cpp
rules.cpp
sort.cpp
util.cpp util.h)
util.cpp)
target_link_libraries(task taskchampion-cpp)
add_library (libshared STATIC libshared/src/Color.cpp libshared/src/Color.h
libshared/src/Configuration.cpp libshared/src/Configuration.h
libshared/src/Datetime.cpp libshared/src/Datetime.h
libshared/src/Duration.cpp libshared/src/Duration.h
libshared/src/FS.cpp libshared/src/FS.h
libshared/src/JSON.cpp libshared/src/JSON.h
libshared/src/Msg.cpp libshared/src/Msg.h
libshared/src/Pig.cpp libshared/src/Pig.h
libshared/src/RX.cpp libshared/src/RX.h
libshared/src/Table.cpp libshared/src/Table.h
libshared/src/Timer.cpp libshared/src/Timer.h
libshared/src/format.cpp libshared/src/format.h
add_library (libshared STATIC libshared/src/Color.cpp
libshared/src/Configuration.cpp
libshared/src/Datetime.cpp
libshared/src/Duration.cpp
libshared/src/FS.cpp
libshared/src/JSON.cpp
libshared/src/Msg.cpp
libshared/src/Pig.cpp
libshared/src/RX.cpp
libshared/src/Table.cpp
libshared/src/Timer.cpp
libshared/src/format.cpp
libshared/src/ip.cpp
libshared/src/shared.cpp libshared/src/shared.h
libshared/src/unicode.cpp libshared/src/unicode.h
libshared/src/utf8.cpp libshared/src/utf8.h
libshared/src/wcwidth.h)
libshared/src/shared.cpp
libshared/src/unicode.cpp
libshared/src/utf8.cpp)
add_executable (task_executable main.cpp)
add_executable (calc_executable calc.cpp)

View file

@ -36,16 +36,15 @@
#include <Version.h>
#include <assert.h>
#include <format.h>
#include <main.h>
#include <recur.h>
#include <rules.h>
#include <rust/cxx.h>
#include <shared.h>
#include <stdlib.h>
#include <string.h>
#include <taskchampion-cpp/lib.h>
#include <unistd.h>
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <regex>
@ -55,7 +54,6 @@
#include <commit.h>
#endif
#include <stdio.h>
#include <sys/ioctl.h>
#ifdef SOLARIS
@ -598,9 +596,6 @@ int Context::initialize(int argc, const char** argv) {
createDefaultConfig();
bool create_if_missing = !config.getBoolean("exit.on.missing.db");
tdb2.open_replica(data_dir, create_if_missing);
////////////////////////////////////////////////////////////////////////////
//
// [3] Instantiate Command objects and capture command entities.
@ -674,6 +669,21 @@ int Context::initialize(int argc, const char** argv) {
if (foundAssumed) header("No command specified - assuming 'information'.");
}
////////////////////////////////////////////////////////////////////////////
//
// [7.5] Open the Replica.
//
////////////////////////////////////////////////////////////////////////////
bool create_if_missing = !config.getBoolean("exit.on.missing.db");
Command* c = commands[cli2.getCommand()];
// We must allow writes if either 'gc' is enabled and the command performs GC, or the command
// itself is read-write.
bool read_write =
(config.getBoolean("gc") && (c->needs_gc() || c->needs_recur_update())) || !c->read_only();
tdb2.open_replica(data_dir, create_if_missing, read_write);
////////////////////////////////////////////////////////////////////////////
//
// [8] Initialize hooks.
@ -869,7 +879,7 @@ int Context::dispatch(std::string& out) {
if (config.getBoolean("debug") && config.getInteger("debug.parser") == 1)
debug(cli2.dump("Parse Tree (before command-specifіc processing)"));
if (c->needs_recur_update()) {
if (c->needs_recur_update() && Context::getContext().config.getBoolean("gc")) {
handleUntil();
handleRecurrence();
}

View file

@ -34,7 +34,6 @@
#include <Task.h>
#include <format.h>
#include <shared.h>
#include <time.h>
#include <map>

View file

@ -36,8 +36,6 @@
#include <format.h>
#include <shared.h>
#include <algorithm>
////////////////////////////////////////////////////////////////////////////////
// Take an input set of tasks and filter into a subset.
void Filter::subset(const std::vector<Task>& input, std::vector<Task>& output) {

View file

@ -30,7 +30,6 @@
#include <Task.h>
#include <Variant.h>
#include <string>
#include <vector>
class Filter {

View file

@ -44,7 +44,6 @@
#include <Variant.h>
#include <format.h>
#include <shared.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
@ -276,7 +275,7 @@ void Hooks::onAdd(Task& task) const {
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
void Hooks::onModify(const Task& before, Task& after) const {
void Hooks::onModify(Task& before, Task& after) const {
if (!_enabled) return;
Timer timer;

View file

@ -40,7 +40,7 @@ class Hooks {
void onLaunch() const;
void onExit() const;
void onAdd(Task&) const;
void onModify(const Task&, Task&) const;
void onModify(Task&, Task&) const;
std::vector<std::string> list() const;
private:

View file

@ -1,6 +1,7 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2024, Tomas Babej, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2025, Tomas Babej, Paul Beckingham, Federico Hernandez,
// Tobias Predel.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@ -24,8 +25,8 @@
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_OPERATIOn
#define INCLUDED_OPERATIOn
#ifndef INCLUDED_OPERATION
#define INCLUDED_OPERATION
#include <taskchampion-cpp/lib.h>

View file

@ -33,16 +33,11 @@
#include <TDB2.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <signal.h>
#include <stdlib.h>
#include <util.h>
#include <algorithm>
#include <iostream>
#include <list>
#include <sstream>
#include <unordered_set>
#include <vector>
@ -50,17 +45,16 @@ bool TDB2::debug_mode = false;
static void dependency_scan(std::vector<Task>&);
////////////////////////////////////////////////////////////////////////////////
void TDB2::open_replica(const std::string& location, bool create_if_missing) {
_replica = tc::new_replica_on_disk(location, create_if_missing);
void TDB2::open_replica(const std::string& location, bool create_if_missing, bool read_write) {
_replica = tc::new_replica_on_disk(location, create_if_missing, read_write);
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::open_replica_in_memory() { _replica = tc::new_replica_in_memory(); }
////////////////////////////////////////////////////////////////////////////////
// Add the new task to the replica.
void TDB2::add(Task& task) {
// Validate a task for addition. This is stricter than `task.validate`, as any
// inconsistency is probably user error.
task.validate_add();
// 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.
@ -194,11 +188,8 @@ void TDB2::purge(Task& task) {
////////////////////////////////////////////////////////////////////////////////
rust::Box<tc::Replica>& TDB2::replica() {
// Create a replica in-memory if `open_replica` has not been called. This
// occurs in tests.
if (!_replica) {
_replica = tc::new_replica_in_memory();
}
// One of the open_replica_ methods must be called before this one.
assert(_replica);
return _replica.value();
}
@ -363,8 +354,7 @@ bool TDB2::get(const std::string& uuid, Task& task) {
////////////////////////////////////////////////////////////////////////////////
// Locate task by UUID, wherever it is.
bool TDB2::has(const std::string& uuid) {
Task task;
return get(uuid, task);
return replica()->get_task_data(tc::uuid_from_string(uuid)).is_some();
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -27,9 +27,7 @@
#ifndef INCLUDED_TDB2
#define INCLUDED_TDB2
#include <FS.h>
#include <Task.h>
#include <stdio.h>
#include <taskchampion-cpp/lib.h>
#include <map>
@ -46,7 +44,8 @@ class TDB2 {
TDB2() = default;
void open_replica(const std::string &, bool create_if_missing);
void open_replica(const std::string &, bool create_if_missing, bool read_write);
void open_replica_in_memory();
void add(Task &);
void modify(Task &);
void purge(Task &);

View file

@ -31,18 +31,12 @@
#include <Table.h>
#include <cmake.h>
#include <format.h>
#include <main.h>
#ifdef PRODUCT_TASKWARRIOR
#include <legacy.h>
#endif
#include <shared.h>
#include <signal.h>
#include <stdlib.h>
#include <util.h>
#include <algorithm>
#include <iostream>
#include <list>
#include <set>
#include <sstream>
#define STRING_TDB2_REVERTED "Modified task reverted."
////////////////////////////////////////////////////////////////////////////////

View file

@ -29,12 +29,9 @@
#include <FS.h>
#include <Task.h>
#include <stdio.h>
#include <map>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
// TF2 Class represents a single 2.x-style file in the task database.

View file

@ -58,7 +58,8 @@
#include <Eval.h>
#include <Filter.h>
#include <Variant.h>
#include <main.h>
#include <dependency.h>
#include <feedback.h>
#define APPROACHING_INFINITY 1000 // Close enough. This isn't rocket surgery.
@ -321,8 +322,8 @@ void Task::setStatus(Task::status status) {
////////////////////////////////////////////////////////////////////////////////
// Determines status of a date attribute.
Task::dateState Task::getDateState(const std::string& name) const {
std::string value = get(name);
if (value.length()) {
time_t value = get_date(name);
if (value > 0) {
Datetime reference(value);
Datetime now;
Datetime today("today");
@ -776,7 +777,7 @@ void Task::parseLegacy(const std::string& line) {
}
////////////////////////////////////////////////////////////////////////////////
std::string Task::composeJSON(bool decorate /*= false*/) const {
std::string Task::composeJSON(bool decorate /*= false*/) {
std::stringstream out;
out << '{';
@ -797,14 +798,16 @@ std::string Task::composeJSON(bool decorate /*= false*/) const {
// If value is an empty string, do not ever output it
if (i.second == "") continue;
if (attributes_written) out << ',';
std::string type = Task::attributes[i.first];
if (type == "") type = "string";
// Date fields are written as ISO 8601.
if (type == "date") {
time_t epoch = get_date(i.first);
if (epoch != 0) {
Datetime d(i.second);
if (attributes_written) out << ',';
out << '"' << (i.first == "modification" ? "modified" : i.first)
<< "\":\""
// Date was deleted, do not export parsed empty string
@ -812,6 +815,7 @@ std::string Task::composeJSON(bool decorate /*= false*/) const {
++attributes_written;
}
}
/*
else if (type == "duration")
@ -820,6 +824,8 @@ std::string Task::composeJSON(bool decorate /*= false*/) const {
}
*/
else if (type == "numeric") {
if (attributes_written) out << ',';
out << '"' << i.first << "\":" << i.second;
++attributes_written;
@ -827,6 +833,8 @@ std::string Task::composeJSON(bool decorate /*= false*/) const {
// Everything else is a quoted value.
else {
if (attributes_written) out << ',';
out << '"' << i.first << "\":\"" << (type == "string" ? json::encode(i.second) : i.second)
<< '"';
@ -886,7 +894,7 @@ std::string Task::composeJSON(bool decorate /*= false*/) const {
#ifdef PRODUCT_TASKWARRIOR
// Include urgency.
if (decorate) out << ',' << "\"urgency\":" << urgency_c();
if (decorate) out << ',' << "\"urgency\":" << urgency();
#endif
out << '}';
@ -920,7 +928,7 @@ void Task::addAnnotation(const std::string& description) {
++now;
} while (has(key));
data[key] = json::decode(description);
data[key] = description;
++annotation_count;
recalc_urgency = true;
}
@ -1991,7 +1999,7 @@ void Task::modify(modType type, bool text_required /* = false */) {
// Delegate modification to the column object or their base classes.
if (name == "depends" || name == "tags" || name == "recur" || column->type() == "date" ||
column->type() == "duration" || column->type() == "numeric" ||
column->type() == "string") {
column->type() == "string" || column->type() == "uuid") {
column->modify(*this, value);
mods = true;
}

View file

@ -30,7 +30,6 @@
#include <Datetime.h>
#include <JSON.h>
#include <Table.h>
#include <stdio.h>
#include <taskchampion-cpp/lib.h>
#include <time.h>
@ -69,7 +68,7 @@ class Task {
Task(rust::Box<tc::TaskData>);
void parse(const std::string&);
std::string composeJSON(bool decorate = false) const;
std::string composeJSON(bool decorate = false);
// Status values.
enum status { pending, completed, deleted, recurring, waiting };

View file

@ -75,7 +75,6 @@
#define STRING_VARIANT_DIV_DUR_BOOL "Cannot divide duration by Boolean"
#define STRING_VARIANT_DIV_DUR_STR "Cannot divide durations by strings"
#define STRING_VARIANT_DIV_DUR_DATE "Cannot divide durations by dates"
#define STRING_VARIANT_DIV_DUR_DUR "Cannot divide durations by durations"
#define STRING_VARIANT_MOD_BOOL "Cannot modulo Booleans"
#define STRING_VARIANT_MOD_DATE "Cannot modulo date values"
#define STRING_VARIANT_MOD_DUR "Cannot modulo duration values"
@ -1760,7 +1759,9 @@ Variant& Variant::operator/=(const Variant& other) {
throw std::string(STRING_VARIANT_DIV_DUR_DATE);
case type_duration:
throw std::string(STRING_VARIANT_DIV_DUR_DUR);
_type = type_real;
_real = static_cast<double>(_duration) / static_cast<double>(right._duration);
break;
}
break;
}

View file

@ -28,9 +28,7 @@
#define INCLUDED_VARIANT
#include <Task.h>
#include <time.h>
#include <map>
#include <string>
class Variant {

View file

@ -30,7 +30,7 @@
#include <Context.h>
#include <ViewTask.h>
#include <format.h>
#include <main.h>
#include <rules.h>
#include <utf8.h>
#include <util.h>

View file

@ -30,13 +30,10 @@
#include <ColDepends.h>
#include <Context.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <stdlib.h>
#include <utf8.h>
#include <util.h>
#include <algorithm>
#include <regex>
#define STRING_COLUMN_LABEL_DEP "Depends"

View file

@ -32,8 +32,8 @@
#include <Eval.h>
#include <Filter.h>
#include <Variant.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <utf8.h>

View file

@ -56,11 +56,9 @@ void ColumnTypeDuration::modify(Task& task, const std::string& value) {
evaluatedValue = Variant(value);
}
// The duration is stored in raw form, but it must still be valid,
// and therefore is parsed first.
// The duration is first parsed, then stored inside the variant as a `time_t`
std::string label = " MODIFICATION ";
if (evaluatedValue.type() == Variant::type_duration) {
// Store the raw value, for 'recur'.
Context::getContext().debug(label + _name + " <-- " + (std::string)evaluatedValue + " <-- '" +
value + '\'');
task.set(_name, evaluatedValue);

View file

@ -297,3 +297,23 @@ void ColumnUDADuration::render(std::vector<std::string>& lines, Task& task, int
}
////////////////////////////////////////////////////////////////////////////////
ColumnUDAUUID::ColumnUDAUUID() {
_name = "<uda>";
_type = "uuid";
_style = "long";
_label = "";
_modifiable = true;
_uda = true;
_styles = {"long", "short"};
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnUDAUUID::validate(const std::string& input) const {
Lexer lex(input);
std::string token;
Lexer::Type type;
return lex.isUUID(token, type, true);
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -31,6 +31,7 @@
#include <ColTypeDuration.h>
#include <ColTypeNumeric.h>
#include <ColTypeString.h>
#include <ColUUID.h>
////////////////////////////////////////////////////////////////////////////////
class ColumnUDAString : public ColumnTypeString {
@ -83,5 +84,12 @@ class ColumnUDADuration : public ColumnTypeDuration {
std::vector<std::string> _values;
};
////////////////////////////////////////////////////////////////////////////////
class ColumnUDAUUID : public ColumnUUID {
public:
ColumnUDAUUID();
bool validate(const std::string&) const;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -246,9 +246,15 @@ Column* Column::uda(const std::string& name) {
c->_label = label;
if (values != "") c->_values = split(values, ',');
return c;
} else if (type == "uuid") {
auto c = new ColumnUDAUUID();
c->_name = name;
c->_label = label;
return c;
} else if (type != "")
throw std::string(
"User defined attributes may only be of type 'string', 'date', 'duration' or 'numeric'.");
"User defined attributes may only be of type 'string', 'uuid', date', 'duration' or "
"'numeric'.");
return nullptr;
}

View file

@ -29,8 +29,8 @@
#include <CmdAdd.h>
#include <Context.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
////////////////////////////////////////////////////////////////////////////////
CmdAdd::CmdAdd() {
@ -56,6 +56,11 @@ int CmdAdd::execute(std::string& output) {
// the task is empty, but DOM references can refer to earlier parts of the
// command line, e.g., `task add due:20110101 wait:due`.
task.modify(Task::modReplace, true);
// Validate a task for addition. This is stricter than `task.validate`, as any
// inconsistency is probably user error.
task.validate_add();
Context::getContext().tdb2.add(task);
// Do not display ID 0, users cannot query by that

View file

@ -30,8 +30,8 @@
#include <CmdAnnotate.h>
#include <Context.h>
#include <Filter.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <iostream>

View file

@ -30,8 +30,8 @@
#include <CmdAppend.h>
#include <Context.h>
#include <Filter.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <iostream>

View file

@ -33,7 +33,6 @@
#include <Duration.h>
#include <Filter.h>
#include <format.h>
#include <main.h>
#include <math.h>
#include <shared.h>
#include <string.h>

View file

@ -32,7 +32,6 @@
#include <Lexer.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <stdlib.h>
#include <utf8.h>

View file

@ -32,7 +32,6 @@
#include <Context.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <sstream>

View file

@ -31,7 +31,6 @@
#include <Color.h>
#include <Context.h>
#include <Table.h>
#include <main.h>
#include <shared.h>
#include <util.h>

View file

@ -33,7 +33,7 @@
#include <Filter.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <rules.h>
#include <shared.h>
#include <util.h>
@ -218,8 +218,8 @@ void CmdContext::defineContext(const std::vector<std::string>& words, std::strin
if (!valid_write_context) {
std::stringstream warning;
warning
<< format("The filter '{1}' is not a valid modification string, because it contains {2}.",
value, reason)
<< format("The filter '{1}' is not a valid modification string, because it {2}.", value,
reason)
<< "\nAs such, value for the write context cannot be set (context will not apply on task "
"add / task log).\n\n"
<< format(

View file

@ -30,7 +30,6 @@
#include <CmdCount.h>
#include <Filter.h>
#include <format.h>
#include <main.h>
////////////////////////////////////////////////////////////////////////////////
CmdCount::CmdCount() {

View file

@ -34,15 +34,15 @@
#include <Lexer.h>
#include <Version.h>
#include <ViewTask.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <legacy.h>
#include <shared.h>
#include <stdlib.h>
#include <sort.h>
#include <util.h>
#include <algorithm>
#include <iostream>
#include <map>
#include <sstream>
#include <vector>

View file

@ -30,8 +30,10 @@
#include <CmdDelete.h>
#include <Context.h>
#include <Filter.h>
#include <dependency.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <recur.h>
#include <shared.h>
#include <iostream>

View file

@ -30,8 +30,8 @@
#include <CmdDenotate.h>
#include <Context.h>
#include <Filter.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <util.h>

View file

@ -30,8 +30,11 @@
#include <CmdDone.h>
#include <Context.h>
#include <Filter.h>
#include <dependency.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <nag.h>
#include <recur.h>
#include <util.h>
#include <iostream>

View file

@ -30,8 +30,8 @@
#include <CmdDuplicate.h>
#include <Context.h>
#include <Filter.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <util.h>
#include <iostream>

View file

@ -36,7 +36,6 @@
#include <Lexer.h>
#include <Pig.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <unistd.h>
#include <util.h>
@ -64,7 +63,7 @@ CmdEdit::CmdEdit() {
_read_only = false;
_displays_id = false;
_needs_gc = false;
_needs_recur_update = false;
_needs_recur_update = true;
_uses_context = true;
_accepts_filter = true;
_accepts_modifications = false;
@ -78,8 +77,6 @@ CmdEdit::CmdEdit() {
// wrench. To be used sparingly.
int CmdEdit::execute(std::string&) {
// Filter the tasks.
handleUntil();
handleRecurrence();
Filter filter;
std::vector<Task> filtered;
filter.subset(filtered);

View file

@ -31,8 +31,9 @@
#include <Context.h>
#include <Filter.h>
#include <format.h>
#include <main.h>
#include <legacy.h>
#include <shared.h>
#include <sort.h>
////////////////////////////////////////////////////////////////////////////////
CmdExport::CmdExport() {

View file

@ -32,7 +32,6 @@
#include <DOM.h>
#include <Variant.h>
#include <format.h>
#include <main.h>
#include <shared.h>
////////////////////////////////////////////////////////////////////////////////

View file

@ -33,7 +33,6 @@
#include <Filter.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <util.h>
#include <sstream>
@ -55,7 +54,7 @@ CmdHistoryBase<HistoryStrategy>::CmdHistoryBase() {
_read_only = true;
_displays_id = false;
_needs_gc = false;
_needs_recur_update = false;
_needs_recur_update = true;
_uses_context = true;
_accepts_filter = true;
_accepts_modifications = false;
@ -294,8 +293,6 @@ int CmdHistoryBase<HistoryStrategy>::execute(std::string& output) {
completedGroup.clear();
// Apply filter.
handleUntil();
handleRecurrence();
Filter filter;
std::vector<Task> filtered;
filter.subset(filtered);

View file

@ -30,7 +30,6 @@
#include <CmdIDs.h>
#include <Context.h>
#include <Filter.h>
#include <main.h>
#include <shared.h>
#include <algorithm>
@ -137,8 +136,6 @@ CmdCompletionIds::CmdCompletionIds() {
////////////////////////////////////////////////////////////////////////////////
int CmdCompletionIds::execute(std::string& output) {
// Apply filter.
handleUntil();
handleRecurrence();
Filter filter;
std::vector<Task> filtered;
filter.subset(filtered);
@ -174,8 +171,6 @@ CmdZshCompletionIds::CmdZshCompletionIds() {
////////////////////////////////////////////////////////////////////////////////
int CmdZshCompletionIds::execute(std::string& output) {
// Apply filter.
handleUntil();
handleRecurrence();
Filter filter;
std::vector<Task> filtered;
filter.subset(filtered);
@ -211,8 +206,6 @@ CmdUUIDs::CmdUUIDs() {
////////////////////////////////////////////////////////////////////////////////
int CmdUUIDs::execute(std::string& output) {
// Apply filter.
handleUntil();
handleRecurrence();
Filter filter;
std::vector<Task> filtered;
filter.subset(filtered);
@ -247,8 +240,6 @@ CmdCompletionUuids::CmdCompletionUuids() {
////////////////////////////////////////////////////////////////////////////////
int CmdCompletionUuids::execute(std::string& output) {
// Apply filter.
handleUntil();
handleRecurrence();
Filter filter;
std::vector<Task> filtered;
filter.subset(filtered);
@ -283,8 +274,6 @@ CmdZshCompletionUuids::CmdZshCompletionUuids() {
////////////////////////////////////////////////////////////////////////////////
int CmdZshCompletionUuids::execute(std::string& output) {
// Apply filter.
handleUntil();
handleRecurrence();
Filter filter;
std::vector<Task> filtered;
filter.subset(filtered);

View file

@ -31,7 +31,6 @@
#include <JSON.h>
#include <string>
#include <unordered_map>
class CmdImportV2 : public Command {
public:

View file

@ -34,9 +34,9 @@
#include <Filter.h>
#include <Lexer.h>
#include <Operation.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <math.h>
#include <rules.h>
#include <shared.h>
#include <stdlib.h>
#include <taskchampion-cpp/lib.h>
@ -120,9 +120,11 @@ int CmdInfo::execute(std::string& output) {
description += '\n' + std::string(indent, ' ') +
Datetime(anno.first.substr(11)).toString(dateformatanno) + ' ' + anno.second;
if (task.has("description")) {
row = view.addRow();
view.set(row, 0, "Description");
view.set(row, 1, description, c);
}
// status
row = view.addRow();
@ -215,6 +217,7 @@ int CmdInfo::execute(std::string& output) {
}
// entry
if (task.has("entry") && task.get_date("entry")) {
row = view.addRow();
view.set(row, 0, "Entered");
Datetime dt(task.get_date("entry"));
@ -228,51 +231,62 @@ int CmdInfo::execute(std::string& output) {
}
view.set(row, 1, entry + " (" + age + ')');
}
auto validDate = [&](const char* prop) {
if (!task.has(prop)) {
return false;
}
if (task.get_date(prop) == 0) {
return false;
}
return true;
};
// wait
if (task.has("wait")) {
if (validDate("wait")) {
row = view.addRow();
view.set(row, 0, "Waiting until");
view.set(row, 1, Datetime(task.get_date("wait")).toString(dateformat));
}
// scheduled
if (task.has("scheduled")) {
if (validDate("scheduled")) {
row = view.addRow();
view.set(row, 0, "Scheduled");
view.set(row, 1, Datetime(task.get_date("scheduled")).toString(dateformat));
}
// start
if (task.has("start")) {
if (validDate("start")) {
row = view.addRow();
view.set(row, 0, "Start");
view.set(row, 1, Datetime(task.get_date("start")).toString(dateformat));
}
// due (colored)
if (task.has("due")) {
if (validDate("due")) {
row = view.addRow();
view.set(row, 0, "Due");
view.set(row, 1, Datetime(task.get_date("due")).toString(dateformat));
}
// end
if (task.has("end")) {
if (validDate("end")) {
row = view.addRow();
view.set(row, 0, "End");
view.set(row, 1, Datetime(task.get_date("end")).toString(dateformat));
}
// until
if (task.has("until")) {
if (validDate("until")) {
row = view.addRow();
view.set(row, 0, "Until");
view.set(row, 1, Datetime(task.get_date("until")).toString(dateformat));
}
// modified
if (task.has("modified")) {
if (validDate("modified")) {
row = view.addRow();
view.set(row, 0, "Last modified");
@ -632,7 +646,11 @@ std::optional<std::string> CmdInfo::formatForInfo(const std::vector<Operation>&
} else {
// Record the last start time for later duration calculation.
if (prop == "start") {
try {
last_start = Datetime(value.value()).toEpoch();
} catch (std::string) {
// ignore invalid dates
}
}
out << format("{1} set to '{2}'.", Lexer::ucFirst(prop),

View file

@ -29,8 +29,8 @@
#include <CmdLog.h>
#include <Context.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
////////////////////////////////////////////////////////////////////////////////
CmdLog::CmdLog() {

View file

@ -30,8 +30,9 @@
#include <CmdModify.h>
#include <Context.h>
#include <Filter.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <recur.h>
#include <shared.h>
#include <iostream>
@ -119,7 +120,7 @@ void CmdModify::checkConsistency(Task &before, Task &after) {
throw std::string("You cannot remove the recurrence from a recurring task.");
if ((before.getStatus() == Task::pending) && (after.getStatus() == Task::pending) &&
(after.get("end") != ""))
(before.get("end") == "") && (after.get("end") != ""))
throw format("Could not modify task {1}. You cannot set an end date on a pending task.",
before.identifier(true));
}

View file

@ -33,7 +33,6 @@
#include <Duration.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <util.h>
@ -161,6 +160,7 @@ std::vector<NewsItem> NewsItem::all() {
version3_1_0(items);
version3_2_0(items);
version3_3_0(items);
version3_4_0(items);
return items;
}
@ -531,6 +531,21 @@ void NewsItem::version3_3_0(std::vector<NewsItem>& items) {
items.push_back(info);
}
void NewsItem::version3_4_0(std::vector<NewsItem>& items) {
Version version("3.4.0");
NewsItem info{version,
/*title=*/"Read-Only Access",
/*bg_title=*/"",
/*background=*/"",
/*punchline=*/"Some Taskwarrior commands operate faster in read-only mode",
/*update=*/
"Some commands do not need to write to the DB, so can open it in read-only\n"
"mode and thus more quickly. This does not include reports (task lists),\n"
"unless the `gc` config is false. Use `rc.gc=0` in command-lines to allow\n"
"read-only access.\n\n"};
items.push_back(info);
}
////////////////////////////////////////////////////////////////////////////////
int CmdNews::execute(std::string& output) {
auto words = Context::getContext().cli2.getWords();
@ -573,61 +588,10 @@ int CmdNews::execute(std::string& output) {
}
wait_for_enter();
// Display outro
Datetime now;
Datetime beginning(2006, 11, 29);
Duration development_time = Duration(now - beginning);
Color underline = Color("underline");
std::stringstream outro;
outro << underline.colorize(bold.colorize("Taskwarrior crowdfunding\n"));
outro << format(
"Taskwarrior has been in development for {1} years but its survival\n"
"depends on your support!\n\n"
"Please consider joining our {2} fundraiser to help us fund maintenance\n"
"and development of new features:\n\n",
std::lround(static_cast<float>(development_time.days()) / 365.25), now.year());
outro << bold.colorize(" https://github.com/sponsors/GothenburgBitFactory/\n\n");
outro << "Perks are available for our sponsors.\n";
std::cout << outro.str();
// Set a mark in the config to remember which version's release notes were displayed
if (news_version < current_version) {
CmdConfig::setConfigVariable("news.version", std::string(current_version), false);
// Revert back to default signal handling after displaying the outro
signal(SIGINT, SIG_DFL);
std::string question = format(
"\nWould you like to open Taskwarrior {1} fundraising campaign to read more?", now.year());
std::vector<std::string> options{"yes", "no"};
std::vector<std::string> matches;
std::cout << question << " (YES/no) ";
std::string answer;
std::getline(std::cin, answer);
if (std::cin.eof() || trim(answer).empty())
answer = "yes";
else
lowerCase(trim(answer));
autoComplete(answer, options, matches, 1); // Hard-coded 1.
if (matches.size() == 1 && matches[0] == "yes")
#if defined(DARWIN)
system("open 'https://github.com/sponsors/GothenburgBitFactory/'");
#else
system("xdg-open 'https://github.com/sponsors/GothenburgBitFactory/'");
#endif
std::cout << std::endl;
} else
wait_for_enter(); // Do not display the outro and footnote at once
}
return 0;
}

View file

@ -53,6 +53,7 @@ class NewsItem {
static void version3_1_0(std::vector<NewsItem>&);
static void version3_2_0(std::vector<NewsItem>&);
static void version3_3_0(std::vector<NewsItem>&);
static void version3_4_0(std::vector<NewsItem>&);
private:
NewsItem(Version, const std::string&, const std::string& = "", const std::string& = "",

View file

@ -30,8 +30,8 @@
#include <CmdPrepend.h>
#include <Context.h>
#include <Filter.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <iostream>

View file

@ -32,10 +32,9 @@
#include <Filter.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <sort.h>
#include <util.h>
#include <algorithm>
#include <list>
#include <sstream>

View file

@ -30,8 +30,8 @@
#include <CmdPurge.h>
#include <Context.h>
#include <Filter.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <shared.h>
////////////////////////////////////////////////////////////////////////////////

View file

@ -32,7 +32,7 @@
#include <FS.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <legacy.h>
#include <util.h>
#include <algorithm>

View file

@ -30,8 +30,11 @@
#include <CmdStart.h>
#include <Context.h>
#include <Filter.h>
#include <dependency.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <nag.h>
#include <recur.h>
#include <util.h>
#include <iostream>

View file

@ -34,7 +34,6 @@
#include <Filter.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <stdlib.h>
#include <util.h>

View file

@ -30,8 +30,10 @@
#include <CmdStop.h>
#include <Context.h>
#include <Filter.h>
#include <dependency.h>
#include <feedback.h>
#include <format.h>
#include <main.h>
#include <recur.h>
#include <iostream>

View file

@ -33,11 +33,10 @@
#include <Filter.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <sort.h>
#include <stdlib.h>
#include <util.h>
#include <algorithm>
#include <list>
#include <sstream>

View file

@ -32,13 +32,11 @@
#include <Context.h>
#include <Filter.h>
#include <format.h>
#include <inttypes.h>
#include <shared.h>
#include <signal.h>
#include <taskchampion-cpp/lib.h>
#include <util.h>
#include <iostream>
#include <regex>
#include <sstream>
////////////////////////////////////////////////////////////////////////////////
@ -81,6 +79,10 @@ int CmdSync::execute(std::string& output) {
out << "sync.server.origin is deprecated. Use sync.server.url instead.\n";
}
// redact credentials from `server_url`, if present
std::regex remove_creds_regex("^(https?://.+):(.+)@(.+)");
std::string safe_server_url = std::regex_replace(server_url, remove_creds_regex, "$1:****@$3");
if (server_dir != "") {
if (verbose) {
out << format("Syncing with {1}", server_dir) << '\n';
@ -149,7 +151,7 @@ int CmdSync::execute(std::string& output) {
throw std::string("sync.server.client_id and sync.encryption_secret are required");
}
if (verbose) {
out << format("Syncing with sync server at {1}", server_url) << '\n';
out << format("Syncing with sync server at {1}", safe_server_url) << '\n';
}
replica->sync_to_remote(server_url, tc::uuid_from_string(client_id), encryption_secret,
avoid_snapshots);

View file

@ -32,7 +32,6 @@
#include <Filter.h>
#include <Table.h>
#include <format.h>
#include <stdlib.h>
#include <util.h>
#include <sstream>

View file

@ -33,8 +33,7 @@
#include <Filter.h>
#include <Table.h>
#include <format.h>
#include <main.h>
#include <stdlib.h>
#include <rules.h>
#include <util.h>
#include <algorithm>

View file

@ -33,7 +33,6 @@
#include <Table.h>
#include <Task.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <util.h>

View file

@ -31,12 +31,11 @@
#include <Context.h>
#include <Operation.h>
#include <Task.h>
#include <shared.h>
#include <iostream>
#include <sstream>
#include "shared.h"
////////////////////////////////////////////////////////////////////////////////
CmdUndo::CmdUndo() {
_keyword = "undo";

View file

@ -32,8 +32,6 @@
#include <Filter.h>
#include <Lexer.h>
#include <format.h>
#include <main.h>
#include <stdlib.h>
#include <sstream>

View file

@ -31,7 +31,6 @@
#include <Context.h>
#include <Datetime.h>
#include <Table.h>
#include <stdlib.h>
#include <sstream>
#ifdef HAVE_COMMIT

View file

@ -50,9 +50,7 @@
#include <CmdEdit.h>
#include <Command.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <stdlib.h>
#include <util.h>
#include <iostream>

View file

@ -31,7 +31,6 @@
#include <map>
#include <string>
#include <vector>
class Command {
public:

View file

@ -28,13 +28,11 @@
// cmake.h include header must come first
#include <Context.h>
#include <dependency.h>
#include <format.h>
#include <main.h>
#include <shared.h>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <stack>
#define STRING_DEPEND_BLOCKED "Task {1} is blocked by:"

43
src/dependency.h Normal file
View file

@ -0,0 +1,43 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2025, Tomas Babej, Paul Beckingham, Federico Hernandez,
// Tobias Predel.
//
// 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_DEPENDENCY
#define INCLUDED_DEPENDENCY
#include <cmake.h>
// cmake.h include header must come first
#include <Task.h>
#define STRING_DEPEND_BLOCKED "Task {1} is blocked by:"
bool dependencyIsCircular(const Task& task);
void dependencyChainOnComplete(Task& task);
void dependencyChainOnStart(Task& task);
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -31,13 +31,11 @@
#include <Datetime.h>
#include <Duration.h>
#include <Lexer.h>
#include <feedback.h>
#include <format.h>
#include <inttypes.h>
#include <main.h>
#include <shared.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
@ -51,7 +49,12 @@ std::string renderAttribute(const std::string& name, const std::string& value,
if (Context::getContext().columns.find(name) != Context::getContext().columns.end()) {
Column* col = Context::getContext().columns[name];
if (col && col->type() == "date" && value != "") {
Datetime d((time_t)strtoll(value.c_str(), nullptr, 10));
int64_t epoch = strtoll(value.c_str(), nullptr, 10);
// Do not try to render an un-parseable value.
if (epoch == 0) {
return value;
}
Datetime d((time_t)epoch);
if (format == "") return d.toString(Context::getContext().config.get("dateformat"));
return d.toString(format);

54
src/feedback.h Normal file
View file

@ -0,0 +1,54 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2025, Tomas Babej, Paul Beckingham, Federico Hernandez,
// Tobias Predel.
//
// 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_FEEDBACK
#define INCLUDED_FEEDBACK
#include <cmake.h>
// cmake.h include header must come first
#include <Task.h>
#include <string>
#include <vector>
std::string renderAttribute(const std::string& name, const std::string& value,
const std::string& format = "");
void feedback_affected(const std::string& effect);
void feedback_affected(const std::string& effect, int quantity);
void feedback_affected(const std::string& effect, const Task& task);
void feedback_reserved_tags(const std::string& tag);
void feedback_special_tags(const Task& task, const std::string& tag);
void feedback_unblocked(const Task& task);
void feedback_backlog();
std::string onProjectChange(Task& task, bool scope = true);
std::string onProjectChange(Task& task1, Task& task2);
std::string onExpiration(Task& task);
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -30,7 +30,6 @@
#include <Context.h>
#include <format.h>
#include <cstddef>
#include <sstream>
#define STRING_LEGACY_PRIORITY "Legacy attribute found. Please change '{1}' to '{2}'."

44
src/legacy.h Normal file
View file

@ -0,0 +1,44 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2025, Tomas Babej, Paul Beckingham, Federico Hernandez,
// Tobias Predel.
//
// 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_LEGACY
#define INCLUDED_LEGACY
#include <cmake.h>
// cmake.h include header must come first
#include <string>
void legacyColumnMap(std::string& name);
void legacySortColumnMap(std::string& name);
std::string legacyCheckForDeprecatedVariables();
std::string legacyCheckForDeprecatedColumns();
void legacyAttributeMap(std::string& name);
#endif
////////////////////////////////////////////////////////////////////////////////

@ -1 +1 @@
Subproject commit 1a06cb4caebdae3c5e58fe83e2fd2211d2959815
Subproject commit 8ad3646209c8d2e7820c3cd59319a2be3b3d221e

View file

@ -1,94 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2021, 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_MAIN
#define INCLUDED_MAIN
#include <Color.h>
#include <Context.h>
#include <Datetime.h>
#include <sys/types.h>
#include <algorithm>
#include <list>
#include <map>
#include <optional>
#include <string>
#include <vector>
// recur.cpp
void handleRecurrence();
void handleUntil();
std::optional<Datetime> checked_add_datetime(Datetime& base, time_t delta);
std::optional<Datetime> getNextRecurrence(Datetime&, std::string&);
bool generateDueDates(Task&, std::vector<Datetime>&);
void updateRecurrenceMask(Task&);
// nag.cpp
void nag(std::vector<Task>&);
// rules.cpp
void initializeColorRules();
void autoColorize(Task&, Color&);
std::string colorizeHeader(const std::string&);
std::string colorizeFootnote(const std::string&);
std::string colorizeError(const std::string&);
std::string colorizeDebug(const std::string&);
// dependency.cpp
bool dependencyIsCircular(const Task&);
void dependencyChainOnComplete(Task&);
void dependencyChainOnStart(Task&);
// feedback.cpp
std::string renderAttribute(const std::string&, const std::string&, const std::string& format = "");
void feedback_affected(const std::string&);
void feedback_affected(const std::string&, int);
void feedback_affected(const std::string&, const Task&);
void feedback_reserved_tags(const std::string&);
void feedback_special_tags(const Task&, const std::string&);
void feedback_unblocked(const Task&);
void feedback_backlog();
std::string onProjectChange(Task&, bool scope = true);
std::string onProjectChange(Task&, Task&);
std::string onExpiration(Task&);
// sort.cpp
void sort_tasks(std::vector<Task>&, std::vector<int>&, const std::string&);
void sort_projects(std::list<std::pair<std::string, int>>& sorted,
std::map<std::string, int>& allProjects);
void sort_projects(std::list<std::pair<std::string, int>>& sorted,
std::map<std::string, bool>& allProjects);
// legacy.cpp
void legacyColumnMap(std::string&);
void legacySortColumnMap(std::string&);
std::string legacyCheckForDeprecatedVariables();
std::string legacyCheckForDeprecatedColumns();
void legacyAttributeMap(std::string&);
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -25,15 +25,9 @@
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <algorithm>
// cmake.h include header must come first
#include <Context.h>
#include <iterator>
#include <unordered_set>
#include <utility>
#include <vector>
////////////////////////////////////////////////////////////////////////////////

43
src/nag.h Normal file
View file

@ -0,0 +1,43 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2025, Tomas Babej, Paul Beckingham, Federico Hernandez,
// Tobias Predel.
//
// 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_NAG
#define INCLUDED_NAG
#include <cmake.h>
// cmake.h include header must come first
#include <Task.h>
#include <vector>
////////////////////////////////////////////////////////////////////////////////
// Generates a nag message when there are READY tasks of a higher urgency.
void nag(std::vector<Task>& tasks);
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -31,25 +31,19 @@
#include <Datetime.h>
#include <Duration.h>
#include <Lexer.h>
#include <feedback.h>
#include <format.h>
#include <inttypes.h>
#include <main.h>
#include <pwd.h>
#include <stdio.h>
#include <recur.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <unicode.h>
#include <unistd.h>
#include <util.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <optional>
#include <sstream>
// Add a `time_t` delta to a Datetime, checking for and returning nullopt on integer overflow.
std::optional<Datetime> checked_add_datetime(Datetime& base, time_t delta) {
@ -288,9 +282,11 @@ std::optional<Datetime> getNextRecurrence(Datetime& current, std::string& period
else if (unicodeLatinDigit(period[0]) && period[period.length() - 1] == 'q') {
int increment = strtol(period.substr(0, period.length() - 1).c_str(), nullptr, 10);
if (increment <= 0)
throw format("Recurrence period '{1}' is equivalent to {2} and hence invalid.", period,
increment);
if (increment <= 0) {
Context::getContext().footnote(format(
"Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, increment));
return std::nullopt;
}
m += 3 * increment;
while (m > 12) {
@ -346,8 +342,11 @@ std::optional<Datetime> getNextRecurrence(Datetime& current, std::string& period
// Add the period to current, and we're done.
std::string::size_type idx = 0;
Duration p;
if (!p.parse(period, idx))
throw std::string(format("The recurrence value '{1}' is not valid.", period));
if (!p.parse(period, idx)) {
Context::getContext().footnote(
format("Warning: The recurrence value '{1}' is not valid.", period));
return std::nullopt;
}
return checked_add_datetime(current, p.toTime_t());
}

57
src/recur.h Normal file
View file

@ -0,0 +1,57 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2025, Tomas Babej, Paul Beckingham, Federico Hernandez,
// Tobias Predel.
//
// 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_RECUR
#define INCLUDED_RECUR
#include <cmake.h>
// cmake.h include header must come first
#include <Context.h>
#include <Datetime.h>
#include <Duration.h>
#include <Lexer.h>
#include <format.h>
#include <pwd.h>
#include <sys/types.h>
#include <time.h>
#include <unicode.h>
#include <unistd.h>
#include <util.h>
#include <optional>
std::optional<Datetime> checked_add_datetime(Datetime& base, time_t delta);
void handleRecurrence();
bool generateDueDates(Task& parent, std::vector<Datetime>& allDue);
std::optional<Datetime> getNextRecurrence(Datetime& current, std::string& period);
void updateRecurrenceMask(Task& task);
void handleUntil();
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -29,9 +29,8 @@
#include <Context.h>
#include <Datetime.h>
#include <main.h>
#include <rules.h>
#include <shared.h>
#include <stdlib.h>
static std::map<std::string, Color> gsColor;
static std::vector<std::string> gsPrecedence;

47
src/rules.h Normal file
View file

@ -0,0 +1,47 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2025, Tomas Babej, Paul Beckingham, Federico Hernandez,
// Tobias Predel.
//
// 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_RULES
#define INCLUDED_RULES
#include <cmake.h>
// cmake.h include header must come first
#include <Context.h>
#include <Datetime.h>
#include <shared.h>
void initializeColorRules();
void autoColorize(Task& task, Color& c);
std::string colorizeHeader(const std::string& input);
std::string colorizeFootnote(const std::string& input);
std::string colorizeError(const std::string& input);
std::string colorizeDebug(const std::string& input);
#endif
////////////////////////////////////////////////////////////////////////////////

51
src/sort.h Normal file
View file

@ -0,0 +1,51 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2025, Tomas Babej, Paul Beckingham, Federico Hernandez,
// Tobias Predel.
//
// 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_SORT
#define INCLUDED_SORT
#include <cmake.h>
// cmake.h include header must come first
#include <Task.h>
#include <list>
#include <map>
#include <string>
#include <vector>
void sort_tasks(std::vector<Task>& data, std::vector<int>& order, const std::string& keys);
void sort_projects(std::list<std::pair<std::string, int>>& sorted,
std::map<std::string, int>& allProjects);
void sort_projects(std::list<std::pair<std::string, int>>& sorted,
std::map<std::string, bool>& allProjects);
#endif
////////////////////////////////////////////////////////////////////////////////

@ -1 +1 @@
Subproject commit 64289b1d79d6d19cd2e241db515381a086bb8407
Subproject commit 715c235daef4b8ee67278f12256334ad3dd4c4ae

View file

@ -104,8 +104,11 @@ mod ffi {
fn new_replica_in_memory() -> Result<Box<Replica>>;
/// Create a new replica stored on-disk.
fn new_replica_on_disk(taskdb_dir: String, create_if_missing: bool)
-> Result<Box<Replica>>;
fn new_replica_on_disk(
taskdb_dir: String,
create_if_missing: bool,
read_write: bool,
) -> Result<Box<Replica>>;
/// Commit the given operations to the replica.
fn commit_operations(&mut self, ops: Vec<Operation>) -> Result<()>;
@ -490,11 +493,14 @@ impl From<tc::Replica> for Replica {
fn new_replica_on_disk(
taskdb_dir: String,
create_if_missing: bool,
read_write: bool,
) -> Result<Box<Replica>, CppError> {
use tc::storage::AccessMode::*;
let access_mode = if read_write { ReadWrite } else { ReadOnly };
let storage = tc::StorageConfig::OnDisk {
taskdb_dir: PathBuf::from(taskdb_dir),
create_if_missing,
access_mode: tc::storage::AccessMode::ReadWrite,
access_mode,
}
.into_storage()?;
Ok(Box::new(tc::Replica::new(storage).into()))

Some files were not shown because too many files have changed in this diff Show more