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: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
# Enable version updates for git submodules
- package-ecosystem: "gitsubmodule"
directory: "/"
schedule:
interval: "daily"
# Enable updates for Rust packages # Enable updates for Rust packages
- package-ecosystem: "cargo" - package-ecosystem: "cargo"
directory: "/" # Location of package manifests directory: "/" # Location of package manifests

View file

@ -15,25 +15,25 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Cache cargo registry - uses: dtolnay/rust-toolchain@master
uses: actions/cache@v4 id: toolchain
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
with: with:
# If this version is old enough to cause errors, or older than the # 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 MSRV, bump it to the MSRV of the currently-required
# TaskChampion package; if necessary, bump that version as well. # TaskChampion package; if necessary, bump that version as well.
toolchain: "1.81.0" # MSRV 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 - uses: actions-rs/cargo@v1.0.3
with: with:
@ -53,12 +53,11 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1 - uses: dtolnay/rust-toolchain@master
id: toolchain
with: with:
profile: minimal toolchain: "stable"
components: rustfmt components: "rustfmt"
toolchain: stable
override: true
- uses: actions-rs/cargo@v1.0.3 - uses: actions-rs/cargo@v1.0.3
with: with:
@ -71,12 +70,11 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1 - uses: dtolnay/rust-toolchain@master
id: toolchain
with: with:
profile: minimal toolchain: "stable"
components: rustfmt components: "rustfmt"
toolchain: stable
override: true
- name: "Check metadata" - name: "Check metadata"
run: ".github/workflows/metadata-check.sh" run: ".github/workflows/metadata-check.sh"

View file

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

View file

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

View file

@ -43,22 +43,20 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
id: toolchain
- name: Cache cargo registry - name: Cache cargo registry
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: ~/.cargo/registry 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 - name: Cache cargo build
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: target path: target
key: ${{ runner.os }}-stable-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/toolchain@v1
with:
toolchain: "stable"
override: true
- name: Test MacOS - name: Test MacOS
run: bash test/scripts/test_macos.sh run: bash test/scripts/test_macos.sh
@ -72,22 +70,20 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
id: toolchain
- name: Cache cargo registry - name: Cache cargo registry
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: ~/.cargo/registry 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 - name: Cache cargo build
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: target path: target
key: ${{ runner.os }}-stable-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/toolchain@v1
with:
toolchain: "stable"
override: true
- name: Test MacOS - name: Test MacOS
run: bash test/scripts/test_macos.sh run: bash test/scripts/test_macos.sh
@ -101,26 +97,25 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - 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 - name: Cache cargo registry
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: ~/.cargo/registry 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 - name: Cache cargo build
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: target path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ 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
- uses: actions-rs/cargo@v1.0.3 - uses: actions-rs/cargo@v1.0.3
with: with:

View file

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

View file

@ -4,7 +4,7 @@ enable_testing()
set (CMAKE_EXPORT_COMPILE_COMMANDS ON) set (CMAKE_EXPORT_COMPILE_COMMANDS ON)
project (task project (task
VERSION 3.3.0 VERSION 3.4.1
DESCRIPTION "Taskwarrior - a command-line TODO list manager" DESCRIPTION "Taskwarrior - a command-line TODO list manager"
HOMEPAGE_URL https://taskwarrior.org/) HOMEPAGE_URL https://taskwarrior.org/)
@ -162,6 +162,6 @@ endforeach (doc_FILE)
set (CPACK_SOURCE_GENERATOR "TGZ") set (CPACK_SOURCE_GENERATOR "TGZ")
set (CPACK_SOURCE_PACKAGE_FILE_NAME ${PACKAGE_NAME}-${PACKAGE_VERSION}) 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/") "commit.h" "cmake.h$" "\\\\.gitmodules" "src/libshared/\\\\.git" ".github/" ".*\\\\.gitignore$" "docker-compose.yml" "\\\\.git/")
include (CPack) 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 --------------------------- ------ 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. - Sync now supports AWS S3 as a backend.
- A new `task import-v2` command allows importing Taskwarrior-2.x - A new `task import-v2` command allows importing Taskwarrior-2.x
data files directly. data files directly.
3.3.0 -
Thanks to the following people for contributions to this release: Thanks to the following people for contributions to this release:
- Chongyun Lee - Chongyun Lee
@ -18,8 +43,6 @@ Thanks to the following people for contributions to this release:
- Scott Mcdermott - Scott Mcdermott
- Thomas Lauf - Thomas Lauf
------ old releases ------------------------------
3.2.0 - 3.2.0 -
- Support for the journal in `task info` has been restored (#3671) and the - 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 -S . -B build -DCMAKE_BUILD_TYPE=Release . [3]
$ cmake --build build [4] $ cmake --build build [4]
$ sudo cmake --install build [5] $ 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: 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 To uninstall Taskwarrior, remove the files listed in the install_manifest.txt
file that was generated when you built Taskwarrior. 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 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` - Add a new item in `content/news`
- Update `data/projects.json` with the latest version and a fake next version for "devel" - 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 `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 ## 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: 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 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 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 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 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: that this:
.nf .nf
task description.word:bar list task description.word:foo list
.fi .fi
Will match the description 'foo bar baz' but does not match 'dog food'. 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 .TP
.B gc=1 .B gc=1
Can be used to temporarily suspend rebuilding, so that task IDs don't change. 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 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 rc.gc=0 ...), and not permanently used in the .taskrc file, as this
significantly affects performance in the long term. 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. is weeks, hours, minutes, money, or some other resource count.
.TP .TP
.B uda.<name>.type=string|numeric|date|duration .B uda.<name>.type=string|numeric|uuid|date|duration
.RS .RS
Defines a UDA called '<name>', of the specified type. Defines a UDA called '<name>', of the specified type.
.RE .RE

View file

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

View file

@ -36,16 +36,15 @@
#include <Version.h> #include <Version.h>
#include <assert.h> #include <assert.h>
#include <format.h> #include <format.h>
#include <main.h> #include <recur.h>
#include <rules.h>
#include <rust/cxx.h> #include <rust/cxx.h>
#include <shared.h> #include <shared.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <taskchampion-cpp/lib.h> #include <taskchampion-cpp/lib.h>
#include <unistd.h> #include <unistd.h>
#include <algorithm> #include <algorithm>
#include <fstream>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <regex> #include <regex>
@ -55,7 +54,6 @@
#include <commit.h> #include <commit.h>
#endif #endif
#include <stdio.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#ifdef SOLARIS #ifdef SOLARIS
@ -598,9 +596,6 @@ int Context::initialize(int argc, const char** argv) {
createDefaultConfig(); 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. // [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'."); 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. // [8] Initialize hooks.
@ -869,7 +879,7 @@ int Context::dispatch(std::string& out) {
if (config.getBoolean("debug") && config.getInteger("debug.parser") == 1) if (config.getBoolean("debug") && config.getInteger("debug.parser") == 1)
debug(cli2.dump("Parse Tree (before command-specifіc processing)")); 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(); handleUntil();
handleRecurrence(); handleRecurrence();
} }

View file

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

View file

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

View file

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

View file

@ -44,7 +44,6 @@
#include <Variant.h> #include <Variant.h>
#include <format.h> #include <format.h>
#include <shared.h> #include <shared.h>
#include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.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 // - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code. // 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; if (!_enabled) return;
Timer timer; Timer timer;

View file

@ -40,7 +40,7 @@ class Hooks {
void onLaunch() const; void onLaunch() const;
void onExit() const; void onExit() const;
void onAdd(Task&) const; void onAdd(Task&) const;
void onModify(const Task&, Task&) const; void onModify(Task&, Task&) const;
std::vector<std::string> list() const; std::vector<std::string> list() const;
private: 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 // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -24,8 +25,8 @@
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_OPERATIOn #ifndef INCLUDED_OPERATION
#define INCLUDED_OPERATIOn #define INCLUDED_OPERATION
#include <taskchampion-cpp/lib.h> #include <taskchampion-cpp/lib.h>

View file

@ -33,16 +33,11 @@
#include <TDB2.h> #include <TDB2.h>
#include <Table.h> #include <Table.h>
#include <format.h> #include <format.h>
#include <main.h>
#include <shared.h> #include <shared.h>
#include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <util.h> #include <util.h>
#include <algorithm> #include <algorithm>
#include <iostream>
#include <list>
#include <sstream>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
@ -50,17 +45,16 @@ bool TDB2::debug_mode = false;
static void dependency_scan(std::vector<Task>&); static void dependency_scan(std::vector<Task>&);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TDB2::open_replica(const std::string& location, bool 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); _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. // Add the new task to the replica.
void TDB2::add(Task& task) { 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. // Ensure the task is consistent, and provide defaults if necessary.
// bool argument to validate() is "applyDefault", to apply default values for // bool argument to validate() is "applyDefault", to apply default values for
// properties not otherwise given. // properties not otherwise given.
@ -194,11 +188,8 @@ void TDB2::purge(Task& task) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
rust::Box<tc::Replica>& TDB2::replica() { rust::Box<tc::Replica>& TDB2::replica() {
// Create a replica in-memory if `open_replica` has not been called. This // One of the open_replica_ methods must be called before this one.
// occurs in tests. assert(_replica);
if (!_replica) {
_replica = tc::new_replica_in_memory();
}
return _replica.value(); return _replica.value();
} }
@ -363,8 +354,7 @@ bool TDB2::get(const std::string& uuid, Task& task) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Locate task by UUID, wherever it is. // Locate task by UUID, wherever it is.
bool TDB2::has(const std::string& uuid) { bool TDB2::has(const std::string& uuid) {
Task task; return replica()->get_task_data(tc::uuid_from_string(uuid)).is_some();
return get(uuid, task);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -27,9 +27,7 @@
#ifndef INCLUDED_TDB2 #ifndef INCLUDED_TDB2
#define INCLUDED_TDB2 #define INCLUDED_TDB2
#include <FS.h>
#include <Task.h> #include <Task.h>
#include <stdio.h>
#include <taskchampion-cpp/lib.h> #include <taskchampion-cpp/lib.h>
#include <map> #include <map>
@ -46,7 +44,8 @@ class TDB2 {
TDB2() = default; 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 add(Task &);
void modify(Task &); void modify(Task &);
void purge(Task &); void purge(Task &);

View file

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

View file

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

View file

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

View file

@ -30,7 +30,6 @@
#include <Datetime.h> #include <Datetime.h>
#include <JSON.h> #include <JSON.h>
#include <Table.h> #include <Table.h>
#include <stdio.h>
#include <taskchampion-cpp/lib.h> #include <taskchampion-cpp/lib.h>
#include <time.h> #include <time.h>
@ -69,7 +68,7 @@ class Task {
Task(rust::Box<tc::TaskData>); Task(rust::Box<tc::TaskData>);
void parse(const std::string&); void parse(const std::string&);
std::string composeJSON(bool decorate = false) const; std::string composeJSON(bool decorate = false);
// Status values. // Status values.
enum status { pending, completed, deleted, recurring, waiting }; 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_BOOL "Cannot divide duration by Boolean"
#define STRING_VARIANT_DIV_DUR_STR "Cannot divide durations by strings" #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_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_BOOL "Cannot modulo Booleans"
#define STRING_VARIANT_MOD_DATE "Cannot modulo date values" #define STRING_VARIANT_MOD_DATE "Cannot modulo date values"
#define STRING_VARIANT_MOD_DUR "Cannot modulo duration 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); throw std::string(STRING_VARIANT_DIV_DUR_DATE);
case type_duration: 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; break;
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -56,11 +56,9 @@ void ColumnTypeDuration::modify(Task& task, const std::string& value) {
evaluatedValue = Variant(value); evaluatedValue = Variant(value);
} }
// The duration is stored in raw form, but it must still be valid, // The duration is first parsed, then stored inside the variant as a `time_t`
// and therefore is parsed first.
std::string label = " MODIFICATION "; std::string label = " MODIFICATION ";
if (evaluatedValue.type() == Variant::type_duration) { if (evaluatedValue.type() == Variant::type_duration) {
// Store the raw value, for 'recur'.
Context::getContext().debug(label + _name + " <-- " + (std::string)evaluatedValue + " <-- '" + Context::getContext().debug(label + _name + " <-- " + (std::string)evaluatedValue + " <-- '" +
value + '\''); value + '\'');
task.set(_name, evaluatedValue); 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 <ColTypeDuration.h>
#include <ColTypeNumeric.h> #include <ColTypeNumeric.h>
#include <ColTypeString.h> #include <ColTypeString.h>
#include <ColUUID.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
class ColumnUDAString : public ColumnTypeString { class ColumnUDAString : public ColumnTypeString {
@ -83,5 +84,12 @@ class ColumnUDADuration : public ColumnTypeDuration {
std::vector<std::string> _values; std::vector<std::string> _values;
}; };
////////////////////////////////////////////////////////////////////////////////
class ColumnUDAUUID : public ColumnUUID {
public:
ColumnUDAUUID();
bool validate(const std::string&) const;
};
#endif #endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -246,9 +246,15 @@ Column* Column::uda(const std::string& name) {
c->_label = label; c->_label = label;
if (values != "") c->_values = split(values, ','); if (values != "") c->_values = split(values, ',');
return c; return c;
} else if (type == "uuid") {
auto c = new ColumnUDAUUID();
c->_name = name;
c->_label = label;
return c;
} else if (type != "") } else if (type != "")
throw std::string( 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; return nullptr;
} }

View file

@ -29,8 +29,8 @@
#include <CmdAdd.h> #include <CmdAdd.h>
#include <Context.h> #include <Context.h>
#include <feedback.h>
#include <format.h> #include <format.h>
#include <main.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
CmdAdd::CmdAdd() { 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 // the task is empty, but DOM references can refer to earlier parts of the
// command line, e.g., `task add due:20110101 wait:due`. // command line, e.g., `task add due:20110101 wait:due`.
task.modify(Task::modReplace, true); 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); Context::getContext().tdb2.add(task);
// Do not display ID 0, users cannot query by that // Do not display ID 0, users cannot query by that

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -33,7 +33,6 @@
#include <Duration.h> #include <Duration.h>
#include <Table.h> #include <Table.h>
#include <format.h> #include <format.h>
#include <main.h>
#include <shared.h> #include <shared.h>
#include <util.h> #include <util.h>
@ -161,6 +160,7 @@ std::vector<NewsItem> NewsItem::all() {
version3_1_0(items); version3_1_0(items);
version3_2_0(items); version3_2_0(items);
version3_3_0(items); version3_3_0(items);
version3_4_0(items);
return items; return items;
} }
@ -531,6 +531,21 @@ void NewsItem::version3_3_0(std::vector<NewsItem>& items) {
items.push_back(info); 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) { int CmdNews::execute(std::string& output) {
auto words = Context::getContext().cli2.getWords(); auto words = Context::getContext().cli2.getWords();
@ -573,61 +588,10 @@ int CmdNews::execute(std::string& output) {
} }
wait_for_enter(); 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 // Set a mark in the config to remember which version's release notes were displayed
if (news_version < current_version) { if (news_version < current_version) {
CmdConfig::setConfigVariable("news.version", std::string(current_version), false); 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; return 0;
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -32,13 +32,11 @@
#include <Context.h> #include <Context.h>
#include <Filter.h> #include <Filter.h>
#include <format.h> #include <format.h>
#include <inttypes.h>
#include <shared.h> #include <shared.h>
#include <signal.h>
#include <taskchampion-cpp/lib.h> #include <taskchampion-cpp/lib.h>
#include <util.h> #include <util.h>
#include <iostream> #include <regex>
#include <sstream> #include <sstream>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -81,6 +79,10 @@ int CmdSync::execute(std::string& output) {
out << "sync.server.origin is deprecated. Use sync.server.url instead.\n"; 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 (server_dir != "") {
if (verbose) { if (verbose) {
out << format("Syncing with {1}", server_dir) << '\n'; 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"); throw std::string("sync.server.client_id and sync.encryption_secret are required");
} }
if (verbose) { 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, replica->sync_to_remote(server_url, tc::uuid_from_string(client_id), encryption_secret,
avoid_snapshots); avoid_snapshots);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -28,13 +28,11 @@
// cmake.h include header must come first // cmake.h include header must come first
#include <Context.h> #include <Context.h>
#include <dependency.h>
#include <format.h> #include <format.h>
#include <main.h>
#include <shared.h> #include <shared.h>
#include <algorithm>
#include <iostream> #include <iostream>
#include <sstream>
#include <stack> #include <stack>
#define STRING_DEPEND_BLOCKED "Task {1} is blocked by:" #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 <Datetime.h>
#include <Duration.h> #include <Duration.h>
#include <Lexer.h> #include <Lexer.h>
#include <feedback.h>
#include <format.h> #include <format.h>
#include <inttypes.h>
#include <main.h>
#include <shared.h> #include <shared.h>
#include <stdlib.h> #include <stdlib.h>
#include <algorithm>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #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()) { if (Context::getContext().columns.find(name) != Context::getContext().columns.end()) {
Column* col = Context::getContext().columns[name]; Column* col = Context::getContext().columns[name];
if (col && col->type() == "date" && value != "") { 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")); if (format == "") return d.toString(Context::getContext().config.get("dateformat"));
return d.toString(format); 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 <Context.h>
#include <format.h> #include <format.h>
#include <cstddef>
#include <sstream> #include <sstream>
#define STRING_LEGACY_PRIORITY "Legacy attribute found. Please change '{1}' to '{2}'." #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 <cmake.h>
#include <algorithm>
// cmake.h include header must come first // cmake.h include header must come first
#include <Context.h> #include <Context.h>
#include <iterator>
#include <unordered_set>
#include <utility>
#include <vector> #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 <Datetime.h>
#include <Duration.h> #include <Duration.h>
#include <Lexer.h> #include <Lexer.h>
#include <feedback.h>
#include <format.h> #include <format.h>
#include <inttypes.h>
#include <main.h>
#include <pwd.h> #include <pwd.h>
#include <stdio.h> #include <recur.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <time.h> #include <time.h>
#include <unicode.h> #include <unicode.h>
#include <unistd.h> #include <unistd.h>
#include <util.h> #include <util.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits> #include <limits>
#include <optional> #include <optional>
#include <sstream>
// Add a `time_t` delta to a Datetime, checking for and returning nullopt on integer overflow. // 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) { 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') { else if (unicodeLatinDigit(period[0]) && period[period.length() - 1] == 'q') {
int increment = strtol(period.substr(0, period.length() - 1).c_str(), nullptr, 10); int increment = strtol(period.substr(0, period.length() - 1).c_str(), nullptr, 10);
if (increment <= 0) if (increment <= 0) {
throw format("Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, Context::getContext().footnote(format(
increment); "Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, increment));
return std::nullopt;
}
m += 3 * increment; m += 3 * increment;
while (m > 12) { 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. // Add the period to current, and we're done.
std::string::size_type idx = 0; std::string::size_type idx = 0;
Duration p; Duration p;
if (!p.parse(period, idx)) if (!p.parse(period, idx)) {
throw std::string(format("The recurrence value '{1}' is not valid.", period)); Context::getContext().footnote(
format("Warning: The recurrence value '{1}' is not valid.", period));
return std::nullopt;
}
return checked_add_datetime(current, p.toTime_t()); 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 <Context.h>
#include <Datetime.h> #include <Datetime.h>
#include <main.h> #include <rules.h>
#include <shared.h> #include <shared.h>
#include <stdlib.h>
static std::map<std::string, Color> gsColor; static std::map<std::string, Color> gsColor;
static std::vector<std::string> gsPrecedence; 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>>; fn new_replica_in_memory() -> Result<Box<Replica>>;
/// Create a new replica stored on-disk. /// Create a new replica stored on-disk.
fn new_replica_on_disk(taskdb_dir: String, create_if_missing: bool) fn new_replica_on_disk(
-> Result<Box<Replica>>; taskdb_dir: String,
create_if_missing: bool,
read_write: bool,
) -> Result<Box<Replica>>;
/// Commit the given operations to the replica. /// Commit the given operations to the replica.
fn commit_operations(&mut self, ops: Vec<Operation>) -> Result<()>; fn commit_operations(&mut self, ops: Vec<Operation>) -> Result<()>;
@ -490,11 +493,14 @@ impl From<tc::Replica> for Replica {
fn new_replica_on_disk( fn new_replica_on_disk(
taskdb_dir: String, taskdb_dir: String,
create_if_missing: bool, create_if_missing: bool,
read_write: bool,
) -> Result<Box<Replica>, CppError> { ) -> Result<Box<Replica>, CppError> {
use tc::storage::AccessMode::*;
let access_mode = if read_write { ReadWrite } else { ReadOnly };
let storage = tc::StorageConfig::OnDisk { let storage = tc::StorageConfig::OnDisk {
taskdb_dir: PathBuf::from(taskdb_dir), taskdb_dir: PathBuf::from(taskdb_dir),
create_if_missing, create_if_missing,
access_mode: tc::storage::AccessMode::ReadWrite, access_mode,
} }
.into_storage()?; .into_storage()?;
Ok(Box::new(tc::Replica::new(storage).into())) Ok(Box::new(tc::Replica::new(storage).into()))

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