From a9995808eca56e87841f153c82694c142a83fd6f Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 21 Apr 2024 08:55:38 -0400 Subject: [PATCH 001/242] Update cmake support for git submodules (#3383) --- CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 37ef3631e..5cb1ca7f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,14 +24,14 @@ if (ENABLE_WASM) set(CMAKE_EXECUTABLE_SUFFIX ".js") endif (ENABLE_WASM) -message ("-- Looking for libshared") -if (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src) - message ("-- Found libshared") +message ("-- Looking for git submodules") +if (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src AND EXISTS ${CMAKE_SOURCE_DIR}/src/tc/corrosion) + message ("-- Found git submodules") else (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src) - message ("-- Cloning libshared") + message ("-- Cloning git submodules") execute_process (COMMAND git submodule update --init WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -endif (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src) +endif (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src AND EXISTS ${CMAKE_SOURCE_DIR}/src/tc/corrosion) message ("-- Looking for SHA1 references") if (EXISTS ${CMAKE_SOURCE_DIR}/.git/index) From 9b35ab37aa996fde75e62214a8c8eff963e64838 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 22 Apr 2024 20:01:26 -0400 Subject: [PATCH 002/242] Remove debug print (#3389) --- src/Version.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Version.cpp b/src/Version.cpp index 56d01afb9..bbce61253 100644 --- a/src/Version.cpp +++ b/src/Version.cpp @@ -95,7 +95,6 @@ bool Version::operator==(const Version &other) const { //////////////////////////////////////////////////////////////////////////////// bool Version::operator!=(const Version &other) const { - std::cout << other; return std::tie(major, minor, patch) != std::tie(other.major, other.minor, other.patch); } From bc86a1e53f641e38d66df40c1a085830ea876d47 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 22 Apr 2024 20:18:38 -0400 Subject: [PATCH 003/242] Release 3.0.2 (#3394) --- CMakeLists.txt | 2 +- ChangeLog | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cb1ca7f0..79900b169 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required (VERSION 3.22) set (CMAKE_EXPORT_COMPILE_COMMANDS ON) project (task - VERSION 3.0.1 + VERSION 3.0.2 DESCRIPTION "Taskwarrior - a command-line TODO list manager" HOMEPAGE_URL https://taskwarrior.org/) diff --git a/ChangeLog b/ChangeLog index 9f56ea666..3f7d99266 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ ------ current release --------------------------- +3.0.2 - + +- Fix an accidentally-included debug print which polluted output of + reports with the Taskwarrior version (#3389) + +------ old releases ------------------------------ + + 3.0.1 - - Fix an error in creation of the 3.0.0 tarball which caused builds to fail (#3302) @@ -8,8 +16,6 @@ - Fix incorrect task ID of 0 when using hooks (#3339) - Issue a warning if .data files remain (#3321) ------- old releases ------------------------------ - 3.0.0 - - [BREAKING CHANGE] the sync functionality has been rewritten entirely, and From eaef05ee95934e74db0f19dd1470c8424b17962c Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:31:36 +0300 Subject: [PATCH 004/242] Update fedora 38 docker container to fedora 40 (#3396) --- .github/workflows/tests.yaml | 4 ++-- docker-compose.yml | 4 ++-- test/docker/{fedora38 => fedora40} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename test/docker/{fedora38 => fedora40} (98%) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index cd3f64d83..36df18ce8 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -66,9 +66,9 @@ jobs: fail-fast: false matrix: include: - - name: "Fedora 38" + - name: "Fedora 40" runner: ubuntu-latest - dockerfile: fedora38 + dockerfile: fedora40 - name: "Fedora 39" runner: ubuntu-latest dockerfile: fedora39 diff --git a/docker-compose.yml b/docker-compose.yml index 836e638fe..3bf2e0903 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,9 @@ version: '3' services: - test-fedora38: + test-fedora40: build: context: . - dockerfile: test/docker/fedora38 + dockerfile: test/docker/fedora40 network_mode: "host" security_opt: - label=type:container_runtime_t diff --git a/test/docker/fedora38 b/test/docker/fedora40 similarity index 98% rename from test/docker/fedora38 rename to test/docker/fedora40 index e254f1eb2..8625f5da0 100644 --- a/test/docker/fedora38 +++ b/test/docker/fedora40 @@ -1,4 +1,4 @@ -FROM fedora:38 +FROM fedora:40 RUN dnf update -y RUN dnf install python3 git gcc gcc-c++ cmake make libuuid-devel libfaketime glibc-langpack-en curl -y From 7dba5e76951a7e4cc6833c42bbcc5bfb6d288154 Mon Sep 17 00:00:00 2001 From: sleepy_nols <138515901+sleepy-nols@users.noreply.github.com> Date: Sun, 28 Apr 2024 21:24:42 +0200 Subject: [PATCH 005/242] update '.data' warning message to '*.data' for better readability (#3409) TDB2: update '.data' warning to '*.data' for better readability, closes #3406 --- src/TDB2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 42b747be5..561511d98 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -62,7 +62,7 @@ void TDB2::open_replica (const std::string& location, bool create_if_missing) if (pending_data.exists()) { Color warning = Color (Context::getContext ().config.get ("color.warning")); std::cerr << warning.colorize ( - format ("Found existing '.data' files in {1}", location)) << "\n"; + format ("Found existing '*.data' files in {1}", location)) << "\n"; std::cerr << " Taskwarrior's storage format changed in 3.0, requiring a manual migration.\n"; std::cerr << " See https://github.com/GothenburgBitFactory/taskwarrior/releases.\n"; } From d093ce3d844939abebcbfb9f18f1342341f18bbf Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Sun, 28 Apr 2024 22:38:14 +0300 Subject: [PATCH 006/242] Fix test script paths (#3387) * fix path to task executable in pyton tests The current approach would copy the current files into the `build/test` directory. Updating the paths according to the custom user setup. By the copy I appended `.py` to have a clear visible distingtion which ones are the python tests. As soon as a source file in the normal directory is changed, it is copied over and the corresponding file is updated. From now on the python tests would need to get run in the according build directory. * reflect the current build instruction in PR template * update paths and globing in run_all * add line break for every cpp test * remove .gitignore in test folder As now all the auxillary files such as `all.log` as well as the executables are present in the `build` directory there is no longer a need to ignore them. * update paths in python test scripts and enable deactivated * remove .py extension when copy to build Further remove glob pattern for `*.t.py` tests. * remove accidentally added template.t from test files --- .github/pull_request_template.md | 2 +- test/.gitignore | 41 -------- test/CMakeLists.txt | 163 ++++++++++++++++++++++++++++++- test/basetest/CMakeLists.txt | 8 ++ test/basetest/utils.py | 6 +- test/bash_completion.t | 2 +- test/run_all | 4 +- test/simpletap/CMakeLists.txt | 1 + test/template.t | 0 test/tw-1379.t | 2 +- test/tw-1837.t | 0 test/tw-2575.t | 0 12 files changed, 179 insertions(+), 50 deletions(-) delete mode 100644 test/.gitignore create mode 100644 test/basetest/CMakeLists.txt create mode 100644 test/simpletap/CMakeLists.txt mode change 100644 => 100755 test/template.t mode change 100644 => 100755 test/tw-1837.t mode change 100644 => 100755 test/tw-2575.t diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 724bf7829..221297751 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,7 +5,7 @@ Replace this text with a description of the PR. #### Additional information... - [ ] I changed C++ code or build infrastructure. - Please run the test suite and include the output of `cd test && ./problems`. + Please run the test suite and include the output of `cd build/test && make && ./problems`. - [ ] I changed Rust code or build infrastructure. Please run `cargo test` and address any failures before submitting. diff --git a/test/.gitignore b/test/.gitignore deleted file mode 100644 index cdb132ea3..000000000 --- a/test/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -*.o -*.pyc -*.data -*.sqlite3 -*.log -*.runlog -col.t -dom.t -eval.t -lexer.t -t.t -taskmod.t -tdb2.t -uri.t -util.t -variant_add.t -variant_and.t -variant_cast.t -variant_divide.t -variant_equal.t -variant_exp.t -variant_gt.t -variant_gte.t -variant_inequal.t -variant_lt.t -variant_lte.t -variant_match.t -variant_math.t -variant_modulo.t -variant_multiply.t -variant_nomatch.t -variant_not.t -variant_or.t -variant_partial.t -variant_subtract.t -variant_xor.t -view.t -tc.t -tw-2689.t - -json_test diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 482014895..853abd814 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,7 +18,39 @@ include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/taskchampion/lib ${TASK_INCLUDE_DIRS}) -set (test_SRCS col.t dom.t eval.t lexer.t t.t tw-2689.t tdb2.t tc.t util.t variant_add.t variant_and.t variant_cast.t variant_divide.t variant_equal.t variant_exp.t variant_gt.t variant_gte.t variant_inequal.t variant_lt.t variant_lte.t variant_match.t variant_math.t variant_modulo.t variant_multiply.t variant_nomatch.t variant_not.t variant_or.t variant_partial.t variant_subtract.t variant_xor.t view.t) +set (test_SRCS + col.t + dom.t + eval.t + lexer.t + t.t + tw-2689.t + tdb2.t + tc.t + util.t + variant_add.t + variant_and.t + variant_cast.t + variant_divide.t + variant_equal.t + variant_exp.t + variant_gt.t + variant_gte.t + variant_inequal.t + variant_lt.t + variant_lte.t + variant_match.t + variant_math.t + variant_modulo.t + variant_multiply.t + variant_nomatch.t + variant_not.t + variant_or.t + variant_partial.t + variant_subtract.t + variant_xor.t + view.t + ) add_custom_target (test ./run_all --verbose DEPENDS ${test_SRCS} task_executable @@ -37,6 +69,135 @@ endforeach (src_FILE) configure_file(run_all run_all COPYONLY) configure_file(problems problems COPYONLY) +configure_file(bash_tap.sh bash_tap.sh COPYONLY) +configure_file(bash_tap_tw.sh bash_tap_tw.sh COPYONLY) + +add_subdirectory(basetest) +add_subdirectory(simpletap) +set (pythonTests + abbreviation.t + add.t + alias.t + annotate.t + append.t + args.t + bash_completion.t + blocked.t + bulk.t + burndown.t + calc.t + calendar.t + caseless.t + color.cmd.t + color.rules.t + columns.t + commands.t + completed.t + configuration.t + confirmation.t + context.t + count.t + custom.config.t + custom.recur_ind.t + custom.t + custom.tag_ind.t + date.iso.t + dateformat.t + datesort.t + datetime-negative.t + debug.t + default.t + delete.t + denotate.t + dependencies.t + diag.t + diag_color.t + dom2.t + due.t + duplicate.t + edit.t + encoding.t + enpassant.t + exec.t + export.t + feature.559.t + feature.default.project.t + feature.print.empty.columns.t + feature.recurrence.t + feedback.t + filter.t + fontunderline.t + format.t + gc.t + helpers.t + history.t + hooks.env.t + hooks.on-add.t + hooks.on-launch.t + hooks.on-modify.t + hyphenate.t + ids.t + import.t + info.t + limit.t + list.all.projects.t + log.t + logo.t + math.t + modify.t + nag.t + obfuscate.t + oldest.t + operators.t + overdue.t + partial.t + prepend.t + pri_sort.t + project.t + quotes.t + rc.override.t + recurrence.t + reports.t + search.t + sequence.t + shell.t + show.t + sorting.t + special.t + start.t + stats.t + substitute.t + sugar.t + summary.t + tag.t + taskrc.t + timesheet.t + tw-1379.t + tw-1837.t + tw-20.t + tw-2575.t + tw-262.t + tw-295.t + uda.t + uda_orphan.t + uda_report.t + uda_sort.t + undo.t + unicode.t + unique.t + upgrade.t + urgency.t + urgency_inherit.t + uuid.t + verbose.t + version.t + wait.t + hooks.on-exit.t + ) + +foreach (python_Test ${pythonTests}) + configure_file(${python_Test} ${python_Test} COPYONLY) +endforeach(python_Test) #SET(CMAKE_BUILD_TYPE gcov) #SET(CMAKE_CXX_FLAGS_GCOV "--coverage") diff --git a/test/basetest/CMakeLists.txt b/test/basetest/CMakeLists.txt new file mode 100644 index 000000000..f78450e77 --- /dev/null +++ b/test/basetest/CMakeLists.txt @@ -0,0 +1,8 @@ +configure_file(__init__.py __init__.py COPYONLY) +configure_file(compat.py compat.py COPYONLY) +configure_file(exceptions.py exceptions.py COPYONLY) +configure_file(hooks.py hooks.py COPYONLY) +configure_file(meta.py meta.py COPYONLY) +configure_file(task.py task.py COPYONLY) +configure_file(testing.py testing.py COPYONLY) +configure_file(utils.py utils.py) \ No newline at end of file diff --git a/test/basetest/utils.py b/test/basetest/utils.py index 0c8a941d0..c6a1970eb 100644 --- a/test/basetest/utils.py +++ b/test/basetest/utils.py @@ -29,17 +29,17 @@ CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) # Location of binary files (usually the src/ folder) BIN_PREFIX = os.path.abspath( - os.path.join(CURRENT_DIR, "..", "..", "src") + os.path.join("${CMAKE_BINARY_DIR}","src") ) # Default location of test certificates DEFAULT_CERT_PATH = os.path.abspath( - os.path.join(CURRENT_DIR, "..", "test_certs") + os.path.join("${CMAKE_SOURCE_DIR}", "test", "test_certs") ) # Default location of test hooks DEFAULT_HOOK_PATH = os.path.abspath( - os.path.join(CURRENT_DIR, "..", "test_hooks") + os.path.join("${CMAKE_SOURCE_DIR}", "test", "test_hooks") ) diff --git a/test/bash_completion.t b/test/bash_completion.t index c2e479bf2..7c6de41e1 100755 --- a/test/bash_completion.t +++ b/test/bash_completion.t @@ -36,7 +36,7 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase from basetest.utils import BIN_PREFIX -TASKSH = os.path.abspath(os.path.join(BIN_PREFIX, "..", "scripts/bash/task.sh")) +TASKSH = os.path.abspath(os.path.join(BIN_PREFIX, "..", "..", "scripts/bash/task.sh")) @contextmanager diff --git a/test/run_all b/test/run_all index 8668d685f..e27e3c571 100755 --- a/test/run_all +++ b/test/run_all @@ -70,7 +70,7 @@ class TestRunner(object): self._outputq = Queue() def _find_tests(self): - for test in glob.glob("*.t") + glob.glob("*.t.exe"): + for test in glob.glob("*.t"): if os.access(test, os.X_OK): # Executables only if self._is_parallelizable(test): @@ -123,7 +123,7 @@ class TestRunner(object): with open(test, 'rb') as fh: header = fh.read(100).split(b"\n") if len(header) >= 2 and \ - ((b"/usr/bin/env python3" in header[0]) or \ + ((b"!#/usr/bin/env python3" in header[0]) or \ (header[1][-14:] == b"bash_tap_tw.sh")): return True else: diff --git a/test/simpletap/CMakeLists.txt b/test/simpletap/CMakeLists.txt new file mode 100644 index 000000000..040a465d6 --- /dev/null +++ b/test/simpletap/CMakeLists.txt @@ -0,0 +1 @@ +configure_file(__init__.py __init__.py COPYONLY) \ No newline at end of file diff --git a/test/template.t b/test/template.t old mode 100644 new mode 100755 diff --git a/test/tw-1379.t b/test/tw-1379.t index 98050fc57..07077ecbc 100755 --- a/test/tw-1379.t +++ b/test/tw-1379.t @@ -41,7 +41,7 @@ class TestBug1379(TestCase): self.t = Task() # Themes are a special case that cannot be set via "task config" with open(self.t.taskrc, 'a') as fh: - fh.write("include " + REPO_DIR + "/doc/rc/no-color.theme\n") + fh.write("include " + REPO_DIR + "/../doc/rc/no-color.theme\n") self.t.config("color.alternate", "") self.t.config("_forcecolor", "1") diff --git a/test/tw-1837.t b/test/tw-1837.t old mode 100644 new mode 100755 diff --git a/test/tw-2575.t b/test/tw-2575.t old mode 100644 new mode 100755 From 43ca74549d1a9a7930d9ded7ab24eda16cef0dcb Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 30 Apr 2024 14:54:42 -0400 Subject: [PATCH 007/242] Include the whole error message in errors from Rust (#3415) --- taskchampion/lib/src/util.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/taskchampion/lib/src/util.rs b/taskchampion/lib/src/util.rs index bfd739282..61223e6bf 100644 --- a/taskchampion/lib/src/util.rs +++ b/taskchampion/lib/src/util.rs @@ -1,7 +1,15 @@ use crate::string::RustString; -pub(crate) fn err_to_ruststring(e: impl std::string::ToString) -> RustString<'static> { - RustString::from(e.to_string()) +pub(crate) fn err_to_ruststring(e: anyhow::Error) -> RustString<'static> { + // The default `to_string` representation of `anyhow::Error` only shows the "outermost" + // context, e.g., "Could not connect to server", and omits the juicy details about what + // actually went wrong. So, join all of those contexts with `: ` for presentation to the C++ + // layer. + let entire_msg = e + .chain() + .skip(1) + .fold(e.to_string(), |a, b| format!("{}: {}", a, b)); + RustString::from(entire_msg) } /// An implementation of Vec::into_raw_parts, which is still unstable. Returns ptr, len, cap. From ef9613e2d610d81bf3ea88eaefc51ad188707773 Mon Sep 17 00:00:00 2001 From: mattsmida <147877353+mattsmida@users.noreply.github.com> Date: Wed, 1 May 2024 14:28:07 -0400 Subject: [PATCH 008/242] Renaming test files according to their language (#3407) --- test/CMakeLists.txt | 303 +++++++++--------- test/README | 19 +- test/{abbreviation.t => abbreviation.test.py} | 0 test/{add.t => add.test.py} | 0 test/{alias.t => alias.test.py} | 0 test/{annotate.t => annotate.test.py} | 0 test/{append.t => append.test.py} | 0 test/{args.t => args.test.py} | 0 ...h_completion.t => bash_completion.test.py} | 0 test/{blocked.t => blocked.test.py} | 0 test/{bulk.t => bulk.test.py} | 0 test/{burndown.t => burndown.test.py} | 0 test/{calc.t => calc.test.py} | 0 test/{calendar.t => calendar.test.py} | 0 test/{caseless.t => caseless.test.py} | 0 test/{col.t.cpp => col.test.cpp} | 0 test/{color.cmd.t => color.cmd.test.py} | 0 test/{color.rules.t => color.rules.test.py} | 0 test/{columns.t => columns.test.py} | 0 test/{commands.t => commands.test.py} | 0 test/{completed.t => completed.test.py} | 0 ...{configuration.t => configuration.test.py} | 0 test/{confirmation.t => confirmation.test.py} | 0 test/{context.t => context.test.py} | 0 test/conversion | 12 +- test/{count.t => count.test.py} | 0 ...{custom.config.t => custom.config.test.py} | 0 ...m.recur_ind.t => custom.recur_ind.test.py} | 0 ...ustom.tag_ind.t => custom.tag_ind.test.py} | 0 test/{custom.t => custom.test.py} | 0 test/{date.iso.t => date.iso.test.py} | 0 test/{dateformat.t => dateformat.test.py} | 0 test/{datesort.t => datesort.test.py} | 0 ...e-negative.t => datetime-negative.test.py} | 0 test/{debug.t => debug.test.py} | 0 test/{default.t => default.test.py} | 0 test/{delete.t => delete.test.py} | 0 test/{denotate.t => denotate.test.py} | 0 test/{dependencies.t => dependencies.test.py} | 0 test/{diag.t => diag.test.py} | 0 test/{diag_color.t => diag_color.test.py} | 0 test/{dom.t.cpp => dom.test.cpp} | 0 test/{dom2.t => dom2.test.py} | 0 test/{due.t => due.test.py} | 0 test/{duplicate.t => duplicate.test.py} | 0 test/{edit.t => edit.test.py} | 0 test/{encoding.t => encoding.test.py} | 0 test/{enpassant.t => enpassant.test.py} | 0 test/{eval.t.cpp => eval.test.cpp} | 0 test/{exec.t => exec.test.py} | 0 test/{export.t => export.test.py} | 0 test/{feature.559.t => feature.559.test.py} | 0 ...ject.t => feature.default.project.test.py} | 0 ....t => feature.print.empty.columns.test.py} | 0 ...ecurrence.t => feature.recurrence.test.py} | 0 test/{feedback.t => feedback.test.py} | 0 test/{filter.t => filter.test.py} | 0 ...{fontunderline.t => fontunderline.test.py} | 0 test/{format.t => format.test.py} | 0 test/{gc.t => gc.test.py} | 0 test/{helpers.t => helpers.test.py} | 0 test/{history.t => history.test.py} | 0 test/{hooks.env.t => hooks.env.test.py} | 0 test/{hooks.on-add.t => hooks.on-add.test.py} | 0 ...{hooks.on-exit.t => hooks.on-exit.test.py} | 0 ...ks.on-launch.t => hooks.on-launch.test.py} | 0 ...ks.on-modify.t => hooks.on-modify.test.py} | 0 test/{hyphenate.t => hyphenate.test.py} | 0 test/{ids.t => ids.test.py} | 0 test/{import.t => import.test.py} | 0 test/{info.t => info.test.py} | 0 test/{lexer.t.cpp => lexer.test.cpp} | 0 test/{limit.t => limit.test.py} | 0 ...l.projects.t => list.all.projects.test.py} | 0 test/{log.t => log.test.py} | 0 test/{logo.t => logo.test.py} | 0 test/{math.t => math.test.py} | 0 test/{modify.t => modify.test.py} | 0 test/{nag.t => nag.test.py} | 0 test/{obfuscate.t => obfuscate.test.py} | 0 test/{oldest.t => oldest.test.py} | 0 test/{operators.t => operators.test.py} | 0 test/{overdue.t => overdue.test.py} | 0 test/{partial.t => partial.test.py} | 0 test/{prepend.t => prepend.test.py} | 0 test/{pri_sort.t => pri_sort.test.py} | 0 test/problems | 2 +- test/{project.t => project.test.py} | 0 test/{quotes.t => quotes.test.py} | 0 test/{rc.override.t => rc.override.test.py} | 0 test/{recurrence.t => recurrence.test.py} | 0 test/{reports.t => reports.test.py} | 0 test/run_all | 2 +- test/{search.t => search.test.py} | 0 test/{sequence.t => sequence.test.py} | 0 test/{shell.t => shell.test.py} | 0 test/{show.t => show.test.py} | 0 test/{sorting.t => sorting.test.py} | 0 test/{special.t => special.test.py} | 0 test/{start.t => start.test.py} | 0 test/{stats.t => stats.test.py} | 0 test/stress_test | 2 +- test/{substitute.t => substitute.test.py} | 0 test/{sugar.t => sugar.test.py} | 0 test/{summary.t => summary.test.py} | 0 test/{t.t.cpp => t.test.cpp} | 0 test/{tag.t => tag.test.py} | 0 test/{taskrc.t => taskrc.test.py} | 0 test/{tc.t.cpp => tc.test.cpp} | 0 test/{tdb2.t.cpp => tdb2.test.cpp} | 0 test/{template.t => template.test.py} | 0 test/{timesheet.t => timesheet.test.py} | 0 test/{tw-1379.t => tw-1379.test.py} | 0 test/{tw-1637.t => tw-1637.test.sh} | 0 test/{tw-1643.t => tw-1643.test.sh} | 0 test/{tw-1688.t => tw-1688.test.sh} | 0 test/{tw-1715.t => tw-1715.test.sh} | 0 test/{tw-1718.t => tw-1718.test.sh} | 0 test/{tw-1804.t => tw-1804.test.sh} | 0 test/{tw-1837.t => tw-1837.test.py} | 0 test/{tw-1883.t => tw-1883.test.sh} | 0 test/{tw-1895.t => tw-1895.test.sh} | 0 test/{tw-1938.t => tw-1938.test.sh} | 0 test/{tw-20.t => tw-20.test.py} | 0 test/{tw-2124.t => tw-2124.test.sh} | 0 test/{tw-2189.t => tw-2189.test.sh} | 0 test/{tw-2257.t => tw-2257.test.sh} | 0 test/{tw-2386.t => tw-2386.test.sh} | 0 test/{tw-2392.t => tw-2392.test.sh} | 0 test/{tw-2429.t => tw-2429.test.sh} | 0 test/{tw-2451.t => tw-2451.test.sh} | 0 test/{tw-2514.t => tw-2514.test.sh} | 0 test/{tw-2530.t => tw-2530.test.sh} | 0 test/{tw-2550.t => tw-2550.test.sh} | 0 test/{tw-2575.t => tw-2575.test.py} | 0 test/{tw-2581.t => tw-2581.test.sh} | 0 test/{tw-262.t => tw-262.test.py} | 0 test/{tw-2689.t.cpp => tw-2689.test.cpp} | 0 test/{tw-295.t => tw-295.test.py} | 0 test/{tw-3102.t => tw-3102.test.sh} | 0 test/{tw-3109.t => tw-3109.test.sh} | 0 test/{uda.t => uda.test.py} | 0 test/{uda_orphan.t => uda_orphan.test.py} | 0 test/{uda_report.t => uda_report.test.py} | 0 test/{uda_sort.t => uda_sort.test.py} | 0 test/{undo.t => undo.test.py} | 0 test/{unicode.t => unicode.test.py} | 0 test/{unique.t => unique.test.py} | 0 test/{upgrade.t => upgrade.test.py} | 0 test/{urgency.t => urgency.test.py} | 0 ...ency_inherit.t => urgency_inherit.test.py} | 0 test/{util.t.cpp => util.test.cpp} | 0 test/{uuid.t => uuid.test.py} | 0 ...variant_add.t.cpp => variant_add.test.cpp} | 0 ...variant_and.t.cpp => variant_and.test.cpp} | 0 ...riant_cast.t.cpp => variant_cast.test.cpp} | 0 ...t_divide.t.cpp => variant_divide.test.cpp} | 0 ...ant_equal.t.cpp => variant_equal.test.cpp} | 0 ...variant_exp.t.cpp => variant_exp.test.cpp} | 0 .../{variant_gt.t.cpp => variant_gt.test.cpp} | 0 ...variant_gte.t.cpp => variant_gte.test.cpp} | 0 ...inequal.t.cpp => variant_inequal.test.cpp} | 0 .../{variant_lt.t.cpp => variant_lt.test.cpp} | 0 ...variant_lte.t.cpp => variant_lte.test.cpp} | 0 ...ant_match.t.cpp => variant_match.test.cpp} | 0 ...riant_math.t.cpp => variant_math.test.cpp} | 0 ...t_modulo.t.cpp => variant_modulo.test.cpp} | 0 ...ltiply.t.cpp => variant_multiply.test.cpp} | 0 ...nomatch.t.cpp => variant_nomatch.test.cpp} | 0 ...variant_not.t.cpp => variant_not.test.cpp} | 0 .../{variant_or.t.cpp => variant_or.test.cpp} | 0 ...partial.t.cpp => variant_partial.test.cpp} | 0 ...btract.t.cpp => variant_subtract.test.cpp} | 0 ...variant_xor.t.cpp => variant_xor.test.cpp} | 0 test/{verbose.t => verbose.test.py} | 0 test/{version.t => version.test.py} | 0 test/{view.t.cpp => view.test.cpp} | 0 test/{wait.t => wait.test.py} | 0 178 files changed, 171 insertions(+), 169 deletions(-) rename test/{abbreviation.t => abbreviation.test.py} (100%) rename test/{add.t => add.test.py} (100%) rename test/{alias.t => alias.test.py} (100%) rename test/{annotate.t => annotate.test.py} (100%) rename test/{append.t => append.test.py} (100%) rename test/{args.t => args.test.py} (100%) rename test/{bash_completion.t => bash_completion.test.py} (100%) rename test/{blocked.t => blocked.test.py} (100%) rename test/{bulk.t => bulk.test.py} (100%) rename test/{burndown.t => burndown.test.py} (100%) rename test/{calc.t => calc.test.py} (100%) rename test/{calendar.t => calendar.test.py} (100%) rename test/{caseless.t => caseless.test.py} (100%) rename test/{col.t.cpp => col.test.cpp} (100%) rename test/{color.cmd.t => color.cmd.test.py} (100%) rename test/{color.rules.t => color.rules.test.py} (100%) rename test/{columns.t => columns.test.py} (100%) rename test/{commands.t => commands.test.py} (100%) rename test/{completed.t => completed.test.py} (100%) rename test/{configuration.t => configuration.test.py} (100%) rename test/{confirmation.t => confirmation.test.py} (100%) rename test/{context.t => context.test.py} (100%) rename test/{count.t => count.test.py} (100%) rename test/{custom.config.t => custom.config.test.py} (100%) rename test/{custom.recur_ind.t => custom.recur_ind.test.py} (100%) rename test/{custom.tag_ind.t => custom.tag_ind.test.py} (100%) rename test/{custom.t => custom.test.py} (100%) rename test/{date.iso.t => date.iso.test.py} (100%) rename test/{dateformat.t => dateformat.test.py} (100%) rename test/{datesort.t => datesort.test.py} (100%) rename test/{datetime-negative.t => datetime-negative.test.py} (100%) rename test/{debug.t => debug.test.py} (100%) rename test/{default.t => default.test.py} (100%) rename test/{delete.t => delete.test.py} (100%) rename test/{denotate.t => denotate.test.py} (100%) rename test/{dependencies.t => dependencies.test.py} (100%) rename test/{diag.t => diag.test.py} (100%) rename test/{diag_color.t => diag_color.test.py} (100%) rename test/{dom.t.cpp => dom.test.cpp} (100%) rename test/{dom2.t => dom2.test.py} (100%) rename test/{due.t => due.test.py} (100%) rename test/{duplicate.t => duplicate.test.py} (100%) rename test/{edit.t => edit.test.py} (100%) rename test/{encoding.t => encoding.test.py} (100%) rename test/{enpassant.t => enpassant.test.py} (100%) rename test/{eval.t.cpp => eval.test.cpp} (100%) rename test/{exec.t => exec.test.py} (100%) rename test/{export.t => export.test.py} (100%) rename test/{feature.559.t => feature.559.test.py} (100%) rename test/{feature.default.project.t => feature.default.project.test.py} (100%) rename test/{feature.print.empty.columns.t => feature.print.empty.columns.test.py} (100%) rename test/{feature.recurrence.t => feature.recurrence.test.py} (100%) rename test/{feedback.t => feedback.test.py} (100%) rename test/{filter.t => filter.test.py} (100%) rename test/{fontunderline.t => fontunderline.test.py} (100%) rename test/{format.t => format.test.py} (100%) rename test/{gc.t => gc.test.py} (100%) rename test/{helpers.t => helpers.test.py} (100%) rename test/{history.t => history.test.py} (100%) rename test/{hooks.env.t => hooks.env.test.py} (100%) rename test/{hooks.on-add.t => hooks.on-add.test.py} (100%) rename test/{hooks.on-exit.t => hooks.on-exit.test.py} (100%) rename test/{hooks.on-launch.t => hooks.on-launch.test.py} (100%) rename test/{hooks.on-modify.t => hooks.on-modify.test.py} (100%) rename test/{hyphenate.t => hyphenate.test.py} (100%) rename test/{ids.t => ids.test.py} (100%) rename test/{import.t => import.test.py} (100%) rename test/{info.t => info.test.py} (100%) rename test/{lexer.t.cpp => lexer.test.cpp} (100%) rename test/{limit.t => limit.test.py} (100%) rename test/{list.all.projects.t => list.all.projects.test.py} (100%) rename test/{log.t => log.test.py} (100%) rename test/{logo.t => logo.test.py} (100%) rename test/{math.t => math.test.py} (100%) rename test/{modify.t => modify.test.py} (100%) rename test/{nag.t => nag.test.py} (100%) rename test/{obfuscate.t => obfuscate.test.py} (100%) rename test/{oldest.t => oldest.test.py} (100%) rename test/{operators.t => operators.test.py} (100%) rename test/{overdue.t => overdue.test.py} (100%) rename test/{partial.t => partial.test.py} (100%) rename test/{prepend.t => prepend.test.py} (100%) rename test/{pri_sort.t => pri_sort.test.py} (100%) rename test/{project.t => project.test.py} (100%) rename test/{quotes.t => quotes.test.py} (100%) rename test/{rc.override.t => rc.override.test.py} (100%) rename test/{recurrence.t => recurrence.test.py} (100%) rename test/{reports.t => reports.test.py} (100%) rename test/{search.t => search.test.py} (100%) rename test/{sequence.t => sequence.test.py} (100%) rename test/{shell.t => shell.test.py} (100%) rename test/{show.t => show.test.py} (100%) rename test/{sorting.t => sorting.test.py} (100%) rename test/{special.t => special.test.py} (100%) rename test/{start.t => start.test.py} (100%) rename test/{stats.t => stats.test.py} (100%) rename test/{substitute.t => substitute.test.py} (100%) rename test/{sugar.t => sugar.test.py} (100%) rename test/{summary.t => summary.test.py} (100%) rename test/{t.t.cpp => t.test.cpp} (100%) rename test/{tag.t => tag.test.py} (100%) rename test/{taskrc.t => taskrc.test.py} (100%) rename test/{tc.t.cpp => tc.test.cpp} (100%) rename test/{tdb2.t.cpp => tdb2.test.cpp} (100%) rename test/{template.t => template.test.py} (100%) rename test/{timesheet.t => timesheet.test.py} (100%) rename test/{tw-1379.t => tw-1379.test.py} (100%) rename test/{tw-1637.t => tw-1637.test.sh} (100%) rename test/{tw-1643.t => tw-1643.test.sh} (100%) rename test/{tw-1688.t => tw-1688.test.sh} (100%) rename test/{tw-1715.t => tw-1715.test.sh} (100%) rename test/{tw-1718.t => tw-1718.test.sh} (100%) rename test/{tw-1804.t => tw-1804.test.sh} (100%) rename test/{tw-1837.t => tw-1837.test.py} (100%) rename test/{tw-1883.t => tw-1883.test.sh} (100%) rename test/{tw-1895.t => tw-1895.test.sh} (100%) rename test/{tw-1938.t => tw-1938.test.sh} (100%) rename test/{tw-20.t => tw-20.test.py} (100%) rename test/{tw-2124.t => tw-2124.test.sh} (100%) rename test/{tw-2189.t => tw-2189.test.sh} (100%) rename test/{tw-2257.t => tw-2257.test.sh} (100%) rename test/{tw-2386.t => tw-2386.test.sh} (100%) rename test/{tw-2392.t => tw-2392.test.sh} (100%) rename test/{tw-2429.t => tw-2429.test.sh} (100%) rename test/{tw-2451.t => tw-2451.test.sh} (100%) rename test/{tw-2514.t => tw-2514.test.sh} (100%) rename test/{tw-2530.t => tw-2530.test.sh} (100%) rename test/{tw-2550.t => tw-2550.test.sh} (100%) rename test/{tw-2575.t => tw-2575.test.py} (100%) rename test/{tw-2581.t => tw-2581.test.sh} (100%) rename test/{tw-262.t => tw-262.test.py} (100%) rename test/{tw-2689.t.cpp => tw-2689.test.cpp} (100%) rename test/{tw-295.t => tw-295.test.py} (100%) rename test/{tw-3102.t => tw-3102.test.sh} (100%) rename test/{tw-3109.t => tw-3109.test.sh} (100%) rename test/{uda.t => uda.test.py} (100%) rename test/{uda_orphan.t => uda_orphan.test.py} (100%) rename test/{uda_report.t => uda_report.test.py} (100%) rename test/{uda_sort.t => uda_sort.test.py} (100%) rename test/{undo.t => undo.test.py} (100%) rename test/{unicode.t => unicode.test.py} (100%) rename test/{unique.t => unique.test.py} (100%) rename test/{upgrade.t => upgrade.test.py} (100%) rename test/{urgency.t => urgency.test.py} (100%) rename test/{urgency_inherit.t => urgency_inherit.test.py} (100%) rename test/{util.t.cpp => util.test.cpp} (100%) rename test/{uuid.t => uuid.test.py} (100%) rename test/{variant_add.t.cpp => variant_add.test.cpp} (100%) rename test/{variant_and.t.cpp => variant_and.test.cpp} (100%) rename test/{variant_cast.t.cpp => variant_cast.test.cpp} (100%) rename test/{variant_divide.t.cpp => variant_divide.test.cpp} (100%) rename test/{variant_equal.t.cpp => variant_equal.test.cpp} (100%) rename test/{variant_exp.t.cpp => variant_exp.test.cpp} (100%) rename test/{variant_gt.t.cpp => variant_gt.test.cpp} (100%) rename test/{variant_gte.t.cpp => variant_gte.test.cpp} (100%) rename test/{variant_inequal.t.cpp => variant_inequal.test.cpp} (100%) rename test/{variant_lt.t.cpp => variant_lt.test.cpp} (100%) rename test/{variant_lte.t.cpp => variant_lte.test.cpp} (100%) rename test/{variant_match.t.cpp => variant_match.test.cpp} (100%) rename test/{variant_math.t.cpp => variant_math.test.cpp} (100%) rename test/{variant_modulo.t.cpp => variant_modulo.test.cpp} (100%) rename test/{variant_multiply.t.cpp => variant_multiply.test.cpp} (100%) rename test/{variant_nomatch.t.cpp => variant_nomatch.test.cpp} (100%) rename test/{variant_not.t.cpp => variant_not.test.cpp} (100%) rename test/{variant_or.t.cpp => variant_or.test.cpp} (100%) rename test/{variant_partial.t.cpp => variant_partial.test.cpp} (100%) rename test/{variant_subtract.t.cpp => variant_subtract.test.cpp} (100%) rename test/{variant_xor.t.cpp => variant_xor.test.cpp} (100%) rename test/{verbose.t => verbose.test.py} (100%) rename test/{version.t => version.test.py} (100%) rename test/{view.t.cpp => view.test.cpp} (100%) rename test/{wait.t => wait.test.py} (100%) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 853abd814..7f7d9d6db 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,37 +19,37 @@ include_directories (${CMAKE_SOURCE_DIR} ${TASK_INCLUDE_DIRS}) set (test_SRCS - col.t - dom.t - eval.t - lexer.t - t.t - tw-2689.t - tdb2.t - tc.t - util.t - variant_add.t - variant_and.t - variant_cast.t - variant_divide.t - variant_equal.t - variant_exp.t - variant_gt.t - variant_gte.t - variant_inequal.t - variant_lt.t - variant_lte.t - variant_match.t - variant_math.t - variant_modulo.t - variant_multiply.t - variant_nomatch.t - variant_not.t - variant_or.t - variant_partial.t - variant_subtract.t - variant_xor.t - view.t + col.test.cpp + dom.test.cpp + eval.test.cpp + lexer.test.cpp + t.test.cpp + tw-2689.test.cpp + tdb2.test.cpp + tc.test.cpp + util.test.cpp + variant_add.test.cpp + variant_and.test.cpp + variant_cast.test.cpp + variant_divide.test.cpp + variant_equal.test.cpp + variant_exp.test.cpp + variant_gt.test.cpp + variant_gte.test.cpp + variant_inequal.test.cpp + variant_lt.test.cpp + variant_lte.test.cpp + variant_match.test.cpp + variant_math.test.cpp + variant_modulo.test.cpp + variant_multiply.test.cpp + variant_nomatch.test.cpp + variant_not.test.cpp + variant_or.test.cpp + variant_partial.test.cpp + variant_subtract.test.cpp + variant_xor.test.cpp + view.test.cpp ) add_custom_target (test ./run_all --verbose @@ -60,7 +60,7 @@ add_custom_target (build_tests DEPENDS ${test_SRCS} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/test) foreach (src_FILE ${test_SRCS}) - add_executable (${src_FILE} "${src_FILE}.cpp" test.cpp) + add_executable (${src_FILE} ${src_FILE} test.cpp) target_link_libraries (${src_FILE} task tc commands columns libshared task tc commands columns libshared task commands columns libshared ${TASK_LIBRARIES}) if (DARWIN) target_link_libraries (${src_FILE} "-framework CoreFoundation -framework Security -framework SystemConfiguration") @@ -74,125 +74,126 @@ configure_file(bash_tap_tw.sh bash_tap_tw.sh COPYONLY) add_subdirectory(basetest) add_subdirectory(simpletap) + set (pythonTests - abbreviation.t - add.t - alias.t - annotate.t - append.t - args.t - bash_completion.t - blocked.t - bulk.t - burndown.t - calc.t - calendar.t - caseless.t - color.cmd.t - color.rules.t - columns.t - commands.t - completed.t - configuration.t - confirmation.t - context.t - count.t - custom.config.t - custom.recur_ind.t - custom.t - custom.tag_ind.t - date.iso.t - dateformat.t - datesort.t - datetime-negative.t - debug.t - default.t - delete.t - denotate.t - dependencies.t - diag.t - diag_color.t - dom2.t - due.t - duplicate.t - edit.t - encoding.t - enpassant.t - exec.t - export.t - feature.559.t - feature.default.project.t - feature.print.empty.columns.t - feature.recurrence.t - feedback.t - filter.t - fontunderline.t - format.t - gc.t - helpers.t - history.t - hooks.env.t - hooks.on-add.t - hooks.on-launch.t - hooks.on-modify.t - hyphenate.t - ids.t - import.t - info.t - limit.t - list.all.projects.t - log.t - logo.t - math.t - modify.t - nag.t - obfuscate.t - oldest.t - operators.t - overdue.t - partial.t - prepend.t - pri_sort.t - project.t - quotes.t - rc.override.t - recurrence.t - reports.t - search.t - sequence.t - shell.t - show.t - sorting.t - special.t - start.t - stats.t - substitute.t - sugar.t - summary.t - tag.t - taskrc.t - timesheet.t - tw-1379.t - tw-1837.t - tw-20.t - tw-2575.t - tw-262.t - tw-295.t - uda.t - uda_orphan.t - uda_report.t - uda_sort.t - undo.t - unicode.t - unique.t - upgrade.t - urgency.t - urgency_inherit.t - uuid.t - verbose.t - version.t - wait.t - hooks.on-exit.t + abbreviation.test.py + add.test.py + alias.test.py + annotate.test.py + append.test.py + args.test.py + bash_completion.test.py + blocked.test.py + bulk.test.py + burndown.test.py + calc.test.py + calendar.test.py + caseless.test.py + color.cmd.test.py + color.rules.test.py + columns.test.py + commands.test.py + completed.test.py + configuration.test.py + confirmation.test.py + context.test.py + count.test.py + custom.config.test.py + custom.recur_ind.test.py + custom.test.py + custom.tag_ind.test.py + date.iso.test.py + dateformat.test.py + datesort.test.py + datetime-negative.test.py + debug.test.py + default.test.py + delete.test.py + denotate.test.py + dependencies.test.py + diag.test.py + diag_color.test.py + dom2.test.py + due.test.py + duplicate.test.py + edit.test.py + encoding.test.py + enpassant.test.py + exec.test.py + export.test.py + feature.559.test.py + feature.default.project.test.py + feature.print.empty.columns.test.py + feature.recurrence.test.py + feedback.test.py + filter.test.py + fontunderline.test.py + format.test.py + gc.test.py + helpers.test.py + history.test.py + hooks.env.test.py + hooks.on-add.test.py + hooks.on-launch.test.py + hooks.on-modify.test.py + hyphenate.test.py + ids.test.py + import.test.py + info.test.py + limit.test.py + list.all.projects.test.py + log.test.py + logo.test.py + math.test.py + modify.test.py + nag.test.py + obfuscate.test.py + oldest.test.py + operators.test.py + overdue.test.py + partial.test.py + prepend.test.py + pri_sort.test.py + project.test.py + quotes.test.py + rc.override.test.py + recurrence.test.py + reports.test.py + search.test.py + sequence.test.py + shell.test.py + show.test.py + sorting.test.py + special.test.py + start.test.py + stats.test.py + substitute.test.py + sugar.test.py + summary.test.py + tag.test.py + taskrc.test.py + timesheet.test.py + tw-1379.test.py + tw-1837.test.py + tw-20.test.py + tw-2575.test.py + tw-262.test.py + tw-295.test.py + uda.test.py + uda_orphan.test.py + uda_report.test.py + uda_sort.test.py + undo.test.py + unicode.test.py + unique.test.py + upgrade.test.py + urgency.test.py + urgency_inherit.test.py + uuid.test.py + verbose.test.py + version.test.py + wait.test.py + hooks.on-exit.test.py ) foreach (python_Test ${pythonTests}) @@ -202,4 +203,4 @@ endforeach(python_Test) #SET(CMAKE_BUILD_TYPE gcov) #SET(CMAKE_CXX_FLAGS_GCOV "--coverage") #SET(CMAKE_C_FLAGS_GCOV "--coverage") -#SET(CMAKE_EXE_LINKER_FLAGS_GCOV "--coverage") +#SET(CMAKE_EXE_LINKER_FLAGS_GCOV "--coverage") \ No newline at end of file diff --git a/test/README b/test/README index c6334c8ee..30ba98b8c 100644 --- a/test/README +++ b/test/README @@ -38,19 +38,20 @@ There are three varieties of tests: very fast tests, and are exhaustive in nature. * Python unit tests that are at the highest level, exercising the command - line, hooks and syncing. There is an example, 'template.t', that shows how - to perform various high level tests. + line, hooks and syncing. There is an example, 'template.test.py', that + shows how to perform various high level tests. * Bash unit tests, one test per file, using the bash_tap_tw.sh script. These tests are small, quick tests, not intended to be permanent. -All tests are named with the pattern '*.t', and any other forms are not run by -the test harness. Additionally a test must be set executable (chmod +x) for it -to be run. In the case of Python tests one can still run them manually by -launching them with 'python test.t' or simply './test.t'. It also allows us to -keep tests submitted for bugs that are not scheduled to be fixed in the -upcoming release, and we don't want the failing tests to prevent us from seeing -100% pass rate for the bugs we *have* fixed. +All tests are named with the pattern '*.test.py', '*.test.sh', or '*.test.cpp', +and any other forms are not run by the test harness. Additionally a test must +be set executable (chmod +x) for it to be run. In the case of Python tests one +can still run them manually by launching them with 'python testname.test.py' or +simply './testname.test.py'. It also allows us to keep tests submitted for bugs +that are not scheduled to be fixed in the upcoming release, and we don't want +the failing tests to prevent us from seeing 100% pass rate for the bugs we +*have* fixed. Goals diff --git a/test/abbreviation.t b/test/abbreviation.test.py similarity index 100% rename from test/abbreviation.t rename to test/abbreviation.test.py diff --git a/test/add.t b/test/add.test.py similarity index 100% rename from test/add.t rename to test/add.test.py diff --git a/test/alias.t b/test/alias.test.py similarity index 100% rename from test/alias.t rename to test/alias.test.py diff --git a/test/annotate.t b/test/annotate.test.py similarity index 100% rename from test/annotate.t rename to test/annotate.test.py diff --git a/test/append.t b/test/append.test.py similarity index 100% rename from test/append.t rename to test/append.test.py diff --git a/test/args.t b/test/args.test.py similarity index 100% rename from test/args.t rename to test/args.test.py diff --git a/test/bash_completion.t b/test/bash_completion.test.py similarity index 100% rename from test/bash_completion.t rename to test/bash_completion.test.py diff --git a/test/blocked.t b/test/blocked.test.py similarity index 100% rename from test/blocked.t rename to test/blocked.test.py diff --git a/test/bulk.t b/test/bulk.test.py similarity index 100% rename from test/bulk.t rename to test/bulk.test.py diff --git a/test/burndown.t b/test/burndown.test.py similarity index 100% rename from test/burndown.t rename to test/burndown.test.py diff --git a/test/calc.t b/test/calc.test.py similarity index 100% rename from test/calc.t rename to test/calc.test.py diff --git a/test/calendar.t b/test/calendar.test.py similarity index 100% rename from test/calendar.t rename to test/calendar.test.py diff --git a/test/caseless.t b/test/caseless.test.py similarity index 100% rename from test/caseless.t rename to test/caseless.test.py diff --git a/test/col.t.cpp b/test/col.test.cpp similarity index 100% rename from test/col.t.cpp rename to test/col.test.cpp diff --git a/test/color.cmd.t b/test/color.cmd.test.py similarity index 100% rename from test/color.cmd.t rename to test/color.cmd.test.py diff --git a/test/color.rules.t b/test/color.rules.test.py similarity index 100% rename from test/color.rules.t rename to test/color.rules.test.py diff --git a/test/columns.t b/test/columns.test.py similarity index 100% rename from test/columns.t rename to test/columns.test.py diff --git a/test/commands.t b/test/commands.test.py similarity index 100% rename from test/commands.t rename to test/commands.test.py diff --git a/test/completed.t b/test/completed.test.py similarity index 100% rename from test/completed.t rename to test/completed.test.py diff --git a/test/configuration.t b/test/configuration.test.py similarity index 100% rename from test/configuration.t rename to test/configuration.test.py diff --git a/test/confirmation.t b/test/confirmation.test.py similarity index 100% rename from test/confirmation.t rename to test/confirmation.test.py diff --git a/test/context.t b/test/context.test.py similarity index 100% rename from test/context.t rename to test/context.test.py diff --git a/test/conversion b/test/conversion index 687c848ce..7b271134b 100755 --- a/test/conversion +++ b/test/conversion @@ -1,10 +1,10 @@ #!/bin/sh -printf "C++: %5d\n" $(ls *.t.cpp | wc -l) -printf "Python: %5d\n" $(head -n1 *.t | grep -a '\bpython' | wc -l) -printf "Bash: %5d\n" $(head -n1 *.t | grep -a '\bbash' | wc -l) +printf "C++: %5d\n" $(ls *.test.cpp | wc -l) +printf "Python: %5d\n" $(head -n1 *.test.py | grep -a '\bpython' | wc -l) +printf "Bash: %5d\n" $(head -n1 *.test.sh | grep -a '\bbash' | wc -l) echo -printf "Feature %5d\n" $(ls feature.*.t | wc -l) -printf "Bug %5d\n" $(ls tw-*.t | wc -l) +printf "Feature %5d\n" $(ls feature.*.test.py | wc -l) +printf "Bug %5d\n" $(ls tw-*.test.sh | wc -l) echo -printf "Total: %5d\n" $(ls *.t | wc -l) +printf "Total: %5d\n" $(ls *.test.* | wc -l) diff --git a/test/count.t b/test/count.test.py similarity index 100% rename from test/count.t rename to test/count.test.py diff --git a/test/custom.config.t b/test/custom.config.test.py similarity index 100% rename from test/custom.config.t rename to test/custom.config.test.py diff --git a/test/custom.recur_ind.t b/test/custom.recur_ind.test.py similarity index 100% rename from test/custom.recur_ind.t rename to test/custom.recur_ind.test.py diff --git a/test/custom.tag_ind.t b/test/custom.tag_ind.test.py similarity index 100% rename from test/custom.tag_ind.t rename to test/custom.tag_ind.test.py diff --git a/test/custom.t b/test/custom.test.py similarity index 100% rename from test/custom.t rename to test/custom.test.py diff --git a/test/date.iso.t b/test/date.iso.test.py similarity index 100% rename from test/date.iso.t rename to test/date.iso.test.py diff --git a/test/dateformat.t b/test/dateformat.test.py similarity index 100% rename from test/dateformat.t rename to test/dateformat.test.py diff --git a/test/datesort.t b/test/datesort.test.py similarity index 100% rename from test/datesort.t rename to test/datesort.test.py diff --git a/test/datetime-negative.t b/test/datetime-negative.test.py similarity index 100% rename from test/datetime-negative.t rename to test/datetime-negative.test.py diff --git a/test/debug.t b/test/debug.test.py similarity index 100% rename from test/debug.t rename to test/debug.test.py diff --git a/test/default.t b/test/default.test.py similarity index 100% rename from test/default.t rename to test/default.test.py diff --git a/test/delete.t b/test/delete.test.py similarity index 100% rename from test/delete.t rename to test/delete.test.py diff --git a/test/denotate.t b/test/denotate.test.py similarity index 100% rename from test/denotate.t rename to test/denotate.test.py diff --git a/test/dependencies.t b/test/dependencies.test.py similarity index 100% rename from test/dependencies.t rename to test/dependencies.test.py diff --git a/test/diag.t b/test/diag.test.py similarity index 100% rename from test/diag.t rename to test/diag.test.py diff --git a/test/diag_color.t b/test/diag_color.test.py similarity index 100% rename from test/diag_color.t rename to test/diag_color.test.py diff --git a/test/dom.t.cpp b/test/dom.test.cpp similarity index 100% rename from test/dom.t.cpp rename to test/dom.test.cpp diff --git a/test/dom2.t b/test/dom2.test.py similarity index 100% rename from test/dom2.t rename to test/dom2.test.py diff --git a/test/due.t b/test/due.test.py similarity index 100% rename from test/due.t rename to test/due.test.py diff --git a/test/duplicate.t b/test/duplicate.test.py similarity index 100% rename from test/duplicate.t rename to test/duplicate.test.py diff --git a/test/edit.t b/test/edit.test.py similarity index 100% rename from test/edit.t rename to test/edit.test.py diff --git a/test/encoding.t b/test/encoding.test.py similarity index 100% rename from test/encoding.t rename to test/encoding.test.py diff --git a/test/enpassant.t b/test/enpassant.test.py similarity index 100% rename from test/enpassant.t rename to test/enpassant.test.py diff --git a/test/eval.t.cpp b/test/eval.test.cpp similarity index 100% rename from test/eval.t.cpp rename to test/eval.test.cpp diff --git a/test/exec.t b/test/exec.test.py similarity index 100% rename from test/exec.t rename to test/exec.test.py diff --git a/test/export.t b/test/export.test.py similarity index 100% rename from test/export.t rename to test/export.test.py diff --git a/test/feature.559.t b/test/feature.559.test.py similarity index 100% rename from test/feature.559.t rename to test/feature.559.test.py diff --git a/test/feature.default.project.t b/test/feature.default.project.test.py similarity index 100% rename from test/feature.default.project.t rename to test/feature.default.project.test.py diff --git a/test/feature.print.empty.columns.t b/test/feature.print.empty.columns.test.py similarity index 100% rename from test/feature.print.empty.columns.t rename to test/feature.print.empty.columns.test.py diff --git a/test/feature.recurrence.t b/test/feature.recurrence.test.py similarity index 100% rename from test/feature.recurrence.t rename to test/feature.recurrence.test.py diff --git a/test/feedback.t b/test/feedback.test.py similarity index 100% rename from test/feedback.t rename to test/feedback.test.py diff --git a/test/filter.t b/test/filter.test.py similarity index 100% rename from test/filter.t rename to test/filter.test.py diff --git a/test/fontunderline.t b/test/fontunderline.test.py similarity index 100% rename from test/fontunderline.t rename to test/fontunderline.test.py diff --git a/test/format.t b/test/format.test.py similarity index 100% rename from test/format.t rename to test/format.test.py diff --git a/test/gc.t b/test/gc.test.py similarity index 100% rename from test/gc.t rename to test/gc.test.py diff --git a/test/helpers.t b/test/helpers.test.py similarity index 100% rename from test/helpers.t rename to test/helpers.test.py diff --git a/test/history.t b/test/history.test.py similarity index 100% rename from test/history.t rename to test/history.test.py diff --git a/test/hooks.env.t b/test/hooks.env.test.py similarity index 100% rename from test/hooks.env.t rename to test/hooks.env.test.py diff --git a/test/hooks.on-add.t b/test/hooks.on-add.test.py similarity index 100% rename from test/hooks.on-add.t rename to test/hooks.on-add.test.py diff --git a/test/hooks.on-exit.t b/test/hooks.on-exit.test.py similarity index 100% rename from test/hooks.on-exit.t rename to test/hooks.on-exit.test.py diff --git a/test/hooks.on-launch.t b/test/hooks.on-launch.test.py similarity index 100% rename from test/hooks.on-launch.t rename to test/hooks.on-launch.test.py diff --git a/test/hooks.on-modify.t b/test/hooks.on-modify.test.py similarity index 100% rename from test/hooks.on-modify.t rename to test/hooks.on-modify.test.py diff --git a/test/hyphenate.t b/test/hyphenate.test.py similarity index 100% rename from test/hyphenate.t rename to test/hyphenate.test.py diff --git a/test/ids.t b/test/ids.test.py similarity index 100% rename from test/ids.t rename to test/ids.test.py diff --git a/test/import.t b/test/import.test.py similarity index 100% rename from test/import.t rename to test/import.test.py diff --git a/test/info.t b/test/info.test.py similarity index 100% rename from test/info.t rename to test/info.test.py diff --git a/test/lexer.t.cpp b/test/lexer.test.cpp similarity index 100% rename from test/lexer.t.cpp rename to test/lexer.test.cpp diff --git a/test/limit.t b/test/limit.test.py similarity index 100% rename from test/limit.t rename to test/limit.test.py diff --git a/test/list.all.projects.t b/test/list.all.projects.test.py similarity index 100% rename from test/list.all.projects.t rename to test/list.all.projects.test.py diff --git a/test/log.t b/test/log.test.py similarity index 100% rename from test/log.t rename to test/log.test.py diff --git a/test/logo.t b/test/logo.test.py similarity index 100% rename from test/logo.t rename to test/logo.test.py diff --git a/test/math.t b/test/math.test.py similarity index 100% rename from test/math.t rename to test/math.test.py diff --git a/test/modify.t b/test/modify.test.py similarity index 100% rename from test/modify.t rename to test/modify.test.py diff --git a/test/nag.t b/test/nag.test.py similarity index 100% rename from test/nag.t rename to test/nag.test.py diff --git a/test/obfuscate.t b/test/obfuscate.test.py similarity index 100% rename from test/obfuscate.t rename to test/obfuscate.test.py diff --git a/test/oldest.t b/test/oldest.test.py similarity index 100% rename from test/oldest.t rename to test/oldest.test.py diff --git a/test/operators.t b/test/operators.test.py similarity index 100% rename from test/operators.t rename to test/operators.test.py diff --git a/test/overdue.t b/test/overdue.test.py similarity index 100% rename from test/overdue.t rename to test/overdue.test.py diff --git a/test/partial.t b/test/partial.test.py similarity index 100% rename from test/partial.t rename to test/partial.test.py diff --git a/test/prepend.t b/test/prepend.test.py similarity index 100% rename from test/prepend.t rename to test/prepend.test.py diff --git a/test/pri_sort.t b/test/pri_sort.test.py similarity index 100% rename from test/pri_sort.t rename to test/pri_sort.test.py diff --git a/test/problems b/test/problems index da3c2606f..7f53a8661 100755 --- a/test/problems +++ b/test/problems @@ -52,7 +52,7 @@ if __name__ == "__main__": unexpected = defaultdict(int) passed = defaultdict(int) - file = re.compile(r"^# (?:./)?(\S+\.t)(?:\.exe)?$") + file = re.compile(r"^# (?:./)?(\S+\.test)(?:\.py|\.cpp)?$") timestamp = re.compile(r"^# (\d+(?:\.\d+)?) ==>.*$") expected_fail = re.compile(r"^not ok.*?#\s*TODO", re.I) diff --git a/test/project.t b/test/project.test.py similarity index 100% rename from test/project.t rename to test/project.test.py diff --git a/test/quotes.t b/test/quotes.test.py similarity index 100% rename from test/quotes.t rename to test/quotes.test.py diff --git a/test/rc.override.t b/test/rc.override.test.py similarity index 100% rename from test/rc.override.t rename to test/rc.override.test.py diff --git a/test/recurrence.t b/test/recurrence.test.py similarity index 100% rename from test/recurrence.t rename to test/recurrence.test.py diff --git a/test/reports.t b/test/reports.test.py similarity index 100% rename from test/reports.t rename to test/reports.test.py diff --git a/test/run_all b/test/run_all index e27e3c571..f20f092d1 100755 --- a/test/run_all +++ b/test/run_all @@ -70,7 +70,7 @@ class TestRunner(object): self._outputq = Queue() def _find_tests(self): - for test in glob.glob("*.t"): + for test in glob.glob("*.test.sh") + glob.glob("*.test.py") + glob.glob("*.test.cpp"): if os.access(test, os.X_OK): # Executables only if self._is_parallelizable(test): diff --git a/test/search.t b/test/search.test.py similarity index 100% rename from test/search.t rename to test/search.test.py diff --git a/test/sequence.t b/test/sequence.test.py similarity index 100% rename from test/sequence.t rename to test/sequence.test.py diff --git a/test/shell.t b/test/shell.test.py similarity index 100% rename from test/shell.t rename to test/shell.test.py diff --git a/test/show.t b/test/show.test.py similarity index 100% rename from test/show.t rename to test/show.test.py diff --git a/test/sorting.t b/test/sorting.test.py similarity index 100% rename from test/sorting.t rename to test/sorting.test.py diff --git a/test/special.t b/test/special.test.py similarity index 100% rename from test/special.t rename to test/special.test.py diff --git a/test/start.t b/test/start.test.py similarity index 100% rename from test/start.t rename to test/start.test.py diff --git a/test/stats.t b/test/stats.test.py similarity index 100% rename from test/stats.t rename to test/stats.test.py diff --git a/test/stress_test b/test/stress_test index 73975127f..3073324fa 100755 --- a/test/stress_test +++ b/test/stress_test @@ -13,7 +13,7 @@ import sys def find_tests(): tests = [] - for test in glob.glob("*.t") + glob.glob("*.t.exe"): + for test in glob.glob("*.test.sh") + glob.glob("*.test.py"): if os.access(test, os.X_OK): # Executables only tests.append(test) diff --git a/test/substitute.t b/test/substitute.test.py similarity index 100% rename from test/substitute.t rename to test/substitute.test.py diff --git a/test/sugar.t b/test/sugar.test.py similarity index 100% rename from test/sugar.t rename to test/sugar.test.py diff --git a/test/summary.t b/test/summary.test.py similarity index 100% rename from test/summary.t rename to test/summary.test.py diff --git a/test/t.t.cpp b/test/t.test.cpp similarity index 100% rename from test/t.t.cpp rename to test/t.test.cpp diff --git a/test/tag.t b/test/tag.test.py similarity index 100% rename from test/tag.t rename to test/tag.test.py diff --git a/test/taskrc.t b/test/taskrc.test.py similarity index 100% rename from test/taskrc.t rename to test/taskrc.test.py diff --git a/test/tc.t.cpp b/test/tc.test.cpp similarity index 100% rename from test/tc.t.cpp rename to test/tc.test.cpp diff --git a/test/tdb2.t.cpp b/test/tdb2.test.cpp similarity index 100% rename from test/tdb2.t.cpp rename to test/tdb2.test.cpp diff --git a/test/template.t b/test/template.test.py similarity index 100% rename from test/template.t rename to test/template.test.py diff --git a/test/timesheet.t b/test/timesheet.test.py similarity index 100% rename from test/timesheet.t rename to test/timesheet.test.py diff --git a/test/tw-1379.t b/test/tw-1379.test.py similarity index 100% rename from test/tw-1379.t rename to test/tw-1379.test.py diff --git a/test/tw-1637.t b/test/tw-1637.test.sh similarity index 100% rename from test/tw-1637.t rename to test/tw-1637.test.sh diff --git a/test/tw-1643.t b/test/tw-1643.test.sh similarity index 100% rename from test/tw-1643.t rename to test/tw-1643.test.sh diff --git a/test/tw-1688.t b/test/tw-1688.test.sh similarity index 100% rename from test/tw-1688.t rename to test/tw-1688.test.sh diff --git a/test/tw-1715.t b/test/tw-1715.test.sh similarity index 100% rename from test/tw-1715.t rename to test/tw-1715.test.sh diff --git a/test/tw-1718.t b/test/tw-1718.test.sh similarity index 100% rename from test/tw-1718.t rename to test/tw-1718.test.sh diff --git a/test/tw-1804.t b/test/tw-1804.test.sh similarity index 100% rename from test/tw-1804.t rename to test/tw-1804.test.sh diff --git a/test/tw-1837.t b/test/tw-1837.test.py similarity index 100% rename from test/tw-1837.t rename to test/tw-1837.test.py diff --git a/test/tw-1883.t b/test/tw-1883.test.sh similarity index 100% rename from test/tw-1883.t rename to test/tw-1883.test.sh diff --git a/test/tw-1895.t b/test/tw-1895.test.sh similarity index 100% rename from test/tw-1895.t rename to test/tw-1895.test.sh diff --git a/test/tw-1938.t b/test/tw-1938.test.sh similarity index 100% rename from test/tw-1938.t rename to test/tw-1938.test.sh diff --git a/test/tw-20.t b/test/tw-20.test.py similarity index 100% rename from test/tw-20.t rename to test/tw-20.test.py diff --git a/test/tw-2124.t b/test/tw-2124.test.sh similarity index 100% rename from test/tw-2124.t rename to test/tw-2124.test.sh diff --git a/test/tw-2189.t b/test/tw-2189.test.sh similarity index 100% rename from test/tw-2189.t rename to test/tw-2189.test.sh diff --git a/test/tw-2257.t b/test/tw-2257.test.sh similarity index 100% rename from test/tw-2257.t rename to test/tw-2257.test.sh diff --git a/test/tw-2386.t b/test/tw-2386.test.sh similarity index 100% rename from test/tw-2386.t rename to test/tw-2386.test.sh diff --git a/test/tw-2392.t b/test/tw-2392.test.sh similarity index 100% rename from test/tw-2392.t rename to test/tw-2392.test.sh diff --git a/test/tw-2429.t b/test/tw-2429.test.sh similarity index 100% rename from test/tw-2429.t rename to test/tw-2429.test.sh diff --git a/test/tw-2451.t b/test/tw-2451.test.sh similarity index 100% rename from test/tw-2451.t rename to test/tw-2451.test.sh diff --git a/test/tw-2514.t b/test/tw-2514.test.sh similarity index 100% rename from test/tw-2514.t rename to test/tw-2514.test.sh diff --git a/test/tw-2530.t b/test/tw-2530.test.sh similarity index 100% rename from test/tw-2530.t rename to test/tw-2530.test.sh diff --git a/test/tw-2550.t b/test/tw-2550.test.sh similarity index 100% rename from test/tw-2550.t rename to test/tw-2550.test.sh diff --git a/test/tw-2575.t b/test/tw-2575.test.py similarity index 100% rename from test/tw-2575.t rename to test/tw-2575.test.py diff --git a/test/tw-2581.t b/test/tw-2581.test.sh similarity index 100% rename from test/tw-2581.t rename to test/tw-2581.test.sh diff --git a/test/tw-262.t b/test/tw-262.test.py similarity index 100% rename from test/tw-262.t rename to test/tw-262.test.py diff --git a/test/tw-2689.t.cpp b/test/tw-2689.test.cpp similarity index 100% rename from test/tw-2689.t.cpp rename to test/tw-2689.test.cpp diff --git a/test/tw-295.t b/test/tw-295.test.py similarity index 100% rename from test/tw-295.t rename to test/tw-295.test.py diff --git a/test/tw-3102.t b/test/tw-3102.test.sh similarity index 100% rename from test/tw-3102.t rename to test/tw-3102.test.sh diff --git a/test/tw-3109.t b/test/tw-3109.test.sh similarity index 100% rename from test/tw-3109.t rename to test/tw-3109.test.sh diff --git a/test/uda.t b/test/uda.test.py similarity index 100% rename from test/uda.t rename to test/uda.test.py diff --git a/test/uda_orphan.t b/test/uda_orphan.test.py similarity index 100% rename from test/uda_orphan.t rename to test/uda_orphan.test.py diff --git a/test/uda_report.t b/test/uda_report.test.py similarity index 100% rename from test/uda_report.t rename to test/uda_report.test.py diff --git a/test/uda_sort.t b/test/uda_sort.test.py similarity index 100% rename from test/uda_sort.t rename to test/uda_sort.test.py diff --git a/test/undo.t b/test/undo.test.py similarity index 100% rename from test/undo.t rename to test/undo.test.py diff --git a/test/unicode.t b/test/unicode.test.py similarity index 100% rename from test/unicode.t rename to test/unicode.test.py diff --git a/test/unique.t b/test/unique.test.py similarity index 100% rename from test/unique.t rename to test/unique.test.py diff --git a/test/upgrade.t b/test/upgrade.test.py similarity index 100% rename from test/upgrade.t rename to test/upgrade.test.py diff --git a/test/urgency.t b/test/urgency.test.py similarity index 100% rename from test/urgency.t rename to test/urgency.test.py diff --git a/test/urgency_inherit.t b/test/urgency_inherit.test.py similarity index 100% rename from test/urgency_inherit.t rename to test/urgency_inherit.test.py diff --git a/test/util.t.cpp b/test/util.test.cpp similarity index 100% rename from test/util.t.cpp rename to test/util.test.cpp diff --git a/test/uuid.t b/test/uuid.test.py similarity index 100% rename from test/uuid.t rename to test/uuid.test.py diff --git a/test/variant_add.t.cpp b/test/variant_add.test.cpp similarity index 100% rename from test/variant_add.t.cpp rename to test/variant_add.test.cpp diff --git a/test/variant_and.t.cpp b/test/variant_and.test.cpp similarity index 100% rename from test/variant_and.t.cpp rename to test/variant_and.test.cpp diff --git a/test/variant_cast.t.cpp b/test/variant_cast.test.cpp similarity index 100% rename from test/variant_cast.t.cpp rename to test/variant_cast.test.cpp diff --git a/test/variant_divide.t.cpp b/test/variant_divide.test.cpp similarity index 100% rename from test/variant_divide.t.cpp rename to test/variant_divide.test.cpp diff --git a/test/variant_equal.t.cpp b/test/variant_equal.test.cpp similarity index 100% rename from test/variant_equal.t.cpp rename to test/variant_equal.test.cpp diff --git a/test/variant_exp.t.cpp b/test/variant_exp.test.cpp similarity index 100% rename from test/variant_exp.t.cpp rename to test/variant_exp.test.cpp diff --git a/test/variant_gt.t.cpp b/test/variant_gt.test.cpp similarity index 100% rename from test/variant_gt.t.cpp rename to test/variant_gt.test.cpp diff --git a/test/variant_gte.t.cpp b/test/variant_gte.test.cpp similarity index 100% rename from test/variant_gte.t.cpp rename to test/variant_gte.test.cpp diff --git a/test/variant_inequal.t.cpp b/test/variant_inequal.test.cpp similarity index 100% rename from test/variant_inequal.t.cpp rename to test/variant_inequal.test.cpp diff --git a/test/variant_lt.t.cpp b/test/variant_lt.test.cpp similarity index 100% rename from test/variant_lt.t.cpp rename to test/variant_lt.test.cpp diff --git a/test/variant_lte.t.cpp b/test/variant_lte.test.cpp similarity index 100% rename from test/variant_lte.t.cpp rename to test/variant_lte.test.cpp diff --git a/test/variant_match.t.cpp b/test/variant_match.test.cpp similarity index 100% rename from test/variant_match.t.cpp rename to test/variant_match.test.cpp diff --git a/test/variant_math.t.cpp b/test/variant_math.test.cpp similarity index 100% rename from test/variant_math.t.cpp rename to test/variant_math.test.cpp diff --git a/test/variant_modulo.t.cpp b/test/variant_modulo.test.cpp similarity index 100% rename from test/variant_modulo.t.cpp rename to test/variant_modulo.test.cpp diff --git a/test/variant_multiply.t.cpp b/test/variant_multiply.test.cpp similarity index 100% rename from test/variant_multiply.t.cpp rename to test/variant_multiply.test.cpp diff --git a/test/variant_nomatch.t.cpp b/test/variant_nomatch.test.cpp similarity index 100% rename from test/variant_nomatch.t.cpp rename to test/variant_nomatch.test.cpp diff --git a/test/variant_not.t.cpp b/test/variant_not.test.cpp similarity index 100% rename from test/variant_not.t.cpp rename to test/variant_not.test.cpp diff --git a/test/variant_or.t.cpp b/test/variant_or.test.cpp similarity index 100% rename from test/variant_or.t.cpp rename to test/variant_or.test.cpp diff --git a/test/variant_partial.t.cpp b/test/variant_partial.test.cpp similarity index 100% rename from test/variant_partial.t.cpp rename to test/variant_partial.test.cpp diff --git a/test/variant_subtract.t.cpp b/test/variant_subtract.test.cpp similarity index 100% rename from test/variant_subtract.t.cpp rename to test/variant_subtract.test.cpp diff --git a/test/variant_xor.t.cpp b/test/variant_xor.test.cpp similarity index 100% rename from test/variant_xor.t.cpp rename to test/variant_xor.test.cpp diff --git a/test/verbose.t b/test/verbose.test.py similarity index 100% rename from test/verbose.t rename to test/verbose.test.py diff --git a/test/version.t b/test/version.test.py similarity index 100% rename from test/version.t rename to test/version.test.py diff --git a/test/view.t.cpp b/test/view.test.cpp similarity index 100% rename from test/view.t.cpp rename to test/view.test.cpp diff --git a/test/wait.t b/test/wait.test.py similarity index 100% rename from test/wait.t rename to test/wait.test.py From 94b3e301d16e99f26b1bb8786cef13809b0d1139 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 1 May 2024 22:45:11 -0400 Subject: [PATCH 009/242] Remove taskchampion source from this repo (#3427) * move taskchampion-lib to src/tc/lib, remove the rest * update references to taskchampion * Use a top-level Cargo.toml so everything is consistent * apply comments from ryneeverett --- .github/CODEOWNERS | 3 - .github/workflows/checks.yml | 16 - .github/workflows/publish-docs.yml | 31 - .github/workflows/rust-tests.yml | 83 - Cargo.lock | 319 +-- Cargo.toml | 10 +- doc/devel/README.md | 8 +- doc/devel/contrib/development.md | 7 - doc/devel/contrib/releasing.md | 1 - doc/devel/contrib/rust-and-c++.md | 29 +- src/CMakeLists.txt | 2 +- src/columns/CMakeLists.txt | 2 +- src/commands/CMakeLists.txt | 2 +- src/tc/CMakeLists.txt | 5 +- {taskchampion => src/tc}/lib/Cargo.toml | 4 +- {taskchampion => src/tc}/lib/Makefile | 0 .../tc}/lib/src/annotation.rs | 0 {taskchampion => src/tc}/lib/src/atomic.rs | 0 {taskchampion => src/tc}/lib/src/kv.rs | 0 {taskchampion => src/tc}/lib/src/lib.rs | 0 {taskchampion => src/tc}/lib/src/replica.rs | 0 {taskchampion => src/tc}/lib/src/result.rs | 0 {taskchampion => src/tc}/lib/src/server.rs | 0 {taskchampion => src/tc}/lib/src/status.rs | 0 {taskchampion => src/tc}/lib/src/string.rs | 0 {taskchampion => src/tc}/lib/src/task.rs | 0 {taskchampion => src/tc}/lib/src/traits.rs | 0 {taskchampion => src/tc}/lib/src/uda.rs | 0 {taskchampion => src/tc}/lib/src/util.rs | 0 {taskchampion => src/tc}/lib/src/uuid.rs | 0 .../tc}/lib/src/workingset.rs | 0 {taskchampion => src/tc}/lib/taskchampion.h | 0 taskchampion/.changelogs/.gitignore | 0 .../.changelogs/2021-10-03-server-storage.md | 2 - .../.changelogs/2021-10-11-issue23-client.md | 2 - .../.changelogs/2021-10-16-issue299.md | 1 - .../2021-10-25-issue23-integration.md | 1 - taskchampion/.gitignore | 1 - taskchampion/CHANGELOG.md | 26 - taskchampion/CODE_OF_CONDUCT.md | 76 - taskchampion/CONTRIBUTING.md | 62 - taskchampion/LICENSE | 21 - taskchampion/POLICY.md | 45 - taskchampion/README.md | 50 - taskchampion/RELEASING.md | 16 - taskchampion/SECURITY.md | 11 - taskchampion/docs/.gitignore | 2 - taskchampion/docs/README.md | 3 - taskchampion/docs/assets/cgi/LICENSE.md | 2 - .../cgi/icon_rounded/icon_rounded_1024.png | Bin 566832 -> 0 bytes .../cgi/icon_rounded/icon_rounded_128.png | Bin 16993 -> 0 bytes .../cgi/icon_rounded/icon_rounded_16.png | Bin 1183 -> 0 bytes .../cgi/icon_rounded/icon_rounded_256.png | Bin 52056 -> 0 bytes .../cgi/icon_rounded/icon_rounded_32.png | Bin 2337 -> 0 bytes .../cgi/icon_rounded/icon_rounded_512.png | Bin 169678 -> 0 bytes .../cgi/icon_rounded/icon_rounded_64.png | Bin 5819 -> 0 bytes .../cgi/icon_square/icon_square_1024.png | Bin 536011 -> 0 bytes .../cgi/icon_square/icon_square_128.png | Bin 14641 -> 0 bytes .../assets/cgi/icon_square/icon_square_16.png | Bin 1066 -> 0 bytes .../cgi/icon_square/icon_square_256.png | Bin 46171 -> 0 bytes .../assets/cgi/icon_square/icon_square_32.png | Bin 2011 -> 0 bytes .../cgi/icon_square/icon_square_512.png | Bin 155209 -> 0 bytes .../assets/cgi/icon_square/icon_square_64.png | Bin 5036 -> 0 bytes .../docs/assets/cgi/logo/logo_1024.png | Bin 826502 -> 0 bytes .../docs/assets/cgi/logo/logo_128.png | Bin 21775 -> 0 bytes taskchampion/docs/assets/cgi/logo/logo_16.png | Bin 1373 -> 0 bytes .../docs/assets/cgi/logo/logo_256.png | Bin 68818 -> 0 bytes taskchampion/docs/assets/cgi/logo/logo_32.png | Bin 2820 -> 0 bytes .../docs/assets/cgi/logo/logo_512.png | Bin 234149 -> 0 bytes taskchampion/docs/assets/cgi/logo/logo_64.png | Bin 7359 -> 0 bytes taskchampion/docs/book.toml | 9 - taskchampion/docs/src/SUMMARY.md | 17 - taskchampion/docs/src/data-model.md | 5 - taskchampion/docs/src/encryption.md | 38 - taskchampion/docs/src/http.md | 65 - .../docs/src/images/name_timestamp.png | Bin 8194 -> 0 bytes taskchampion/docs/src/installation.md | 3 - taskchampion/docs/src/internals.md | 5 - taskchampion/docs/src/object-store.md | 9 - taskchampion/docs/src/plans.md | 35 - taskchampion/docs/src/running-sync-server.md | 11 - taskchampion/docs/src/snapshots.md | 39 - taskchampion/docs/src/storage.md | 83 - taskchampion/docs/src/sync-model.md | 141 -- taskchampion/docs/src/sync-protocol.md | 115 - taskchampion/docs/src/sync.md | 7 - taskchampion/docs/src/taskdb.md | 32 - taskchampion/docs/src/tasks.md | 58 - taskchampion/integration-tests/.gitignore | 2 - taskchampion/integration-tests/Cargo.toml | 20 - taskchampion/integration-tests/README.md | 30 - taskchampion/integration-tests/build.rs | 51 - .../src/bindings_tests/mod.rs | 30 - .../src/bindings_tests/replica.c | 330 --- .../src/bindings_tests/string.c | 125 - .../src/bindings_tests/task.c | 717 ------ .../src/bindings_tests/test.c | 30 - .../src/bindings_tests/unity/LICENSE.txt | 21 - .../src/bindings_tests/unity/README.md | 3 - .../src/bindings_tests/unity/unity.c | 2119 ----------------- .../src/bindings_tests/unity/unity.h | 661 ----- .../bindings_tests/unity/unity_internals.h | 1053 -------- .../src/bindings_tests/uuid.c | 72 - taskchampion/integration-tests/src/lib.rs | 2 - .../integration-tests/tests/bindings.rs | 31 - .../integration-tests/tests/cross-sync.rs | 66 - .../tests/update-and-delete-sync.rs | 72 - taskchampion/scripts/changelog.py | 64 - taskchampion/taskchampion/Cargo.toml | 58 - taskchampion/taskchampion/src/depmap.rs | 81 - taskchampion/taskchampion/src/errors.rs | 48 - taskchampion/taskchampion/src/lib.rs | 82 - taskchampion/taskchampion/src/macros.rs | 17 - taskchampion/taskchampion/src/replica.rs | 677 ------ .../taskchampion/src/server/cloud/gcp.rs | 407 ---- .../taskchampion/src/server/cloud/mod.rs | 16 - .../taskchampion/src/server/cloud/server.rs | 1183 --------- .../taskchampion/src/server/cloud/service.rs | 38 - .../taskchampion/src/server/config.rs | 74 - .../taskchampion/src/server/encryption.rs | 414 ---- .../src/server/generate-test-data.py | 77 - .../taskchampion/src/server/local/mod.rs | 258 -- taskchampion/taskchampion/src/server/mod.rs | 31 - taskchampion/taskchampion/src/server/op.rs | 421 ---- .../taskchampion/src/server/sync/mod.rs | 192 -- .../src/server/test-bad-app-id.data | 2 - .../src/server/test-bad-client-id.data | 1 - .../src/server/test-bad-secret.data | 1 - .../src/server/test-bad-version-id.data | 1 - .../src/server/test-bad-version.data | 1 - .../src/server/test-bad-version_id.data | 2 - .../taskchampion/src/server/test-good.data | 1 - taskchampion/taskchampion/src/server/test.rs | 131 - taskchampion/taskchampion/src/server/types.rs | 73 - .../taskchampion/src/storage/config.rs | 29 - .../taskchampion/src/storage/inmemory.rs | 246 -- taskchampion/taskchampion/src/storage/mod.rs | 133 -- taskchampion/taskchampion/src/storage/op.rs | 289 --- .../taskchampion/src/storage/sqlite.rs | 819 ------- .../taskchampion/src/task/annotation.rs | 10 - taskchampion/taskchampion/src/task/mod.rs | 12 - taskchampion/taskchampion/src/task/status.rs | 74 - taskchampion/taskchampion/src/task/tag.rs | 174 -- taskchampion/taskchampion/src/task/task.rs | 1270 ---------- taskchampion/taskchampion/src/task/time.rs | 11 - taskchampion/taskchampion/src/taskdb/apply.rs | 406 ---- taskchampion/taskchampion/src/taskdb/mod.rs | 297 --- .../taskchampion/src/taskdb/snapshot.rs | 181 -- taskchampion/taskchampion/src/taskdb/sync.rs | 386 --- taskchampion/taskchampion/src/taskdb/undo.rs | 154 -- .../taskchampion/src/taskdb/working_set.rs | 152 -- taskchampion/taskchampion/src/utils.rs | 61 - taskchampion/taskchampion/src/workingset.rs | 154 -- taskchampion/xtask/src/main.rs | 124 - test/CMakeLists.txt | 5 +- {taskchampion/xtask => xtask}/Cargo.toml | 4 +- xtask/src/main.rs | 35 + 157 files changed, 62 insertions(+), 16265 deletions(-) delete mode 100644 .github/CODEOWNERS delete mode 100644 .github/workflows/publish-docs.yml delete mode 100644 .github/workflows/rust-tests.yml rename {taskchampion => src/tc}/lib/Cargo.toml (84%) rename {taskchampion => src/tc}/lib/Makefile (100%) rename {taskchampion => src/tc}/lib/src/annotation.rs (100%) rename {taskchampion => src/tc}/lib/src/atomic.rs (100%) rename {taskchampion => src/tc}/lib/src/kv.rs (100%) rename {taskchampion => src/tc}/lib/src/lib.rs (100%) rename {taskchampion => src/tc}/lib/src/replica.rs (100%) rename {taskchampion => src/tc}/lib/src/result.rs (100%) rename {taskchampion => src/tc}/lib/src/server.rs (100%) rename {taskchampion => src/tc}/lib/src/status.rs (100%) rename {taskchampion => src/tc}/lib/src/string.rs (100%) rename {taskchampion => src/tc}/lib/src/task.rs (100%) rename {taskchampion => src/tc}/lib/src/traits.rs (100%) rename {taskchampion => src/tc}/lib/src/uda.rs (100%) rename {taskchampion => src/tc}/lib/src/util.rs (100%) rename {taskchampion => src/tc}/lib/src/uuid.rs (100%) rename {taskchampion => src/tc}/lib/src/workingset.rs (100%) rename {taskchampion => src/tc}/lib/taskchampion.h (100%) delete mode 100644 taskchampion/.changelogs/.gitignore delete mode 100644 taskchampion/.changelogs/2021-10-03-server-storage.md delete mode 100644 taskchampion/.changelogs/2021-10-11-issue23-client.md delete mode 100644 taskchampion/.changelogs/2021-10-16-issue299.md delete mode 100644 taskchampion/.changelogs/2021-10-25-issue23-integration.md delete mode 100644 taskchampion/.gitignore delete mode 100644 taskchampion/CHANGELOG.md delete mode 100644 taskchampion/CODE_OF_CONDUCT.md delete mode 100644 taskchampion/CONTRIBUTING.md delete mode 100644 taskchampion/LICENSE delete mode 100644 taskchampion/POLICY.md delete mode 100644 taskchampion/README.md delete mode 100644 taskchampion/RELEASING.md delete mode 100644 taskchampion/SECURITY.md delete mode 100644 taskchampion/docs/.gitignore delete mode 100644 taskchampion/docs/README.md delete mode 100644 taskchampion/docs/assets/cgi/LICENSE.md delete mode 100755 taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_1024.png delete mode 100755 taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_128.png delete mode 100755 taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_16.png delete mode 100755 taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_256.png delete mode 100755 taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_32.png delete mode 100755 taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_512.png delete mode 100755 taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_64.png delete mode 100755 taskchampion/docs/assets/cgi/icon_square/icon_square_1024.png delete mode 100755 taskchampion/docs/assets/cgi/icon_square/icon_square_128.png delete mode 100755 taskchampion/docs/assets/cgi/icon_square/icon_square_16.png delete mode 100755 taskchampion/docs/assets/cgi/icon_square/icon_square_256.png delete mode 100755 taskchampion/docs/assets/cgi/icon_square/icon_square_32.png delete mode 100755 taskchampion/docs/assets/cgi/icon_square/icon_square_512.png delete mode 100755 taskchampion/docs/assets/cgi/icon_square/icon_square_64.png delete mode 100755 taskchampion/docs/assets/cgi/logo/logo_1024.png delete mode 100755 taskchampion/docs/assets/cgi/logo/logo_128.png delete mode 100755 taskchampion/docs/assets/cgi/logo/logo_16.png delete mode 100755 taskchampion/docs/assets/cgi/logo/logo_256.png delete mode 100755 taskchampion/docs/assets/cgi/logo/logo_32.png delete mode 100755 taskchampion/docs/assets/cgi/logo/logo_512.png delete mode 100755 taskchampion/docs/assets/cgi/logo/logo_64.png delete mode 100644 taskchampion/docs/book.toml delete mode 100644 taskchampion/docs/src/SUMMARY.md delete mode 100644 taskchampion/docs/src/data-model.md delete mode 100644 taskchampion/docs/src/encryption.md delete mode 100644 taskchampion/docs/src/http.md delete mode 100644 taskchampion/docs/src/images/name_timestamp.png delete mode 100644 taskchampion/docs/src/installation.md delete mode 100644 taskchampion/docs/src/internals.md delete mode 100644 taskchampion/docs/src/object-store.md delete mode 100644 taskchampion/docs/src/plans.md delete mode 100644 taskchampion/docs/src/running-sync-server.md delete mode 100644 taskchampion/docs/src/snapshots.md delete mode 100644 taskchampion/docs/src/storage.md delete mode 100644 taskchampion/docs/src/sync-model.md delete mode 100644 taskchampion/docs/src/sync-protocol.md delete mode 100644 taskchampion/docs/src/sync.md delete mode 100644 taskchampion/docs/src/taskdb.md delete mode 100644 taskchampion/docs/src/tasks.md delete mode 100644 taskchampion/integration-tests/.gitignore delete mode 100644 taskchampion/integration-tests/Cargo.toml delete mode 100644 taskchampion/integration-tests/README.md delete mode 100644 taskchampion/integration-tests/build.rs delete mode 100644 taskchampion/integration-tests/src/bindings_tests/mod.rs delete mode 100644 taskchampion/integration-tests/src/bindings_tests/replica.c delete mode 100644 taskchampion/integration-tests/src/bindings_tests/string.c delete mode 100644 taskchampion/integration-tests/src/bindings_tests/task.c delete mode 100644 taskchampion/integration-tests/src/bindings_tests/test.c delete mode 100644 taskchampion/integration-tests/src/bindings_tests/unity/LICENSE.txt delete mode 100644 taskchampion/integration-tests/src/bindings_tests/unity/README.md delete mode 100644 taskchampion/integration-tests/src/bindings_tests/unity/unity.c delete mode 100644 taskchampion/integration-tests/src/bindings_tests/unity/unity.h delete mode 100644 taskchampion/integration-tests/src/bindings_tests/unity/unity_internals.h delete mode 100644 taskchampion/integration-tests/src/bindings_tests/uuid.c delete mode 100644 taskchampion/integration-tests/src/lib.rs delete mode 100644 taskchampion/integration-tests/tests/bindings.rs delete mode 100644 taskchampion/integration-tests/tests/cross-sync.rs delete mode 100644 taskchampion/integration-tests/tests/update-and-delete-sync.rs delete mode 100755 taskchampion/scripts/changelog.py delete mode 100644 taskchampion/taskchampion/Cargo.toml delete mode 100644 taskchampion/taskchampion/src/depmap.rs delete mode 100644 taskchampion/taskchampion/src/errors.rs delete mode 100644 taskchampion/taskchampion/src/lib.rs delete mode 100644 taskchampion/taskchampion/src/macros.rs delete mode 100644 taskchampion/taskchampion/src/replica.rs delete mode 100644 taskchampion/taskchampion/src/server/cloud/gcp.rs delete mode 100644 taskchampion/taskchampion/src/server/cloud/mod.rs delete mode 100644 taskchampion/taskchampion/src/server/cloud/server.rs delete mode 100644 taskchampion/taskchampion/src/server/cloud/service.rs delete mode 100644 taskchampion/taskchampion/src/server/config.rs delete mode 100644 taskchampion/taskchampion/src/server/encryption.rs delete mode 100644 taskchampion/taskchampion/src/server/generate-test-data.py delete mode 100644 taskchampion/taskchampion/src/server/local/mod.rs delete mode 100644 taskchampion/taskchampion/src/server/mod.rs delete mode 100644 taskchampion/taskchampion/src/server/op.rs delete mode 100644 taskchampion/taskchampion/src/server/sync/mod.rs delete mode 100644 taskchampion/taskchampion/src/server/test-bad-app-id.data delete mode 100644 taskchampion/taskchampion/src/server/test-bad-client-id.data delete mode 100644 taskchampion/taskchampion/src/server/test-bad-secret.data delete mode 100644 taskchampion/taskchampion/src/server/test-bad-version-id.data delete mode 100644 taskchampion/taskchampion/src/server/test-bad-version.data delete mode 100644 taskchampion/taskchampion/src/server/test-bad-version_id.data delete mode 100644 taskchampion/taskchampion/src/server/test-good.data delete mode 100644 taskchampion/taskchampion/src/server/test.rs delete mode 100644 taskchampion/taskchampion/src/server/types.rs delete mode 100644 taskchampion/taskchampion/src/storage/config.rs delete mode 100644 taskchampion/taskchampion/src/storage/inmemory.rs delete mode 100644 taskchampion/taskchampion/src/storage/mod.rs delete mode 100644 taskchampion/taskchampion/src/storage/op.rs delete mode 100644 taskchampion/taskchampion/src/storage/sqlite.rs delete mode 100644 taskchampion/taskchampion/src/task/annotation.rs delete mode 100644 taskchampion/taskchampion/src/task/mod.rs delete mode 100644 taskchampion/taskchampion/src/task/status.rs delete mode 100644 taskchampion/taskchampion/src/task/tag.rs delete mode 100644 taskchampion/taskchampion/src/task/task.rs delete mode 100644 taskchampion/taskchampion/src/task/time.rs delete mode 100644 taskchampion/taskchampion/src/taskdb/apply.rs delete mode 100644 taskchampion/taskchampion/src/taskdb/mod.rs delete mode 100644 taskchampion/taskchampion/src/taskdb/snapshot.rs delete mode 100644 taskchampion/taskchampion/src/taskdb/sync.rs delete mode 100644 taskchampion/taskchampion/src/taskdb/undo.rs delete mode 100644 taskchampion/taskchampion/src/taskdb/working_set.rs delete mode 100644 taskchampion/taskchampion/src/utils.rs delete mode 100644 taskchampion/taskchampion/src/workingset.rs delete mode 100644 taskchampion/xtask/src/main.rs rename {taskchampion/xtask => xtask}/Cargo.toml (60%) create mode 100644 xtask/src/main.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index a945a565d..000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,3 +0,0 @@ -taskchampion/* @dbr @djmitche -Cargo.toml @dbr @djmitche -Cargo.lock @dbr @djmitche diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 96553404a..c95ba45fa 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -44,22 +44,6 @@ jobs: args: --all-features name: "Clippy Results" - mdbook: - runs-on: ubuntu-latest - name: "Documentation" - - steps: - - uses: actions/checkout@v4 - - - name: Setup mdBook - uses: peaceiris/actions-mdbook@v2 - with: - # if this changes, change it in .github/workflows/publish-docs.yml as well - mdbook-version: '0.4.10' - - - run: mdbook test taskchampion/docs - - run: mdbook build taskchampion/docs - fmt: runs-on: ubuntu-latest name: "Formatting" diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml deleted file mode 100644 index dfa4c54e7..000000000 --- a/.github/workflows/publish-docs.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: docs - -on: - push: - branches: - - develop - -permissions: - contents: write - -jobs: - mdbook-deploy: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Setup mdBook - uses: peaceiris/actions-mdbook@v2 - with: - # if this changes, change it in .github/workflows/checks.yml as well - mdbook-version: '0.4.10' - - - run: mdbook build taskchampion/docs - - - name: Deploy - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./taskchampion/docs/book - destination_dir: taskchampion diff --git a/.github/workflows/rust-tests.yml b/.github/workflows/rust-tests.yml deleted file mode 100644 index cf11d390c..000000000 --- a/.github/workflows/rust-tests.yml +++ /dev/null @@ -1,83 +0,0 @@ - -name: tests - rust - -on: - push: - branches: - - develop - pull_request: - types: [opened, reopened, synchronize] - -jobs: - ## Run the `taskchampion` crate's tests with various combinations of features. - features: - strategy: - matrix: - features: - - "" - - "server-sync" - - name: "taskchampion ${{ matrix.features == '' && 'with no features' || format('with features {0}', matrix.features) }}" - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: ~/.cargo/registry - key: ubuntu-latest-stable-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo build - uses: actions/cache@v4 - with: - path: target - key: ubuntu-latest-stable-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - - - name: test - run: cargo test -p taskchampion --no-default-features --features "${{ matrix.features }}" - - ## Run all TaskChampion crate tests, using both the minimum supported rust version - ## and the latest stable Rust. - test: - strategy: - matrix: - rust: - - "1.70.0" # MSRV - - "stable" - os: - - ubuntu-latest - - macOS-latest - - windows-latest - - name: "rust ${{ matrix.rust }} on ${{ matrix.os }}" - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v4 - - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-${{ matrix.rust }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo build - uses: actions/cache@v4 - with: - path: target - key: ${{ runner.os }}-${{ matrix.rust }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - uses: actions-rs/toolchain@v1 - with: - toolchain: "${{ matrix.rust }}" - override: true - - - name: test - run: cargo test diff --git a/Cargo.lock b/Cargo.lock index 8d742b235..11c8e2d7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,21 +124,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bit-set" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bitflags" version = "1.3.2" @@ -288,27 +273,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "fallible-iterator" version = "0.2.0" @@ -321,15 +285,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "fastrand" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" -dependencies = [ - "instant", -] - [[package]] name = "ffizz-header" version = "0.5.0" @@ -378,21 +333,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futures" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" version = "0.3.25" @@ -400,7 +340,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -409,17 +348,6 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" -[[package]] -name = "futures-executor" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - [[package]] name = "futures-io" version = "0.3.25" @@ -449,19 +377,12 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" - [[package]] name = "futures-util" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ - "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -631,12 +552,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - [[package]] name = "hex" version = "0.4.3" @@ -758,39 +673,6 @@ dependencies = [ "hashbrown 0.11.2", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "integration-tests" -version = "0.4.1" -dependencies = [ - "anyhow", - "cc", - "lazy_static", - "pretty_assertions", - "taskchampion", - "taskchampion-lib", - "tempfile", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.9.0" @@ -847,12 +729,6 @@ version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - [[package]] name = "libsqlite3-sys" version = "0.26.0" @@ -884,12 +760,6 @@ dependencies = [ "syn 1.0.104", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "lock_api" version = "0.4.7" @@ -988,7 +858,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -997,7 +866,7 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", ] @@ -1034,7 +903,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.13", + "redox_syscall", "smallvec", "windows-sys 0.45.0", ] @@ -1091,12 +960,6 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "pretty_assertions" version = "1.4.0" @@ -1116,32 +979,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proptest" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.0.2", - "lazy_static", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.28" @@ -1151,45 +988,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - [[package]] name = "redox_syscall" version = "0.2.13" @@ -1199,15 +997,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "regex" version = "1.10.2" @@ -1308,32 +1097,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "rstest" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de1bb486a691878cd320c2f0d319ba91eeaa2e894066d8b5f8f117c000e9d962" -dependencies = [ - "futures", - "futures-timer", - "rstest_macros", - "rustc_version", -] - -[[package]] -name = "rstest_macros" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290ca1a1c8ca7edb7c3283bd44dc35dd54fdec6253a3912e201ba1072018fca8" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.104", - "unicode-ident", -] - [[package]] name = "rusqlite" version = "0.29.0" @@ -1354,29 +1117,6 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.37.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - [[package]] name = "rustls" version = "0.21.11" @@ -1414,18 +1154,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - [[package]] name = "ryu" version = "1.0.10" @@ -1448,12 +1176,6 @@ dependencies = [ "untrusted 0.7.1", ] -[[package]] -name = "semver" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" - [[package]] name = "serde" version = "1.0.147" @@ -1617,7 +1339,9 @@ dependencies = [ [[package]] name = "taskchampion" -version = "0.4.1" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9e2d64086cc515f801ba0e1f366e5e029dd47db3c499a9e173a60b62145410" dependencies = [ "anyhow", "byteorder", @@ -1625,16 +1349,12 @@ dependencies = [ "flate2", "google-cloud-storage", "log", - "pretty_assertions", - "proptest", "ring 0.17.3", - "rstest", "rusqlite", "serde", "serde_json", "strum", "strum_macros", - "tempfile", "thiserror", "tokio", "ureq", @@ -1653,20 +1373,6 @@ dependencies = [ "taskchampion", ] -[[package]] -name = "tempfile" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" -dependencies = [ - "autocfg", - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.48.0", -] - [[package]] name = "thiserror" version = "1.0.37" @@ -1842,12 +1548,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - [[package]] name = "unicase" version = "2.7.0" @@ -1945,15 +1645,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - [[package]] name = "want" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index efe0c179d..3d6494992 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,12 @@ [workspace] members = [ - "taskchampion/taskchampion", - "taskchampion/lib", - "taskchampion/integration-tests", - "taskchampion/xtask", + "src/tc/lib", + "xtask", ] resolver = "2" -# src/tc/rust is just part of the TW build and not a public crate -exclude = [ "src/tc/rust" ] - # All Rust dependencies are defined here, and then referenced by the # Cargo.toml's in the members with `foo.workspace = true`. [workspace.dependencies] @@ -40,3 +35,4 @@ thiserror = "1.0" ureq = { version = "^2.9.0", features = ["tls"] } uuid = { version = "^1.8.0", features = ["serde", "v4"] } url = { version = "2" } +taskchampion = "0.5" diff --git a/doc/devel/README.md b/doc/devel/README.md index 7a09621b5..4f55544e3 100644 --- a/doc/devel/README.md +++ b/doc/devel/README.md @@ -11,10 +11,6 @@ For all other documenation, see https://taskwarrior.org. As of the 3.0 release, Taskwarrior uses TaskChampion to manage task data. Find documentation of TaskChampion here: - * [TaskChampion README](../../taskchampion) - * [TaskChampion CONTRIBUTING guide](../../taskchampion/CONTRIBUTING.md) - * [TaskChampion Book](../../taskchampion/docs/src/SUMMARY.md) + * [TaskChampion Repository](https://github.com/GothenburgBitFactory/taskchampion/) + * [TaskChampion Book](https://gothenburgbitfactory.github.io/taskchampion/) * [TaskChampion API Documentation](https://docs.rs/taskchampion) - -TaskChampion will [become its own -project](https://github.com/GothenburgBitFactory/taskwarrior/issues/3209) soon. diff --git a/doc/devel/contrib/development.md b/doc/devel/contrib/development.md index ff6ff5d98..4a29f14ea 100644 --- a/doc/devel/contrib/development.md +++ b/doc/devel/contrib/development.md @@ -1,12 +1,5 @@ # Developing Taskwarrior -The following describes the process for developing Taskwarrior. If you are only -changing TaskChampion (Rust code), you can simply treat it like any other Rust -project: modify the source under `taskchampion/` and use `cargo test` to run -the TaskChampion tests. - -See the [TaskChampion CONTRIBUTING guide](../../../taskchampion/CONTRIBUTING.md) for more. - ## Satisfy the Requirements: * CMake 3.0 or later diff --git a/doc/devel/contrib/releasing.md b/doc/devel/contrib/releasing.md index 15fd1292a..da002ca9f 100644 --- a/doc/devel/contrib/releasing.md +++ b/doc/devel/contrib/releasing.md @@ -11,7 +11,6 @@ To release Taskwarrior, follow this process: - On `develop` after that PR merges, create a release tarball: - `git clone . release-tarball` - `cd release-tarball/` - - edit `Cargo.toml` to contain only `taskchampion` and `taskchampion-lib` in `members` (see https://github.com/GothenburgBitFactory/taskwarrior/issues/3294). - `cmake -S. -Bbuild` - `make -Cbuild package_source` - copy build/task-*.tar.gz elsewhere and delete the `release-tarball` dir diff --git a/doc/devel/contrib/rust-and-c++.md b/doc/devel/contrib/rust-and-c++.md index e0244424d..5317985e3 100644 --- a/doc/devel/contrib/rust-and-c++.md +++ b/doc/devel/contrib/rust-and-c++.md @@ -1,42 +1,21 @@ # Rust and C++ -Taskwarrior has historically been a C++ project, but as part of an [ongoing effort to replace its storage backend](https://github.com/GothenburgBitFactory/taskwarrior/issues/2770) it now includes a Rust library called TaskChampion. -To develop Taskwarrior, you will need both a [C++ compiler and tools](./development.md), and a [Rust toolchain](https://www.rust-lang.org/tools/install). -However, most tasks will only require you to be familiar with one of the two languages. +Taskwarrior has historically been a C++ project, but as of taskwarrior-3.0.0, the storage backend is now provided by a Rust library called TaskChampion. ## TaskChampion TaskChampion implements storage and access to "replicas" containing a user's tasks. It defines an abstract model for this data, and also provides a simple Rust API for manipulating replicas. It also defines a method of synchronizing replicas and provides an implementation of that method in the form of a sync server. -TaskChampion provides a C interface via the `taskchampion-lib` crate. +TaskChampion provides a C interface via the `taskchampion-lib` crate, at `src/tc/lib`. Other applications, besides Taskwarrior, can use TaskChampion to manage tasks. -Applications written in Rust can use the `taskchampion` crate, while those in other languages may use the `taskchampion-lib` crate. Taskwarrior is just one application using the TaskChampion interface. -You can build TaskChampion locally by simply running `cargo build` in the root of this repository. -The implementation, including more documentation, is in the [`rust`](../../rust) subdirectory. - ## Taskwarrior's use of TaskChampion Taskwarrior's interface to TaskChampion has a few layers: -* The skeletal Rust crate in [`src/tc/rust`](../../src/tc/rust) brings the symbols from `taskchampion-lib` under CMake's management. - The corresponding header file is included from [`taskchampion/lib`](../../taskchampion/lib). - All of these symbols are placed in the C++ namespace, `tc::ffi`. -* C++ wrappers for the types from `taskchampion-lib` are defined in [`src/tc`](../../src/tc), ensuring memory safety (with `unique_ptr`) and adding methods corresponding to the Rust API's methods. +* A Rust library, `takschampion-lib`, that presents `extern "C"` functions for use from C++, essentially defining a C interface to TaskChampion. +* C++ wrappers for the types from `taskchampion-lib`, defined in [`src/tc`](../../src/tc), ensuring memory safety (with `unique_ptr`) and adding methods corresponding to the Rust API's methods. The wrapper types are in the C++ namespace, `tc`. - -## WARNING About Dependency Tracking - -CMake cannot detect changes to Rust files in under the `taskchampion/` directory. -Running `make` after these files are changed will not incorporate the changes into the resulting executables. -To force re-compilation of the Rust dependencies: - -``` -rm -rf src/tc/rust/x86_64-unknown-linux-gnu/debug/libtc_rust.a -make -``` - -You may need to adjust the `x86_64-unknown-linux-gnu` part of that command depending on what system you are using for development. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9b095c45e..1c269770b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,10 +2,10 @@ cmake_minimum_required (VERSION 3.22) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/tc + ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src - ${CMAKE_SOURCE_DIR}/taskchampion/lib ${TASK_INCLUDE_DIRS}) add_library (task STATIC CLI2.cpp CLI2.h diff --git a/src/columns/CMakeLists.txt b/src/columns/CMakeLists.txt index f667aac33..ced88cb77 100644 --- a/src/columns/CMakeLists.txt +++ b/src/columns/CMakeLists.txt @@ -2,10 +2,10 @@ cmake_minimum_required (VERSION 3.22) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/tc + ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src - ${CMAKE_SOURCE_DIR}/taskchampion/lib ${TASK_INCLUDE_DIRS}) set (columns_SRCS Column.cpp Column.h diff --git a/src/commands/CMakeLists.txt b/src/commands/CMakeLists.txt index c71718d22..16b8025af 100644 --- a/src/commands/CMakeLists.txt +++ b/src/commands/CMakeLists.txt @@ -2,10 +2,10 @@ cmake_minimum_required (VERSION 3.22) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/tc + ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src - ${CMAKE_SOURCE_DIR}/taskchampion/lib ${TASK_INCLUDE_DIRS}) set (commands_SRCS Command.cpp Command.h diff --git a/src/tc/CMakeLists.txt b/src/tc/CMakeLists.txt index 165af059c..883367e63 100644 --- a/src/tc/CMakeLists.txt +++ b/src/tc/CMakeLists.txt @@ -8,15 +8,18 @@ corrosion_import_crate( LOCKED CRATES "taskchampion-lib") +# TODO(#3425): figure out how to create taskchampion.h + include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/tc + ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/libshared/src - ${CMAKE_SOURCE_DIR}/taskchampion/lib ${TASK_INCLUDE_DIRS}) set (tc_SRCS ffi.h + lib/taskchampion.h util.cpp util.h Replica.cpp Replica.h Server.cpp Server.h diff --git a/taskchampion/lib/Cargo.toml b/src/tc/lib/Cargo.toml similarity index 84% rename from taskchampion/lib/Cargo.toml rename to src/tc/lib/Cargo.toml index e9257da28..ecfb1e931 100644 --- a/taskchampion/lib/Cargo.toml +++ b/src/tc/lib/Cargo.toml @@ -2,6 +2,7 @@ name = "taskchampion-lib" version = "0.1.0" edition = "2021" +publish = false [lib] crate-type = ["staticlib", "rlib"] @@ -10,8 +11,7 @@ crate-type = ["staticlib", "rlib"] libc.workspace = true anyhow.workspace = true ffizz-header.workspace = true - -taskchampion = { path = "../taskchampion" } +taskchampion.workspace = true [dev-dependencies] pretty_assertions.workspace = true diff --git a/taskchampion/lib/Makefile b/src/tc/lib/Makefile similarity index 100% rename from taskchampion/lib/Makefile rename to src/tc/lib/Makefile diff --git a/taskchampion/lib/src/annotation.rs b/src/tc/lib/src/annotation.rs similarity index 100% rename from taskchampion/lib/src/annotation.rs rename to src/tc/lib/src/annotation.rs diff --git a/taskchampion/lib/src/atomic.rs b/src/tc/lib/src/atomic.rs similarity index 100% rename from taskchampion/lib/src/atomic.rs rename to src/tc/lib/src/atomic.rs diff --git a/taskchampion/lib/src/kv.rs b/src/tc/lib/src/kv.rs similarity index 100% rename from taskchampion/lib/src/kv.rs rename to src/tc/lib/src/kv.rs diff --git a/taskchampion/lib/src/lib.rs b/src/tc/lib/src/lib.rs similarity index 100% rename from taskchampion/lib/src/lib.rs rename to src/tc/lib/src/lib.rs diff --git a/taskchampion/lib/src/replica.rs b/src/tc/lib/src/replica.rs similarity index 100% rename from taskchampion/lib/src/replica.rs rename to src/tc/lib/src/replica.rs diff --git a/taskchampion/lib/src/result.rs b/src/tc/lib/src/result.rs similarity index 100% rename from taskchampion/lib/src/result.rs rename to src/tc/lib/src/result.rs diff --git a/taskchampion/lib/src/server.rs b/src/tc/lib/src/server.rs similarity index 100% rename from taskchampion/lib/src/server.rs rename to src/tc/lib/src/server.rs diff --git a/taskchampion/lib/src/status.rs b/src/tc/lib/src/status.rs similarity index 100% rename from taskchampion/lib/src/status.rs rename to src/tc/lib/src/status.rs diff --git a/taskchampion/lib/src/string.rs b/src/tc/lib/src/string.rs similarity index 100% rename from taskchampion/lib/src/string.rs rename to src/tc/lib/src/string.rs diff --git a/taskchampion/lib/src/task.rs b/src/tc/lib/src/task.rs similarity index 100% rename from taskchampion/lib/src/task.rs rename to src/tc/lib/src/task.rs diff --git a/taskchampion/lib/src/traits.rs b/src/tc/lib/src/traits.rs similarity index 100% rename from taskchampion/lib/src/traits.rs rename to src/tc/lib/src/traits.rs diff --git a/taskchampion/lib/src/uda.rs b/src/tc/lib/src/uda.rs similarity index 100% rename from taskchampion/lib/src/uda.rs rename to src/tc/lib/src/uda.rs diff --git a/taskchampion/lib/src/util.rs b/src/tc/lib/src/util.rs similarity index 100% rename from taskchampion/lib/src/util.rs rename to src/tc/lib/src/util.rs diff --git a/taskchampion/lib/src/uuid.rs b/src/tc/lib/src/uuid.rs similarity index 100% rename from taskchampion/lib/src/uuid.rs rename to src/tc/lib/src/uuid.rs diff --git a/taskchampion/lib/src/workingset.rs b/src/tc/lib/src/workingset.rs similarity index 100% rename from taskchampion/lib/src/workingset.rs rename to src/tc/lib/src/workingset.rs diff --git a/taskchampion/lib/taskchampion.h b/src/tc/lib/taskchampion.h similarity index 100% rename from taskchampion/lib/taskchampion.h rename to src/tc/lib/taskchampion.h diff --git a/taskchampion/.changelogs/.gitignore b/taskchampion/.changelogs/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/taskchampion/.changelogs/2021-10-03-server-storage.md b/taskchampion/.changelogs/2021-10-03-server-storage.md deleted file mode 100644 index 7834601d5..000000000 --- a/taskchampion/.changelogs/2021-10-03-server-storage.md +++ /dev/null @@ -1,2 +0,0 @@ -- The SQLite server storage schema has changed incompatibly, in order to add support for snapshots. - As this is not currently ready for production usage, no migration path is provided except deleting the existing database. diff --git a/taskchampion/.changelogs/2021-10-11-issue23-client.md b/taskchampion/.changelogs/2021-10-11-issue23-client.md deleted file mode 100644 index 91a6b0f9e..000000000 --- a/taskchampion/.changelogs/2021-10-11-issue23-client.md +++ /dev/null @@ -1,2 +0,0 @@ -- The `avoid_snapshots` configuration value, if set, will cause the replica to - avoid creating snapshots unless required. diff --git a/taskchampion/.changelogs/2021-10-16-issue299.md b/taskchampion/.changelogs/2021-10-16-issue299.md deleted file mode 100644 index a74af24c5..000000000 --- a/taskchampion/.changelogs/2021-10-16-issue299.md +++ /dev/null @@ -1 +0,0 @@ -- The encryption format used for synchronization has changed incompatibly diff --git a/taskchampion/.changelogs/2021-10-25-issue23-integration.md b/taskchampion/.changelogs/2021-10-25-issue23-integration.md deleted file mode 100644 index d10a4d0ec..000000000 --- a/taskchampion/.changelogs/2021-10-25-issue23-integration.md +++ /dev/null @@ -1 +0,0 @@ -- The details of how task start/stop is represented have changed. Any existing tasks will all be treated as inactive (stopped). diff --git a/taskchampion/.gitignore b/taskchampion/.gitignore deleted file mode 100644 index 72429aeef..000000000 --- a/taskchampion/.gitignore +++ /dev/null @@ -1 +0,0 @@ -**/*.rs.bk diff --git a/taskchampion/CHANGELOG.md b/taskchampion/CHANGELOG.md deleted file mode 100644 index 91a692b5a..000000000 --- a/taskchampion/CHANGELOG.md +++ /dev/null @@ -1,26 +0,0 @@ -# Changelog - -## [Unreleased] - -Note: unreleased change log entries are kept in `.changelogs/` directory in repo root, and can be added with `./script/changelog.py add "Added thing for reason" - -## 0.4.1 - 2021-09-24 -- Fix for the build process to include the serde feature "derive". 0.4.0 could not be published to crates.io due to this bug. - -## 0.4.0 - 2021-09-25 -- Breaking: Removed the KV based storage backend in client and server, and replaced with SQLite ([Issue #131](https://github.com/taskchampion/taskchampion/issues/131), [PR #206](https://github.com/taskchampion/taskchampion/pull/206)) - -## 0.3.0 - 2021-01-11 -- Flexible named reports -- Updates to the TaskChampion crate API -- Usability improvements - -## 0.2.0 - 2020-11-30 - -This release is the first "MVP" version of this tool. It can do basic task operations, and supports a synchronization. Major missing features are captured in issues, but briefly: - - better command-line API, similar to TaskWarrior - authentication of the replica / server protocol - encryption of replica data before transmission to the server - lots of task features (tags, annotations, dependencies, ..) - lots of CLI features (filtering, modifying, ..) diff --git a/taskchampion/CODE_OF_CONDUCT.md b/taskchampion/CODE_OF_CONDUCT.md deleted file mode 100644 index 807def586..000000000 --- a/taskchampion/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,76 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at dustin@cs.uchicago.edu. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq diff --git a/taskchampion/CONTRIBUTING.md b/taskchampion/CONTRIBUTING.md deleted file mode 100644 index 7b44aad75..000000000 --- a/taskchampion/CONTRIBUTING.md +++ /dev/null @@ -1,62 +0,0 @@ -# Welcome - -This application is still in a pre-release state. -That means it's very open to contributions, and we'd love to have your help! - -It also means that things are changing quickly, and lots of stuff is planned that's not quite done yet. -If you would like to work on TaskChampion, please contact the developers (via the issue tracker) before spending a lot of time working on a pull request. -Doing so may save you some wasted time and frustration! - -A good starting point might be one of the issues tagged with ["good first issue"][first]. - -[first]: https://github.com/taskchampion/taskchampion/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 - -# Other Ways To Help - -The best way to help this project to grow is to help spread awareness of it. -Tell your friends, post to social media, blog about it -- whatever works best! - -Other ideas; - * Improve the documentation where it's unclear or lacking some information - * Build and maintain tools that integrate with TaskChampion - -# Development Guide - -TaskChampion is a typical Rust application. -To work on TaskChampion, you'll need to [install the latest version of Rust](https://www.rust-lang.org/tools/install). - -## Running Tests - -It's always a good idea to make sure tests run before you start hacking on a project. -Run `cargo test` from the top-level of this repository to run the tests. - -## Read the Source - -Aside from that, start reading the docs and the source to learn more! -The book documentation explains lots of the concepts in the design of TaskChampion. -It is linked from the README. - -There are three important crates in this repository. -You may be able to limit the scope of what you need to understand to just one crate. - * `taskchampion` is the core functionality of the application, implemented as a library - * `taskchampion-lib` implements a C API for `taskchampion`, used by Taskwarrior - * `integration-tests` contains some tests for integrations between multiple crates. - -You can generate the documentation for the `taskchampion` crate with `cargo doc --release --open -p taskchampion`. - -## Making a Pull Request - -We expect contributors to follow the [GitHub Flow](https://guides.github.com/introduction/flow/). -Aside from that, we have no particular requirements on pull requests. -Make your patch, double-check that it's complete (tests? docs? documentation comments?), and make a new pull request. - -Any non-trivial change (particularly those that change the behaviour of the application, or change the API) should be noted in the projects changelog. -In order to manage this, changelog entries are stored as text files in the `.changelog/` directory at the repository root. - -To add a new changelog entry, you can simply run `python3 ./script/changelog.py add "Fixed thingo to increase zorbloxification [Issue #2](http://example.com)` - -This creates a file named `./changelogs/yyyy-mm-dd-branchname.md` (timestamp, current git branch) which contains a markdown snippet. - -If you don't have a Python 3 intepreter installed, you can simply create this file manually. It should contain a list item like `- Fixed thingo [...]` - -Periodically (probably just before release), these changelog entries are concatenated combined together and added into the `CHANGELOG.md` file. diff --git a/taskchampion/LICENSE b/taskchampion/LICENSE deleted file mode 100644 index f0c9756e1..000000000 --- a/taskchampion/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Dustin J. Mitchell - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/taskchampion/POLICY.md b/taskchampion/POLICY.md deleted file mode 100644 index 3d84cbb82..000000000 --- a/taskchampion/POLICY.md +++ /dev/null @@ -1,45 +0,0 @@ -# Compatibility & deprecation - -Until TaskChampion reaches [v1.0.0](https://github.com/taskchampion/taskchampion/milestone/7), nothing is set in stone. That being said, we aim for the following: - -1. Major versions represent significant change and may be incompatible with previous major release. -2. Minor versions are always backwards compatible and might add some new functionality. -3. Patch versions should not introduce any new functionality and do what name implies — fix bugs. - -As there are no major releases yet, we do not support any older versions. Users are encouraged to use the latest release. - -## ABI policy - -1. We target stable `rustc`. -2. TaskChampion will never upgrade any storage to a non-compatible version without explicit user's request. - -## API policy - -1. Deprecated features return a warning at least 1 minor version prior to being removed. - - Example: - - > If support of `--bar` is to be dropped in v2.0.0, we shall announce it in v1.9.0 at latest. - -2. We aim to issue a notice of newly added functionality when appropriate. - - Example: - - > "NOTICE: Since v1.1.0 you can use `--foo` in conjunction with `--bar`. Foobar!" - -3. TaskChampion always uses UTF-8. - -## Command-line interface - -Considered to be part of the API policy. - -## CLI exit codes - -- `0` - No errors, normal exit. -- `1` - Generic error. -- `2` - Never used to avoid conflicts with Bash. -- `3` - Command-line Syntax Error. - -# Security - -See [SECURITY.md](./SECURITY.md). diff --git a/taskchampion/README.md b/taskchampion/README.md deleted file mode 100644 index ed7cfa77c..000000000 --- a/taskchampion/README.md +++ /dev/null @@ -1,50 +0,0 @@ -TaskChampion ------------- - -TaskChampion implements the task storage and synchronization behind Taskwarrior. -It includes an implementation with Rust and C APIs, allowing any application to maintain and manipulate its own replica. -It also includes a specification for tasks and how they are synchronized, inviting alternative implementations of replicas or task servers. - -See the [documentation](https://gothenburgbitfactory.github.io/taskwarrior/taskchampion/) for more! - -NOTE: Taskwarrior is currently in the midst of a change to use TaskChampion as its storage. -Until that is complete, the information here may be out-of-date. - -## Structure - -There are four crates here: - - * [taskchampion](./taskchampion) - the core of the tool - * [taskchampion-lib](./lib) - glue code to use _taskchampion_ from C - * [integration-tests](./integration-tests) (private) - integration tests covering _taskchampion_ and _taskchampion-lib_. - * [xtask](./xtask) (private) - implementation of the `cargo xtask codegen` command - -## Code Generation - -The _taskchampion_lib_ crate uses a bit of code generation to create the `lib/taskchampion.h` header file. -To regenerate this file, run `cargo xtask codegen`. - -## Rust API - -The Rust API, as defined in [the docs](https://docs.rs/taskchampion/latest/taskchampion/), supports simple creation and manipulation of replicas and the tasks they contain. - -The Rust API follows semantic versioning. -As this is still in the `0.x` phase, so breaking changes may occur but will be indicated with a change to the minor version. - -## C API - -The `taskchampion-lib` crate generates libraries suitable for use from C (or any C-compatible language). -It is a "normal" Cargo crate that happens to export a number of `extern "C"` symbols, and also contains a `taskchampion.h` defining those symbols. - -*WARNING: the C API is not yet stable!* - -It is your responsibility to link this into a form usable in your own build process. -For example, in a typical CMake C++ project, CMakeRust can do this for you. -In many cases, this is as simple as a rust crate with `src/lib.rs` containing - -```rust -pub use taskchampion_lib::*; -``` - -Arrange to use the header file, `lib/taskchampion.h`, by copying it or adding its directory to your include search path. -[Future work](https://github.com/GothenburgBitFactory/taskwarrior/issues/2870) will provide better automation for this process. diff --git a/taskchampion/RELEASING.md b/taskchampion/RELEASING.md deleted file mode 100644 index 78650fe0a..000000000 --- a/taskchampion/RELEASING.md +++ /dev/null @@ -1,16 +0,0 @@ -# Release process - -1. Ensure the changelog is updated with everything from the `.changelogs` directory. `python3 ./scripts/changelog.py build` will output a Markdown snippet to include in `CHANGELOG.md` then `rm .changelog/*.txt` -1. Run `git pull upstream main` -1. Run `cargo test` -1. Run `cargo clean && cargo clippy` -1. Run `mdbook test docs` -1. Update `version` in `*/Cargo.toml`. All versions should match. -1. Run `cargo build --release` -1. Commit the changes (Cargo.lock will change too) with comment `vX.Y.Z`. -1. Run `git tag vX.Y.Z` -1. Run `git push upstream` -1. Run `git push --tags upstream` -1. Run `(cd taskchampion; cargo publish)` (note that the other crates do not get published) -1. Navigate to the tag in the GitHub releases UI and create a release with general comments about the changes in the release -1. Upload `./target/release/task` and `./target/release/task-sync-server` to the release diff --git a/taskchampion/SECURITY.md b/taskchampion/SECURITY.md deleted file mode 100644 index 9d8d975d9..000000000 --- a/taskchampion/SECURITY.md +++ /dev/null @@ -1,11 +0,0 @@ -# Security - -To report a vulnerability, please contact [dustin@cs.uchicago.edu](dustin@cs.uchicago.edu), you may use GPG public-key `D8097934A92E4B4210368102FF8B7AC6154E3226` which is available [here](https://keybase.io/djmitche/pgp_keys.asc?fingerprint=d8097934a92e4b4210368102ff8b7ac6154e3226). Initial response is expected within ~48h. - -We kindly ask to follow the responsible disclosure model and refrain from sharing information until: -1. Vulnerabilities are patched in TaskChampion + 60 days to coordinate with distributions. -2. 90 days since the vulnerability is disclosed to us. - -We recognise the legitimacy of public interest and accept that security researchers can publish information after 90-days deadline unilaterally. - -We will assist with obtaining CVE and acknowledge the vulnerabilites reported. diff --git a/taskchampion/docs/.gitignore b/taskchampion/docs/.gitignore deleted file mode 100644 index d2479eb14..000000000 --- a/taskchampion/docs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -book -tmp diff --git a/taskchampion/docs/README.md b/taskchampion/docs/README.md deleted file mode 100644 index 7aaa35c16..000000000 --- a/taskchampion/docs/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This is an [mdbook](https://rust-lang.github.io/mdBook/index.html) book. -Minor modifications can be made without installing the mdbook tool, as the content is simple Markdown. -Changes are verified on pull requests. diff --git a/taskchampion/docs/assets/cgi/LICENSE.md b/taskchampion/docs/assets/cgi/LICENSE.md deleted file mode 100644 index 1d4dbe059..000000000 --- a/taskchampion/docs/assets/cgi/LICENSE.md +++ /dev/null @@ -1,2 +0,0 @@ -Copyright (C) Andrew Savchenko - All Rights Reserved -All files within this folder are proprietary and reserved for the use by TaskChampion project. diff --git a/taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_1024.png b/taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_1024.png deleted file mode 100755 index d4a4a9e13a759f242f2fbe470ac198abbc465343..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 566832 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�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≷? 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

5xuPip24S-Od zr8YTF!0fX1>01rAwa%*gvRdHn7X&a5Q*M&IX6LDsZqa z7~Pebp;>Ck1n{96Y%g&DNb4@;jbakVT2Us%{mNCxe1gH*81%wQ;@vuHL};B59hvmu zX+FBWJ`qI`vv1NajuVEHG z-n||ITHi|30iOUKgna=4*kemKZcF}SA2+$3dxjMNil^cR8I>uzG(vf`=K%$s1P(yJ z-?n;>#j{ndVAT8pS>;tv^*3#rnwG_g_i=wRZ7+?h*un=jJ$@VrPRXwmP1vI>mri(| z&zSqum%xJv%;6`N+)e*$wJ}F;6BsxPD9g+|^>6U}d!pD*m9nPgH0&X9-~Vgj^Qgsc zpqCY{VYi+)I>K#e$C+RP@7)y1Ce|9Js;y=G%tDPSpVc8y5q>m=rf z78H-qk}h)pCP@#8Qp6hSq)-1o9iga&uXIq(%87L3yA|)xM^GstjN_{y|NmhzjLQXS z!CvZ&@8m^`J%#XFO*2P781sn;UMgW)8~S{xzFUk5 zeP|m(CiUF0MDmSU^L( zS_+U|0UPBVJI+!#K)d~(Z#Cx86&-rJha{i+a?<#sPKXMYjRUDiQaogCmYjCWyqVct zcxI4P9aBMs*a89uv55x3g7+JsJmM`yFB8z2`H_3dti1(YH+OVWY=f~=22apF0rPB2 z#fQ#Mn#5X2k^*z4<=Nz)&q!G6NOZJ~O!HsB%lN9gi<(upCGf5|f8hH2w8t%$A=^5M z+=E^Hn`QcmC0oR992Gf5>wC0r4A8&rZp>G`ydBpEk~yyEKNJ>Nm-9INy)(e)J>h|zZ9&b)%Y?yhXt8U#CsYU#CMvI@(< z*3`sQqsYvFYjr5@L zsEq3l&*@*Jh-fuVu*aKVXJ>@75Q~$7`5;>QXgygp zvtp5#j&MFTG31Zj{G?KB?77NeED55V^}Vjss?%%(o71V9|8?zpt#Rj^+NA8i!Bh08_)GbRVClze9)R_$a%8IHlSxg{(Ert`io0tM zd=?c>A4O}<3nwi3E-4pkY63rbFm(Ytiqzu~V6m<0Z4zN>9=Pa}?~xoS&`;B* zt>n2HP5!hS9oqXWrDPIyx$tw)1q^vd{Yiofupwt8Yt!?i67xe5`RA}Q$7;^OxxwOq z16Py<1?F|StpFdwLc-g5IW(qik!DAY2C!p3=>CJ^(aA1mwk(3z)^}E*QlFtm!sP=3=S8 zWQ2)F>l1Y8&*BcpNY!BFzAwzo-l!5qf1jO@PSs=Ts6ezoY@yu_kniJ#*))%KoHRjd zJKfC~h1iJdRg{{$W)fSD{_HVs#yx}7{d6~>QpzM49basZ&K4Li_T%TH@2Eo0zrp{s zAugw#MNHG@DBixMzAOh2Gv$<_}8 zU0TRRCxwkbY(Jcw3&jk%r2Ynp>F+-jXw>;G zH8C5K&L!D3`6nqX5L$F*!y16_;T?_-XGy+i4xv|%Xoj+`<0PCYNb6;WA=o-z_(X&7 z03%oC9S4KuyFJ86+Xg)uvI)RgIan=vHM#!!lW(ILOwVx+M23_VF~G$#t$t(}}E5a2?a z%DfF+5%3{Yz7)w?b{WcjZC#wC+gY1RN^kKw`^d3sLKALkk*B9#{Hjb-8+t4;p=!#g zCAz5GBEu{p;qqEMdHlyR!44w3+nb=}^}=oT)cfc6$_Fnk!BS4UQqce`UkRuE3ceBS z>Zk}JNLoM9_`AX0;E&YrUmLklbYS#U;Q-TEeV;49hN`h0r(h8PYd~dW6Xc)mDFdPH z&6(DdmIQy47$lDPz)o)u(C&gX3!usE7nuUqB^(umxZ-;Ql*skNxjFY|xn~Zf4_^{_ zZkXRA*z59$Pw2tiiS}&6C3%TK@`ityM1HKC;no`G>XmGNo}D447-SFyl{ynFgpJ)X z_8?rx+xS;|GD&+d$lBiX#94USgskCRjpqH6ZD+5NW@s6b;V{PQG|fwhl(PM`PO zX)#2x@6iA&gF4pSLqE<{7Z}x4*(-eldU@q@*Ye$=$q*`OqeI4zg}9gtCK-}N5+Bo? z^!(&a^JMP&=@+rIWvOj`?@s)EzxjJIY0X1i>f_U0>JwMz!(=MXCYu!sV0IXL0sVs# z&{)j6Q1?(*Fj3b)mMgJrguJ zd+n@*`!AK*=Cu)eK{!^Orq0 zABmktVSS79t5Lgpj1sFm*X;@D|D)+GqoQux@9&wRySqWUyITQiq&t*u=^PqFQluqC zI;EvSLb|)VyBTJlx$fWpS!-U;t7Co7BR+fY)IR5bT{9}{)!Ai0>kp3Ve1ocyK2P@R z=^_{#HVPLgu)5<>y%0A=O_N0A9g+R_UQ=Aak&tK{VOstTQ7m#3GgmGZV==7E4@;rj zMJ+s+IlXZbUMFZ0)Gr`fo))m@tM> zqJY$NKNBnAvsyBEy z5|(Df9Diia!tHiw@LJ~6T*aXQdzTWy`xvlA4YLRMPR3#OPT+1wSo)&LcYo8Bvd9-g z1CP~eVoqyhv@{@d1k#3@XU|wd@RRHD-KJJq{U`|7W{F%3b;_{j*)~59ul_<)3g;@{ z%vKz2?`~AAB=rf5O|50$DJ3dMvjpimMytik^of~wlIjRA#%FZ8972Q`FWC`DwfVvbsI2g~PP2>T!1p%60 z4_;kgRyRV%Uf96lA9DWVq9`P4Gqqh$etjaQ^!OAU+xJeS?YznqImbDmdAKFAJZ|cu z$7`wJuKbd7xg~8+p#7as1jBwkbKhzN@@=o3-HyDd{19aq%K7BbBmrGve3{-GJse0^meKBtN-9@En>Sn+e z&QF4T&_d~MQp|>!aaH{)yp#MY_>+;B`yDC|r!FoJJNDOYgPmeF7M}fKG-XazeL4X_ zbc8V6e9eDj$+Qo@tnECCm{~%Kth<%;jii7=nP1eC)lpr?oC%aXE#*{FEJsC@Fx?7l zo!VCp85Ep>t#tQ`V$9-Mk&f4Haq&T4 zVAUwMf@-hx1BaX+CU5DD8jX)vD=%Ez2z+NcNKk?M48?mfX#8n=G3l-lj%p3Cj9duW zj07WHcZ7W|&jdl*@u!*ND${8i43>D-2ZevP^?9$n?R*_?;W2Xy)n`$X$V*#CPb$8a znJVgKC9rUd(l{9Q)CmkQ4Y-tITHW(dT(+_B7(wI_8lZ@C(r1TjL!Soq@8%Uyv}yE@ znx(WZD0GVR)}pErT~isn!ARjP#Z$_QsYj8^HUtAmcY$Y>Kh1T)=mF);qvh?yi< z`;Z#jpjqp$iffuLsZ4jG$}@Y}PWfBEIriz7JrGvz#6z(E?*j5LK54$Pk>OF!(}o~#!ByL@$+*t_OUBB9?EH4ixe-@E>&98= zmb-X<7w-?n`Y1Mgu;tFSM9qa`!1ZUlw5olyEDj`G*rY<`qiL2z8ZBWyyau zXY`6KqtssUA8iGVw_tbynp;sK5Qe6+>Ab1)zE$?l{~htVVpalNP0tfl^JjD0CybB}rgr-TIlyB4l}U)6=-!SI>Q;STUlJzjqqyxbhekO-Ah4B1oIDcHu)E$`c zp7}pmKKCWPMe22387{6 zbH5d@k>o9w)WSr9TpK%PpT>4kd*QtziW#aQg^0>D;H$?>oLlsabG|8Q&hlF8VSkppU5v!_t>Q#C~7$GY&TmEj*notRIpYA zWwOirm9DjgUw+Vo6!`AkoC@Y_@cKXs6>@OL&-q_|j(P!&*k9d5jqL}&#Nmb{>$6K6 zdOIT{(HU#IFHc=&xtKo7>))|mL1!5i#jol|wBkuy3i9r$4YpS!b6a)aA1}NsnVm^3v>33OV$O@9=Hr ziHS&c4iu$6Y4{Px!3cmw>vLRe7scm!jfPYW*Jj*=Q_+QF;ac=IP{uZm@@=J3bj8>v zcFjy7X1M)G*yE?EV1ko7X~S`X3Yg=UD~brjfLPPD!Q96-Tsye4Oq}kS|6h@F-k}&~ z?7V>n4#sp^F(}0{7C^qgPyTEo6dq@>%tXh1;~sI0Tdne&=ZOyBN?_pnO1_+&|pXb^BF&QYKmeXEKnYKK@s zVq_Xt_!>fMhbBkj%u}H_E5-V?po$w9RgeQ6(Ht}jA*p~<1n@{;^I2F0z#<9H$20)f zC-Ff~&aIn=kU8`xFTCd9x0#|YgtY1vV&oqgkpCRGS|o7!#u}9Odn{OzYq^tiM{AMS z70ZUG)te=MN6!WX8V3({Wio#vz40hPqYJz78NRVmj9*>ajoLQ_kgv`TI<)H|CILAi$&Evk&K|FF#CacOz@ z^HWB3SDSDxe2L^j+FZbd8?gV=Wa!d$hwaE=COCrNIvWCNvW?Ke@pDjv14xGxKmwYb zXyC}$hZD>BcA(K|2Qw$FI-^!`BX$B_f?_F7eBN>Oinx4J-iipBKSF<4&8*UD)faX$ zgAct8B@KGZ{A}06Z@vp-$UTm`dM)E$Ql$Hk#l#YC4KS!@M?!2U| zYbN2HojG3K_#jMCM)|c2qLLci&=_uk4d{*pV3hN8BkF;Em?EuTYZ8GtJa{uROB~QO zr4*fm`OW;G=~t&)UuY3C(#!au@K&J?*Fq={aO|-mR=NbG*geZF1mEoCbzgK`ibr0d zGuMd6&7C|~3Saf_j3gMxu7Rx~k(98|_EryR@onP2%mOyq)Ai3_gOUcO!10IuE&^Ot z(J2uuUc2LqR!>qM~lo ztA0)KzjjrJd4K#2O{awQci`N9za*M}Is1S2j3#-eI5D~opOTKzKD01afg43A*G$3} zWHxIC-puswzCZ(-Rp?g};R`jnE`ryE1~t8-wT9_??e`0(4m*OWv0b%T#JW4k z)#ko$M$@?a%-L_$7GV9c2BvhSjFZJ}iR)+&D>Z?MWMA2S%9T?^z1&*QwxV- zdZYlZ-#2uA>g8_t?SYX3@5@hVLWl)ll5?3`kR0Vse_uLG2fy}J5P<~nnZHRcp?tQ1 zX1;Q3MCyE%3+WW?K3kyfeUOCqwZN9Mvqii4YQ})KG1j8n1y|0#4@(_@O-${3G&rfm zN#lWOkip?4=f!)VU3jPc{3hVmwlHmh`uO9UT~})puEAF`?`JBS={9VS&T`>UJJreI z^=5rVy@!bez=?;&5P`?$D5+q=PQb^vIHH`L` zWHVMrQSTs24YEa`JrDJf^4htn^BK+48W!rHM@;?(dk{yx<1pm`+^7IpuMVfG%>1?% zu124j;hG;*KyhRsNOhOg4X#VtC#*di8hh!o42i-SPvqVzk%r;?yE3nDTZfAqWfdUL zTlMRa;V}V_4Xkb#y9C(jr{>!NQ5}JHogL@kMM_Gmu7lT^helP~tJSycGy97A4Azc4 zdhOi;VUxKV8AkM=jH`#BDIwnm(SouwGU_6zk|*+o4?B=dmpj&i4R>B}@8S8gbp1rZ zGL2&%s7^a5%`oNH)+cJ!lQdae)wicEnI{_XyPYk|IP*l*_FdO#4;6LG+1la=Hk^7$ zqJ+i6BWBN>{nj7)fm-|_UB*5e=uL1xOSs-vX9%JZbf4rpKOR7Efnoee^tK@o@h!vrF$jj`0Gyg6{qckd;{ftCR4YQ5upAFnWcPY=EEp3 z?%xL%Vp+{rE`}=h!a3V;wGUa;J>nv@W=cn*{PU_s0dJ#wys|_XtkBGP3DnGq?IMgp z9HP7fTW=Fj%F+HX7vG*ezJ&Rh*~VXLkJIrod>_xweeTZ4PGv%{k|O3nj4GW;_cQ-o z5Bys3|@H~#VqOKUY?j8b;HqLxYli_6iHA^~2}jzQfN$d?{@FB@0Ww{q^A!nFVk&&HFJ z5t_{>zw0qC(%1*E^pkKKtmip~=bOEr=GpmGRj2tGqp@RR3Zg=)`R?!IcAkuez2*&U z!werCpq6RzJeOsCDjR0GuL7=m2bu69r2!gKYn8J7iI|rfio?zDfcaGQg20bDFKz6* ziDQMZ}smtySPD9f)F0C7jJ?g11~w{Pr6sOXW2J`TT`e z>oGGztbr9@JG0XO{VU@0*gH;si9-{VgO^HO&Ycf8W-bjWG&_S5vSa2(3%2-kUz;KC zamNE#i$ZxK-sY#XzrZ0I^2_c@w&k)Eprin}*G!K~qm-kQf+|KBkw;dQnO2#K7^L;* z{Y;&19!i=Rt5O~pJy$7*IiXw^*0@+q*76UM1YNR|_>{xMKgk<%9-Re@P_0P7)q)Xf ztqt_)!3fm?Q=I?=&>uhl`w(~-eYxL8-G|vp@Alc_zsNxjf^)Lg zUnF6;k>Gr;jK1WTPpCbQueM30L}NvYdUVG8d?Cl*I8V7vl1j;jC}A=QTGTg-I-yt7 zY5Vf*6L5Wf@RvSYv9_FMi(_-D^oTB#a}rB*;&$wK4&nFSwrW7yqw7t%TkxEII%6KqZ#D*nF=#2>x-1my4}1d$Fsfm|o;pgi9?%81Ky{&svng-+)L z`*E%g2bT7}4Zm-U@9dv<14YfI#n~7plN*>{=tO2pTBY%84x^9p|2dBbBoqB*v-=FP zlOf6%#GJNfBUQ#C26}!8pK`QA7Ru`t4nrRbNan}**W_yLkc2$F|6b1+EyYX8ucevb zRC4S^1x20$(~{3)$L_NA_31(R`dSPV>`4cJogaZdfdN`9lLR2b`z|!#{fpxxJIyoA zRa0A6zaTI{OgAN@94%kF&TpQ~XITERJ5>?D+J0Gi<{e^hWP%Gvg3E;fA%-JFPHMQJ z*Pjrp`?JK@GyIe==AOCuiTu7DbieiHD5vvbPv3SvM;AYf6-3^L(x=*-eW#I8wBo@x zGnh+RF0wn4qu>i?y6VHeC#S7T)PF^4xew`a=JB|y2p;0u^;|tLS^zj&=~C)!JFbR_ zy>QmNwV6A~=Xd#t&kmjcWoC^wZvu>>x;)Ij53!p`Gi3U~txI6RDg{*4^oCB`Ue2<^ z$lvWZTFjH(z{^YtwRV(0?Ux;>5}BeOezTRhVgL|J`!zZ)c=PG)gZ~~RXlxo#!In~; zy^p&pN}%We*7jTZ@8hp<izd(@p z{&yz{kJWjdU9SeMD3`zDGZt~URa#5aEP&-N=Kf`eIz6c3%!k&S!yi`KUw#wJU?;gK zTTHA1*rW6Kwcqlf#^9;=eqFquSD07QEWn3Ud?766VU|5&R1Zwi!nMM>RYWtXyexpl z;@k|3KQDZzw(n!J;2=2isfHOg_mt-crTK)}iR3Gdunn2m`aJ_pvG`A&P55jX|2Cj# z+d;fcVkPToPtNQeW$I6_8jl|u?X$5iJCKHhh@gM6=5JUZ2-615vP% zJ5S_ga67;y?G-{U4PC}UR*E$dlnQ_KdovjIgD=_K@el>g=T0MwQ=CA>XUPhy`9j`&<%EU57QFN=Ww3+Dl@7g;1Iw%oV|-~VgSesQ#=2jH zuDX%pom2x-A{C!eS?iDge7odgzTh)OW8NE%qq`DZT=MK?|8+op&L?sWY`jBwh`zIp zrrUmKtud_+d;Ya@->xwI>2k*Lmyh=~d(*c`bt)pLbI+>|%Nn+3A`cV-*cs^vHgNX?2&2T^_C$3AU`FC8x(0bxz;5t}DfsvX20(~igH`83E`Eu8 zfWJ&#?od|J0CAbHwJx~SzWl6flIY9yKo8}cG|?l5ty z=LHfeodTM@WnKI=u}xpTjv@SfYxKNNIO)b_U1u9V5tY(Qso{Jtv?~&<RDa4w{h=MZ=ES-a?2`VH{=DGw&YdDmIC8a zqx!YN8&>>G^H63CV>c!FKS3g)3+tOeC+4+|a5a_OsZnJFNa6FRoM57Kfm2HO)_Gp+ zQu6QgKE=n+nYSDRyCY(z+%=dd-XirHm&;Py#%^q4HkA~bKKM$ow5e=*}n zELc9sn5C*Ahgq8UE^!gyOsk;t<_d=qLw@`sABuK+2}V_mr)FMKw5R=)fB`l3z@%>M zf^tTbsEmyg?;5A9vf+GkPEM`{A4ilT2|(i$-=7>Iftrc>DRMgBzuS|!rv0W_Bpzi- zV>6|Dj%{x+3W-z97VH_rx04mU7Zc?_=lVJA1Lj-@DhU#VyTX{wAqSeKCa64_9^H`; z0LMNiF7zpK0w&q+7K|WH7V}h&nR?2L$&kCi>>ebR)`SUvG+ESMH#DG@`*_8iQ^?F& z(@KXL8#sv73KKgMs$Gek69g+1cAXJtSqn(<;318hnMeXA7gxs9FP-wW~jL5)w z+>F8g*am6akFKsIun*Nx{s1U@TZQjn>I2YgEECY(4o%!SJpQl3s9<{|u-fM!_WyIe z6N#+K|Kh$vPnK%tt?_f}DPF1g>d1GWq@L`7JfHNS$bKJ(z=Yk1f2P4caSfmAVSHQ< zrR#@*_8${)Jc&#A-{8$~U!0;Z7QJ@$-zi(dO4X{XX7B-IEMSt^n3vJdZfuJxsFi_0 z-;V)?pAGZ>4TQ5B;M@;bpbp+SDdE$~lBZDWR2~wB$%@xz+sjYJ(T3Hs$NM8h)uiL* zlOf(V0GlEK^kLF3oG>bC@GiH-JR4$Z!bBLZ46~}($LN*It%^iT{So7|&(qRl{b>0w z2>Fg6+xUkXZl(J&BJIDegCNk0defgc%^~7pn%5emj%aIEnkjKeeG9 zbl;%D5nZ!!^P4YBgU9I4+?M&~?++gQKYO3}AgKigJ>PEbay^E84OcF3UAx3ZvdKD< z7IM|XD|$t65rk(c0D1|)4$kcs2U$8&;!tyId*({|SYb(tZKs6@z`-2vrXLYv3w&>D z`RD0PZs1nQF>9BVp$*&$bhIlI;6@_-GM#=|(*>DH{bD-^eun^876xtar>n3>P{DrT zWXZ$mnp@A?tFh|lHt2#*%yT!0ZIAjU7etTP&h6Xj;w_sYVog_<{v`KE1!Kt!V_G*5 zZj~n?ML!9R$1E)`%NYCc%Cd5fZUeO&O1cP*QVsv!6kWsf&BW)SqcDeY+>ps>pV;#9 zC2U?Dz-%_>s(uKO?Z+^ihl^l(oe(77`{&j?=tWfT981J9?d@UO_8iy3c-q=(a#raeiCK>l!h z*@sYl9_=RHZ7KT4qkG5GEhY14zux!wDbaGitWwCdmg7#l5#dx!Mz>%3WptyzkUyuL z7}yP$@0~P)%|(KdqpjUn$3Y=E`K8bW4V|W@VEdbQ(;4La990z~;y0+3--U3j>G^v7 zYu&e>G#^k61soVA#oPzXWsV_kfVPw60i(GqS$OWKC$4LvIhIJivl*GIlJQT=D)G3o z=k^D$d7KBy33peqyJ(%2;SdInDl~p%?p_Qh8-?TEIV)(vn2hT27^%E53Ac2k4j6v3 z`8^mc(O10;#o^mdV1L+zuE~dF>`320@_xX0Z@4>5odUp)rj1`-;r$i-$Ayz6B||Hq z7oDN%GbvS!R>M9m&c!eX1lAT^djzu!UsYsAdxRn$XcMkaP4D+voTK2LHh?1uz{&Z* zE;dDYcte$T>^}hG=MNJ6v9nAILe0;a=~aG3P!}lx%kPRTjfsMA!pJKP#5f)C_Zu+h zkH5yZ-~YCrvGA&V!({wcM2$X;r%pgYEx*T4uqj^mR?T9wH&h~YdiUl*0B`M{0V~+0 zxtX=k_R55$CXI-;BZJ!O!J&^vkqjKTm1vAQNrF(>Rc{{Bh?~FU4k`0_O@H*+fFfjg z{pT*r0XJxk=nK5102Q}pAjF(sC0AVZWG3NSgz~=*`ney3%v8eFh^Ro~Sz{35yVs2v zL!()ziDlgSYQ=e;mhIYFfEqSrpvhciGJ}D69asOh@8J1SSTBW8>0Hb2c(G)8;_aJ#S{YyqO12$o%()xz z+RXG@s~Oyrt^&@W1%La#spq8!lUy7*4+EVbrBoBi$wRWs+RfZgR{d<0Xp_J?jrN0c(D1 zj!tbgak26(IKBU*QkUiv`xiVNnE%~X;VKJRrj}--*dZN4QIML=so>sG34b=*9fp6L zB>zCrQw!O_cpScik)TLMqmg9s$ncIGJZ5e@z1$6V`Cm()XP-&K1BbE?^1{lA2c7Tw z;I@eQr5^f&F7HIlev?z61vyN8Aw1)2ZH`C`s%rPc@-?9 z`n8@N_RW2{>XmHKom=9+qk#6M8#_c+tX~U8a#KSJ_q`M-xcEcEqtViPVPtR6w^s&H zBZ6W*8q{|KhxcIZN&dGF{&`-y9z_=7RUTey^BF|RaJQ@?YC8d2OR)lZNWM#|=XwiY|u%?T_Jn#Y- z35lm*S;Dacr1p=w`!gcp<7Mi@PLn&PT!^1N6tMcnLT|L*DoO|pFmj|J-*I&Vx>a_F zoe{@}g@u#~sD5C|lDx&`uOhKA;NoMr#Qecqa#0na@O|KP6;wq*Dq_}$@jOb(qGq)U z+G+O8AaP76`pl2;{VJ6=C@1)`^2jvZ($b`>+)S=HV9DF6$}M8=D2yqjXB`nCP48D0 z0%|@6#c1h7_6s%cs+aTMA`B)zl$b2Q0UXRL-ue0%ZVN?oIaV=?0%@9FvYSH&eW4IB z@1*=^a|sv;TusBNv2)Za29>wmT>jT(L79!*9h*FDCcP)}K4CYu759RMJrp~Ut^-5Z zmIV!1n`!yNEA?&#_LQ1bcLmS!0fp!oU18|K9|$_<#Q9DDfdrY9Y6TJ!^& zcUHZbqu)gol7H|xAqjhF1TS4}WR00m%!|->xZ$Jz&(4c8ay{32g5)ax$ND4r&y%7p z*R2lApF?gIjxUj0E**%N73l&CefDB!ec0NCKlSMD(DEkxu*396tsKAV0R35_G={N= zkDAB){`I_X6g(Tj6MUUa4UE9d@%h_eBzq#kIgLYrx|qrt;YF_ap7OBzATS?nZEp1_ zZ}#z7yc0{H`5j&a@v1_2UK{;`VUp8S#JT_jkJaGy-ur*E3~ap9M>oP&kH-;!WQ$`o z;36%&m=M#m5o&Q|u5uM&e9-LxC^?|XFL#x&8-5WKvz$UoH20%9r={yIVqt~`zuWvE zMO*?OEpgBB-yrg=MzLYm+H%^k3&GA?!QGP0XR$XUY=|P}!-ApFtzwgvj=R2rc;=8{>E00! ztsQfK1o(zEa2qGMSj>c?jz-}}@{FJdI1P8_DSdJaz7o->VDS$JM@S6ZgA z>eA{Q&%b63nq*5hQHVR>J*mc{MGq+=+Pvu@0O?A|-Y96BvGad4y87K>lKMEl{7o$s zw0=~HlQ$tp=9}vw*u>3L|2Eq$R{_@r!_L`pk0RmslMy~{Z!%v=<`Z`oz4d45ms3#% z`vquDGQfdygfeiq2`9}2J^NOc%+nki%kI8>CbDhmx=&|fLRby0>{c`FqpJS3Cy{y-R(C#@nU@Wt9;UdN z*H!lHc+|G4A$K|?R=GmMt>3R~NsmMqA?6KuSK^(KfT}7ws;!dl%_1RNwGg#Qza2tC z6d~4xMpPj|pg=FhU>WUO$CRXSpllu3Cl7EJL?9D0L1#>HMvRf4^Ox=X4@t|#ea=R{ zduiD3o4s~3f<=f8Wx~WxeHRS8oqA*l=&lL6f`MuYiycYbg?LeGf;_LKo6f!{kJ6~;xD(GaGs|3p+a9?Y{ih~`jdUT ztu<1~P3wSPLlB1byl<*OF4(w7Wz#&;6|^ZhiEPg>(CSPu?3L_1#~sW|+ugQdN?R(d zp_&o^-_S&Fhv4&{)8zIJ`fvj|Akk~EB`Vk$+*aqhl+y4&CpzQBUgD<9N4jag*sGy# z4UzgJ1JXhgOEg<4m&lS6A=jDhQIL;FY|s}lLKx44_+xx%v8S%onBF;N0Ec-Y2b&Jf z+G@()t_>^K>lcX~yv%XFpws2SA)vRsP1YCI$#LaR#bH2R`m??CnC{17b@E)K*6?tt z5ox}o1uY}C!3p&u0onQwBNVxUTR!ZG)2&|Np+a~#$y@3omR}RS;z>Nz2)U*o+LWZE z#bs%~n0|G?=a}6(3zAgLsI};cD2wHStcb77y6k1NM=eNS9bBH=5J5*^Ptt5mu<3!~ zYncq7MPf6y>o&NDX0z>p;zuZbH+Q_NE=PaV(lHOVF1mKPm&KoUJ)~-;*&g?9{eV`t z?yrKG|I$orh4AJ^m{zmNB)FC2HdHm|o>h;6LW|nXqW_ACS%w_U=evYVFFnugW+~;I zGB?JLxMfr(D8FfulKq0A$nB7aSygwFrD7drood;(zBJC3+%ZZ;bNQa`9)q8biq+zm@YqPwf?YN6G}1Ain`%dYS5(z= zJ#YEqgo%})muG&_=Igg#yQ3R^IjDn4Em-+J<=PE#Wp3>uc$wFtmirRK(o+xpVx411 zWsdWRthUe!q7asR=FN%VKn`&jpCz|AWwBi-p9cSW3rxlB?)vQlRL%1&kIjRUlqEE-nQSx*dFN-?|8*=y5T(FPy1xoeNR8JP;vO_Nk4s~WwL&#AYRUo zag!!3!C#T$D;yp4cVtSX8lJ5D>IB5?PQIHz=r?I_6G%UuL|4}n+}MDHG+X1hMEO0a zLXLh$<;8ZT4g?u&F;wUFnQ{V?NJ~dJh+mLMmk?L?yhDUo;E))_84MQ1e3_6LT`6 z8>*1920zB`G?sb1O&i9r&Gg&i1C$1qV0??Ew*+**(|8R{tBg0a*XGKm#|WaCyBHgL ztAggkYy1%Ro#}ff71n3^w}Hruiah+uN;<)riwA7LVCorkYH$NVSYWq$EPt16we**h zqu@HO%+V|JtH-P6rr{&eL!G++yxi*rs{eb897D;dcPob%uJ4aVbB`T)1$orE{ z3>eqLdkO4uFGn_4+LF(m-bHl3bOj8jKDA-&ywZjn1wbZ(CiJ*6#EdB*M3q>`g}W= zrubPp8SRVvJLH3u`}8qBgDcCk5nr@@*6DDP@APGo-#T3jBHP%3adqjJfx?V3w~X^0$8r2_I+z}Vbp7d@!}-9_n~Bc<+E zR-n4YK?sw@yMg(!8n_H0v;R4Np(!b%J1h!04rQ6g>J)Fs7joe4;#bsrXni195Ij?s zr^wLqM)5=I_~Xs9d~K~}(#Yg0Hogw2_xv>WO8?j9en-`Mg#fGs6NQ8u2! zi{{o0O;#aSo<{=izL`mHH*Nfd6JR52DQ)*FGZ77VKZKu9(e&6o#6y(tr?ALeHc%&4 zAwW2lYN78_FRVbsG=(a-5*+KAM=b}C-jLRqy9GP!!#aO{^{)wtY~74SvUjIMul;QD z_d2wa$w>OsuaNk9!HQHj&yb2?OpJw}GA+}IBh7zPG|l4o9u0U-LX>9>3^i|*qIw2Y z>^{9ExPaI)cJs*FiER4?=V_CJv2rDvm%QuQR07(@hehU613!GHU?{(fX?+!ep^D_D zA3xTh!Qh7s?3sqXV5!ZShjnDym;{*j*|W3{L^ye;tL}0`laCm(FxA3~{onVAngO@! zh%0Gag1+it$zeqvx5CYmMg!3XLlf)=W{Krjkuoc&m;~pa#%0{w={`AbT4{yS5nRVxYjG!ZM zvs+_QAu=)j&AS1Vw_|4)`%eEA_kB*49YUiBm*xBZHW%Tx(n)A}ECiW(r_BT`I4;1p zeZX1C3M{PcA_g0x;W`70&xP55s(6zSU6PvX$}m```fd2c zU)@M7;bvRg&_scY{h}8~<`k(CZoMCCyTzRyB9E)LlEjS`*l9yB^$+XF?N<;*#Pr?` z)X=`iUbHjltKz2?lL8280_X%BYlD{WrLO!VpgOMns zZ~GWDeRWOzW;gA%ywuNMXMQyRc?uek(bKtQt`30A4&IPxG!so~K;8KB*pD#ei>KRC zldie36Qx{pa`&ALl41UGWmL0xMKVu0!H0#Uv3cPMN3R1fsh6vjDB(D#`B9WWvFgbZ!1fuQ(qU#faw zmhQf`vjJi~y7|pEUO{FX(pxu{bvpnf4xme#KjmioX2W+^H8jm?bf>=I+)P#%J-{+O zncv4B`~NZ{a%_-uGoU7B)E8vPU$m6nTidmWwD|~x7>C|H56VS5KTS#a&=>Fqzro22 zp_5r5vbn6kOu0V!eqHseS!xvh!+SUT*ptn)c_D}N$?O`hq*7Wp;Ggqi_GK3_W6-Sr zO-!2|Rf#Tb$DbPCqmL**>NXpGxSNcNa54W#BTyHSuRt1(*nR~wzxXHkN54mcOK-pJVQ?B|gYr@Jw`K zNP6I(e2lM+>Qq}zk0tzs#4jK#;-dG(c4Iy&j;H!MP(21cOt?hl3`4e51nD%Z_bMXc ztIDlfcaZ#v73Z+tt>KstVxX!tWIXJBqy#MeCv(73>Kg3$@#5EOg$yp#_H-ZKF9p2T z#Fi>P))ee+J2d^Tq7v~ut+D3SZRFojY;gxxKSg~eshJqpR9K&kwA{DOpfx0WW9zlK zaxvRCi3V-V6*n{&S8pIWt-}0iu6xfh2zc&QQy(72RQ_Fg@f}MLjwMm4e_*T# z#hqZHHr$-J+ih3}A|T6fyv9moM>^3khbQcQmWwVnZ^9hyN#xG=im}hCB!v#-48-Qo zq4i;6^$1QM`xZ93Ua$tzMg|{lc{9oc1Pd^BFH(fC0*iGS0b-2Z+8TlQ8~z7Am$P#> zd(@r}XqpKKmx7@OdlCBHc>@&DCFQkKD0F zs&2W+68_74rKdTtRbsa@DNU-oe{cS-&)o0q=Y<+6A@|6PzHHYg%Sf339}o93cexGC z`DQ+*e?~}dk#kU^t7{At4o!FgmF~G3#?k|!-78< zI~MI7UUj6!Cy8CL1q)!WvloYB;=NaRW$!KfrY$2+ZrN$sh@etLyKx!~@+gW-4C1QJ zS|x^lq~)`qLS2nWZ?jF>mNouQ7yYZq@$b1C+y0pQtLqX@;x23JsCr7v=zt{LVebM@ayI(wAIvJ# z04mxb4n@jzf&}yC@o8qxWElCO&Q! zoi~A0T#75hFp@usPLYZ>9tCx{%xE6c`J2(<32lH*-a)fpL3o4RMvG)q=R3=$30uG4 z`S{E48hEpD6BFb-Yu(#W6UN0t7ltqqa3>1uH?7^K{@}Z*+jDYrx=x*%Z>8Zvu{b1z zdHcKI8Hs7ZA2f&YiMtak7wZ5wVQp<6+96j#?kF)1A`%oi~S{IvJNSN{X;{%O7=%}k6(LN6!LGjpeU`|vEAyg1kw5a+fwbT|~5P)AYz;y`zGAQ~3E!EB0}!*gI1_&wpMBK^|Bzc&%L zc1IeP?fvc%XWshb6zrRv1;O=yJ$7Ke<=<^G82>l@kaLHJbB9|F1m#hj#eKu_lo^T% zfx@pQ&4pd+JipL>|7^Ne93=cR_KEfq40PMa2(`LX`dlD}7wfzw#^s`Eyh4im<>}q= ztF`RVIqv#`g@oK-W&Rb=uTQ4*TXlgw0F_}Nrh^x>RnE}MnTnTTHPX- zHqTTuGYTevT;MRN5^$VS<6ZbZ@jTKg_7wRL}Uf%awgQ7 zw@*E_ttjN${xrp=3onzo<|}JsbGU_qr#}<^3@Y)S|8u$?=M7ePa+QLQpYG91M{45p za*f-H&s2~q8kp&7X__f``Dx>;1x}BIn%4u(`Q+Glz&ewFEqqlD5WlmppG*q*YN^>E zeqI*$gXz|kI`7tzN5dA&4M{OTgs;f+2JW~)Ea-d$P+PdW-yqP&2QnDDYX;#SYvXuyZG-*SX0ItOYM;AD*M|uO08EcN)0N4^LDrE{X0a+rNJeU?l)x#p!3e_0o9{z`DF+|pCE;KZ*K~R{9W=Kk z{QMl#{+wlQK!p-G;2&%>#FnyNyBAQ;j!^Yx;6RDe8cuvYOpVOj{UmjTfIRbnW$cHM zYpPgC!Z0nPky-x5jlAmg^l!}Iyc*6gq^q!e^SxqRla?l1%!mR8fWHYTx4iRAPi)KE zh`Vpw2-0ThnxsJ8=d?Sqzm{%fXx^Fc#dq`mM9Y${s}fyyAe(bxA`(8jEIuExY+re-`;rsEdkgL1i0?;$O(yfI6%sXM;Q_6Y!WA$ms zSdiV}s5C#t*(`%yJ2HjA%l@0xgKyvC<^H`Vb`uW%;o`|JE?}witctJEY6PTv!P~2( z9I)ZraG)GjMrQ8|n<;Q$=(aD6pIl{yT|VtQ<<_}iy#*~sDhEPBwqH}oRV)vozANe| zzR-8-qAGlQkI!aor@bYOFfH#Fy=ka=&|L`2gPmbGj@|UjxWj*+n)7aV#OO7qUYe0L z-uBr$&3d{(woBenNd3M+=HIB0l++fTIHq?ttFaGyk&q9J)R=A$&D@Y0(C8X1$q-ql zveOr-?~EOnQDKFFhaMX-62qdusGOaDGKSJn0xwLS{?duQOL)(BT+dOA`K3BG`&_Lips6_DV+4e}4`sbcEXJsH32)ux{f;ayg zJ6#l)O+g4|J;As*Z6f^%AdoU(@x)5Xed)k`lM17O;Q7eD82%KE`ili|a1fs#jq*`n3a1Z~xYF5oOu^qDlLRqw z>dOm@$;xZ%osGr#iQ3sj=l!FI9Mcdc7Ems*t7^QD~jEifP>cp z4gkjXhX+L~`h<1))Rk;{3T0mT@NJ~MpSt6^Twh110Ihgv5X7?Jv{;B<<{k^^9qL0r z!}J1VvFY@cdxy%o@Np>4d=~gJ1b&&eUU?u z#5b-=U(QH3VmC4hv`KCKRxFQ?)U$U!94(6kYF)ngAn>y>a`a3_9Kjs4z}_`ep*Ev7 zv^znmulChO$o9u;9|EkHXKQAy;S5slz90=skYG5s6czHZe%AG{&TUs2 zdgQ~Zbhs>q{CI+-WhqoX9TsdSWicmAPq(rI8^~yKUxX=~T$O zS#ijl^A9+VqQ-nvqN<{sK8u3UwpG{H}^lI|8G3RZU2B~ z`y8E!-tK3j{`(C&bh6sySomkO_I=b_>-O2LFJhx^4$nw4p9rNCMN2k`*N(#^`=!lW ziN6fbv3HPPkeOD`?)%%TO7_X=xvOgF>rX9I3Lm|ruXa*>(gk{G1)tQ{|4brt+!s-o zp`33Ht(6!hV|h0?v+~VU-SUXN4ss*8FpM~#S&K`5nAhBN_)QMX9~Z`RA%9H4`9;9= zW+u`W-52d#4s8bZt_6w21bf8{-(CXv9nU=9_hFG67WgpX*?W`#OoUrG-#3D0w7Tgo z!k@1=w%UU+Tb3RPLwy8yH0#jB*4@EK;|&7JfUiKeNW&RR(2fZ#Syr42l@yV-jAyqP z4gN599u86tNN*|ggMAgm^O{^&wIxw!bEkf^$}ONq(g?9wfnU;m9nKKzLZq1WBg@LX z2bPQae{XdwJ6YKu7OFW_(Tt3bz4KWck-${RY{`T9e9DJ?K>OHzu5ReN)4V(GfXa^| zg%i#!zz=(6SeXv{F9;gncoHA$I{fw1fOHI=IrAbjU{dDGEC(J51lRWzy|W_HeSP+w zqa?wZJ#u{I)WDZ9^mqCtyd?IVGvNmRGJxI3iJylChCp_#5I}eh($xrXI$q!_qQub= zxPM<~UQB(SC$iMm)ZXQdXM1f0@(F{WYE@^m(JZ^@SYx{zr{0Hj_scJ?1*a}}TAQ!i zdo-ed%fx*eAYzk#CntM;9sxhtGMH3?_P33FpohP@pwq;ZFv7Nx%R86<-5!b2nP@V9 zmtHx7%RFbA(GQP){F37Y$8~78J@gWMzI7M%tASt*RtvgAra>D6z2oP4nIG(#`^!Fu z2c#R0UU3(+OxQxTL%Rqi8H1kBQs*0h0TWS11(iJA1LhfgHZk;nCSRSZq$&qcX8`R2 zEc#LB<~uCxAw^w8lID@`v&byW(LtqR(D(aDOxZYFSx@BrvVQyrnX|k5o+TN+<8d*i z&bzz=du(vpL^5aJ2hs}`C9p&GCGpl%)x&S}UgOUQ9RDb0UJz16XUE;afmZ@O0Whse zJa8J|kkJrSD1<2%nt=ErLc0G4n89z>{QkCx_xEP5I^O2Pd1rzPqei)jD{m&bE+9{AMJN;(%@8i=18=fj(izAutN>u|mp@DbwB0@ydkUwxr zDUp`Mxb;=|42LBv&(ojj2r(@w3v5O==GzExg8}{1N&9ZQ%bNKPpIQq*Un~E@qtnsu zPa3<=SFd|BS9?iPu~IhAKh@jjzL_idewJWFMn*jL(73G>I+*#>J^y<7Avz%=yrBDT3Xs~H}E4+S_;@B`xsLK|M+fm>ji6~Y3t@*a;0ZnTqCrYntQFPR$8rhFr$ zxvb$u2z<#b)%7?u_mEkm#7F?c`NY@&81L*G+(T;=*xjCbJ-1G*%s+zpbTkARV@-S|TQbGU^kSR;KFh?9f37syn7R=C(K#53 zv&u0Rp4XVv&L7`mYrB**bY*KdI?Yi!g@3DQI!Qn;8sbH6#|7-Hj7{HOeRi`Hcf+@FGR^@&-(F+*h|B9y7 zo*kFZtilrbSF6XQajBuc4RfI>tl`Ve;r2`GPZ)~cLm=!tL`yTi_9z#?5%xtaw9ceA zbo==iOrr~0MMQq07|l+r`b02;n{!UY2jW!6|2;liy@i|V z%p2=#N=`rK44od!x;&@16*D~!&jqI(7=>)b}4#ctnX8wQep9+!~!^4LB9_6nWf)&!A3@(2n{#@}#G1dAa${wq&FJ<^6 zsN@n=2~c`I0dPa%!^>nHyCr@3)#+=C(}rlwB|VXnWvn*cEAbDFdP9PJBmy?Xa^PY4 zoa2^u?f=;UjQyTM*@z?@TL$f;D@*xdWejW>N#Q&knv5rPFSMMpD4I)rG@b?^E zn90@CGOkcwBl|s|VZuFtbuH_lx!Hb3w2EeF@_ohDtN;^^s}B#^`h|tcBZInbv-A*8 zq>aSGDx6(yRjZG)f>rDHssq~>T{$~?uYnMC=B)lZhKuU6%jZw%tzb7NH=LdLmaQSxkFOn&Hy~=hlbT;`Iuu#B%Lm%heFPK@9E&GC3Zo=(orEi7> z6j%Cf$B&7&iEf#c760rrgq+i=lx$r8oY_co@K2kQPbQ*3LpJz!%zs^ATRCDq?kAvg zxC;+G6+Ofb;J*2|=Ihux*j=dSW<6AB0PrieWUbLi#=(DM6n*{>uhLlA*pT(*>j-;G zX!091D{dcwUX*YJWW6hX-`q5M;;quO_0Qve)_#Pzy%QwIU^8-_YfQ9C-_{F#cQzPz zE5%w@Ei$5l_GhYy${nRN5cHX9!cutGIieH^hbDT~sow8pR-#Y@rqeQDpb1pC#zx#iuE7xUPfl|jVobDp|hEX~b5r9!h&1n$<8gch|~h}OJBu9^DfYUbh= zfG{#FqDh%&`HD4+ej*v;BH6MwLe?}0lqb8okZP<{4d=u9mTfmM%vp_Knbpz=W*f>G z3vk0%d9RODc?nHW+VS!ZUn$m}f&vks>mPeIfbA>~;?&B{)sA)J{ijD-W!&KE;z9z$ z)ftIT@9^vCD@yq zU|yTsWzjaN4*jB=dc*vk>WXU0`3tr6*ViU)xQByZ!v9s!sAlt8@*HY651XBWW%q4& ze4sl1Uo|{!^#IsMI5QJrw+lrto(FBuOvrR*$f!iJ0!5M`K`|x1a?Dki+0;ixh3hO> z)?(MeZ(2F*ItJ_G0O^KVXIG-r8Gg+x`FFI=smcnv&-6Ia`AjXyOjX(afR2yTs9uK8 zh7LGE$2o!=Ga=H#FhF?Y)2b59`CNorQu?&Nh}`los1^$4HIKnAjc_7i@lm40$DRv` zVQGQ-WG@O#PFemFOn1=r$r%P(h>;&`zy)J@^v1)bd<1HAkKQ*0TxB%4Lo?@k0-#8+~JPV*1F&QD^#07wWk@w=w>%brCfLYS*wqX4GGDq*iJ_C&@sGb?1Jfaq@x)mi= zbS1?~%7qJIU5)EylgQ$kZ^NHL1tf9G4R5+T`_PM;B&P?L{fkh^CAJ@3OCM@QiX5-H z_Jpwnv@ClT8JJGTu=?lpz7ytF#G|4I7`q1*B1NWz*VQkv8Sh|&nZHwA#6PI!f)`P* zF=?9aV!%=k{H06VK8L4R)h%B0mX|jsV0AH)5CsKP%?m8+6m)d^D5OggUQuIr|(uxnn=> zJ$vF&?Vy62Y?~3gAOu~|lZUD8v&B2H_1fnFg?xd{J)X$^!qmQduS1I#u)1u zlvab`uMEBIv0uWI`V2ct5gYTTW09xMf%W|tN_w3F1<1927S10{xn&Zj0;!;Xev*6m zaYyKBbw9<}t1qlnC9nemoDiS*L`+pTP^4m9BzLRsnO#bi9UXYi%1!W0)=AWPR+b|dcn-nRYc4_6cwXMoOe#AX zu;ewrlc1p5eP4Bih?ZCkYEvam!UoAQO8GZ_BsoGlS^tF$SG0n5Q?`db^$di(eubf_ z31x&fkc^w6G~0_~)emx;_C~-inR^qd^QIRu!M`c`YK;AEXTEzzdo%WZBwL^XuMRa_ zZ@NefUG7{9#an2gg^5gOd`eKFu;3_+7+&tlNxJ=cEA)R}09ww^Y_;4@+DVfRM21%) zJTzQIQTdI&H;-IpG-!F^g`xJ0VYuPtL^{EGtb}V=?qU4su3fg5oXCH*`h7+Xgcm_j zL#0w{Re$Nm4=iS?1($nHA&AVud)Ou+N~fZO1`++_iVrSUYEJON_TaOEpUw6gEb_fv z2=Jb9?xD%UlUkq3gR8`=rNW7;fAna_eIfpF&9`AimFD;5RQweCt|rt|LhoI5fP5H;Xy}i zfzTte>#v4Q?6r3>#N#FDlP%pyIpN1y@asuUAqUc|dPyeHeAmhD@qEoX@a<9x`?YJ? zakO2)t(=wKFE(k<(%NHimSGsh`r?BhE$M3M)9 z*9l&uBBLTO!E;Z6VDDu*P*&8*h|cok`TtGbFA7I9p|0Bh+0jjzMx)F9Br!r@Vftf% z>Swztx@8E!qIDbfoaTh8x|X)RhBud)7Cp(_h%@RhFJRd8Z>V?+ zhj#yONO$eU!z-VuFC9E>Xq9l+42$cbO;IFGNyxy@Z zTy(~lI;L{zvjNx3Mxo&o<6>qYRMP~~{Vu$b;E-AK>P++O0exgLzB#n(`qsBEpe?4Z z$NVXVr35@?jQ64g9vhGDb_-OHKn$}CMZ>BKUC0j#8kqMm4J>(zywJ!J<5w9aN_{?i zHb68B6wf0fvp!h+p1L)CeJH+|?Ekw+2kAaK!5oCla39ekhMCCug~@V6fy%g$sHyRs z41&WVp#sz`F$!QC4~M!Xud3^~XCYv{Z#gEFZciIFD-86D^6lP! z4o!F>s9VJWtg~Ghj`j|Af6?=S9l|@Lt#8-8tKu-pj=kRhSHh@ElU%CE>s>vm{Gqx2 z*dLyMOm$7cVYL_%xwI0CES-I7mprh8EHpDJow1v4v#7}3+Hb9yLCo0H?@zXV6we?? z6-pBNa|uNeD+0$^Hb>`F+}coBijoIF8eS9$p(+(%@`7`Zeuv4wjXtz<3OY(^mOm}j zs`;#)_|7RjS3B{NB8f6q&V*P*SwL54ot5e_FQDOk&vKX7DJa^@kj*? zgzEF()o|g!sqcsazI)w(2gt+QStN#{y})7mloRthng|rH=WQE1)y%oKaon4M_|xZD z9|_9Ahm5SiDfEQHQXy3BVSiR7uv`_MIi1DEgroF*T&#!|mEL)q(II><7i;pTz8iSy z$FXNyq38G$=Z!eyUyxj(y&v;3L=xTNYvG!akju4j+0!oj= zolowZ*er?-_Fu_eK9f%!FW(9Ed~jegPP*Cu8Y2*VnqW=BS<-{f<7Sz-9LcU!g~egQ z?|96wrr+oa%KU;!LO$Ls;dugy!3*|vLV8PCDe2P)$oQr7q0!A837s0)wCJtdb`Ps8 zd!B8b&NNSe9eCV@h-lBl8ggJmyTW4pA=Rlms<6%ZkXvX-=Uy)_=C3iZ>ge`=`xUbh zECPaXZ{-wu-QaD3AL~l8L@7H|zCrE41jN8yHpytj)ch?@X)Vw%$~0W4xL-b{X&5Qk zo**t*Bah=Sfn)7j^qP|LHA9-pg(vdPp`c}kO_ACB>-zbJiKxgwyE(L?^DvMzB#1SP zi6KXp2jM+pJZ@8H-v^oS_j7@t%57KL;-jL_&-TP7QcDuBCMYe*#a9Z^AL!JWCaQb3 zEZS~d&=U#%{Q%V;LbMQ6+B)$HTS0!8?RCF%%%Dy}6U#!oaeE#Elkl@j04Og5>8rtU zM(BR-zSw|5mzwck;h*V^VgnY{rl7NztCG@kKNF`b{?dA9h8eLaBv36@#8*40oZ2`6?b$oZZQGMnPS||lRm2#o z{QCQhNQ%n?Py7?fG4Omra`Td?jMic5j|Zt~SwAglBK1ktzpl>}RjS#1dcO)#HI1rC z?l5;^@-*8>?%x)s(BkkjNnO`jTyUk*$6Zcz8{~_b?}5`hT2x|M1{|9ya6=_P!9kuQ z94TzSE4oDJ zDsw8>=&7kyyUqF0fdZ34VSpOst-fp@(+&;^O#b<42px^-GnmvZi8Yx(Y5$P^NGGF_ zjvE>!vPGb~Snpr0>TR=-+mglJYr*{*YmtC+8w z8zml56J_`|>bF|Toy44dePk5wz_C18S(j)*N}HCK1J8;mHnb32KjFWlRqic4vi&GA zn#*Y)`;Xz+NMVcdj7Z)>6^(~)JV={SstM@`8(EeS%!-hW*GB;47Q1E)x@HeSy!TPx zDN*jGt7ijFy}QlsBhUT8*k@NOk*IgK{IOCGq^k_x%Sr@s2yrTR(z&sQ1i$HXx@Roq zJ-_3|)K@)c0#uux8HS;ug21v|DBl+bZkj9RNRnnDx<>QjU#G|>Y^2PZzFYK3OwmG)L zv{r26#9yAI2w0N*F(HO^xJhPm;##%aJ~sP@u=;7Qxe=6CsN+VA5BL zSvszFgZAe>rTKn&vVWUUA7&k_SnCbW2wuo2Hkewf-P=TLhjFwH>xb9kg_kHTsPq`a zYm~hR`clbk#ESk!Hf@HmU#Y|XtoXpnJDw%s`#JEihv|)H0W1%&Fw}Dz_QE`QBUyXs z3#%(EF6aGEK5V(z4gX}pC5`*1q|1mqdX?nTbPny~7st4BOVdRA_U_K8_YGvAf9O*% z4@%v){VY@#zY-9Eet|B}&kvQVs}s4);B@x~2QdybRd?#HU9se4^21uu;|Aa{JTJ;2LT! zC*{&ywo2pflFsKiJCK;2KH}u0iH^2<*mc)^(4|v=zQ3U0{5x_~FFhmJO9_xf z0^QIm=ywg?Z*>cSd3{6>Gfwb3k^p{ko1>o^M=RJ0A*ws02C!H?8>>PSUwq%nfq#QU zu|6(h&xPaOwVb5`n5wN8R%e0usAPQn9;YHW#3exlh4dN@j$Q+AxxdS*c&uWY8cNE$ z8LkFl3zzcR1r<}8-~2K;y0i^E#@Txc+xE*j`Fy}0$e3${ zq1Dt0*m2b8Hah=UzJy~Xpfbjst-1FZbZ0s;eXlNUQ&vN%1XGGL0932tCI1orz3BCV zql&$+LI72FTJ@32@FRJ=l4e>zd^MN1ZIsSsu9Ued0-W6ey=%Mx{1;W+KG8diINWL? z;;$a;^eh)26j!#$Cqh#V*D?P5B9OONgEd9(A5s(aGK*H)!A_@OrZLMfQhuy!LetKG z#y*8NU=zXT)@zvaf9N7PMkyBuzh31P0)+p0qCDGDDtIjQn<$N__Gg#?R!JPrTS`iW z7FoWA|Gd8SOR4NNg+F4ZdqxwtTklTlRQAh$<51`8N?zNo=bf|1imr!|Dp z_f_*ouWm=Z7%sX-R4xk<7xQ-a8XoyQxlEA50<{kUiJrEdparjP#9EW^?<1}oxKRtI zx`6ziqL(jge1lQcO+RBOaJ0>OJ}OH9)dGeL)cwvb(Eau7PJtmxqp+-~b9K zuw3)sh9cg*9$iJny($3OYc{i-=?ucB$@zd@nS}71-Jq&V(^j+(?ZHn(L~98FrD5|` zXl5KY2iqTsfCbZ8hXE^eq2g0^%At#bft*uIV++JpkT;;+v36rSW^Lw&>x7_`y7h_6 z9s-B`!kXz?zscV8-oTN{Bk{OJwZO?Vmz7HCdSgSp?&YLRNa6DI)1)O&&=38f*GcG&R|GzxYabHaAIh zJo9r+Tk;2ERTZg3zPpH;SPmmjRkCMYJg>h*6I%o)Dj z1XptMsluU!ZOxl7)rbF;54#bBk#>FWGu@2*^5kzsNXvhQTN7auH|6!re;aeQEufvS z{yKi`c z6Mt9|`kopJ2DrAT4>xXMv~K~QcrNS7=_S3_Qw;so;9K8x!pdllEN48g1zM$y!gPwgaCKe&?hUo_+Jdf~I4~5*DxD4Nd5xctlP)E4oiDMXV$4l)U zNShjX_@ozvP%jA}NL){dd5hb?2o}W!iQ+hi*i1G)kF38rJ4`k}{moaIxgeeAHnbgi zS#*(VJmT$l)g_ughx%RWD;rLBid<1sg@b~7afhhB7GGUp4JGaF*u6$xy4`oXC#sIs zr@CJ*iXZFvA!3mAYQFw?B3)wHQjw5UU1%7KHsY>+UtuM|EDLMhrOB2^%X=e*^Ei}Z zcW<@&Ia*X$Dp#tN)o73U(Fy9b6Na>9(90Wjr{43U4BtWe?ckjr(Ta=0`csb8HSpDe zpXi&9K^t@%)gLKg5mm5ifa>Jm7LPH(nqiWX+TOKK%CDEc>1;2pjj+JgMNgO<&<%Eh zA@MeZ!bV>$Q)R_B5f*f|-Sqq6yuq@~!ohpV#o|Bd?kkBG3md3IX_^cV;FIH>v^~K` z{!Kem<ca}^l0BgNmX@grza7l0G;JCP)f8xVEY=`i6ZllLb|?YsioO=MCnIHx{Pmx zK6|(!jRwPv20r$yNhc(q(E(1UG?UvNZ%f}gZc9sBSs_INMM-XRm+-L3KLc@%{LBW5 z*?{uZrqQSOUq?)Ve;wpI=`I==`(96pFW}zEuYOP{yBEcdaROqltDC39?+S(7D$hRk ziMn0%`~2(Szi~f6JPP2$0P&p$coWj`fC)TF2lx~O`g5y@Y`#gdOXR&Kl8^nVkSbh6 zcP7%ffVpX4`}~ziYAcIsb9IVO?k)TI7?TvEtY%Lw<&6;L7->Hb3yYEcV?#Xx1Qppy z?@%@Wl&Osi8IPqsrv}UDlp>>9b-){+5_f96t&+fu#_LnC6ipCu*hw0yI#XK@w`ntQtU}#u&W~->ZeZ|33chXT zMKAjS>>3CkhEm2P!75M9*x32uRGY?-wbz2OfIG7QM(zPm>QC!u+XRUJ{TC7oeGTbs zXVq15X-OGOWs(TQPw%r7<9^+AATo|?kqwvju?Z$Gj@f1B?n|gOmiW;-d5PtoPV;;{ zudJ=#bt>^>O?zPGj1ZDQC=M?K9lvdgE<50n9`bkRUa)r%nJbo9K#3eA$e~cxLB5a!;JCiaoOzZhbn1)L4C8Iz zD4KYv;oa+a@}T{=>pR&HA=t6wCpm$)SBD0LlSrbY5HoufV^z7Uv)0 z;Xg@%-$j51!VxLtx>5+mIN=kn+Ycph;+g4ij^6Zhmk9gjn;j@125+Zgk++a-N~YQI zHSeRC2Y(T`2I-6vI)zBlr2l6X^@w41!cX8UIM_FB&JIZJfO+Nh^Mp%(F&H_BW;Cmi zbVmhY&5_a{%51@fT4jKr8)FfQryGeAl&(8l`y)ky@_Ykj!E9i;A~88>8Ta!q@6op4 z_*VxTTm8r}f)M6E+O(}_Udpd#dzrv&aLzMd`JRN@*_&k+GD@AWjNKjqT}OkBw~7v5 zt|CDD6@N7wguYdLxDb=i&Fo9>J<4q%DA}jxX4b!}7%ufuWf)>Fecwo?>DpK$$f-Km z_;#0iP6g&Cj8yVfIC{5@ z!OrKFF^k8jR5X`D;m>pqA5L+Dw{JQxeS*OE@_+iEPwl^l_D#7ySmjt+?E5<=3&)fP z7oN>A9}HjBjin)$B^)&&{zg4?MsuG~_FF%mu1b9nJpoMhwq)gx&-QQbis2+9M83;< z`GOrJN}f|g&FA6qvPPjgVKxW}%v$ipSQB?HioE{;iaMtklp=!eiJIY}My&$`+$n_q z7W7FzPr2^xmrH0RARe_F=+=LXw(!FuY_PIyB-7-jq`ZqS!2`3U53q%c<01CG9fW;h3~+gCXNN6g6JPHgS5 zMM2k`gd@*QWkN}#cyYsf7rWwPTQrEE5RNTg`V(u*^(kUgyayvMO_;pp-`cp(e9o^vQ+ zJ^C41R6Xk5K(XS2%D*%BVE%=^MOtR7ng#X3F(yg-kke#RDegBS4L{GwM}M0OEYe?9 zSZbDC@l>8D-Zy3eQ2meLwmxHaWmLqzr_y5KaVW@#lQLb{xuyNl+6Qhl`=Kh=gXWr% z7MRiPgAw0XblNzG;ulMi!&l*|)c+dKp=hg3TFm?v8 z^!;*8Xw1Y=Z>E3SJ}7}WFcqBd30Uj?d-q9E;Gjr2+1DkF_#+LfH`B*`%FkTu;T(ZJ zZu5p2<HPFeaX+^ z?l?Uf!lt(}g*c(uE_PgfhuiJ{0qZMQF(Z{UAIw5x{#cPC#x9%Rp8rTYmSoNiL;0< zxXV}nH{Qk;umAix{VRtd@Y=bz<#}V4JegQQr(?!szSh7sFl$coFn*0@+C58z3j)Tx zm~nLqWKZN=GdN0o^9Iw+tuvG@B?0vYY*ufVE;ndr88ntJ5JGvezIDXapgz77a%mtV z_AV)l&~-W$N6EdI9+0~v!h$_ElN(;olQh>y`etPU@`0URA7_Tf7Y+x4Gtx1AlG23d z1W4pLHDWaj^B8At0$Dd0CyA&8yNDUVCHKlLfJdP|Stan;)sh848ldwL438Mw?jxV> z!4E>%;9%~hyRcsd{f0OaRUDe0sI!4PZ2wT zW=Pd;rDSMP#>nQE@oT8uE^}8AkCdVn=s|)O_rj9~(gfDwOJC}H;)%{yQhm;!3MCYD zK4}>`VMH2budpH^>$F@khS=YRGwST717#F)#o`U#iqzi9JLLPW`pRp^Di?Uuz7 z;j?1{lZ2b6fYyBa$J}*+pQjtXYmUDJwvh|H6Al!~o~I$|%_=;uuzBrV7#iq2*tZ|# zJ|EI~z_V=IMgV6S=zmW@qQoC>dr~FL z5pul8SfX`dd_qwMWtCn*@v$V1h$K#c2;U$dn;tu_N#w7b!I!+7+xTq0u-N(fm!o~d z7u5W3ahdbAmsy%Y+g?Z@iEQ+l+zM_Udu{}BI?H26oz%$RbeTm?P2?qUpG#hAV115~KQlaUq!IcV+q1zjd9-r5a@lXA{JKu%! zi8nY@fitW?HVi<}{q~-7K5irRCv2MNaMc@CDObCw(t?uP6c6_z_21~|CeBQfW8(-M z8H6BCJg)}6!7I>e1l{1Gi&6;FLcMwJ4*v^CUvLp0JVmW(szmH_-p+y-;PQ1N5w#vi z`82RrgdJa=*30$Z6=?lKs8&w~l3@pMm))`Un;Q;y)VQ7^eD5g5_ z)vS?#z3mtM`PK9P))~ZtNn*B7Erb_Q9By<~B=C}5qNq&2hvrwj*sweG{J@?Y^JoyF z*{<~)eZd(N(U)%&%R~w!Et$-jEb6x3Zj?hXq7$k}e=B@zwI!JOjBXb>uRIJm1oq*% zTEG3AGOB$LNBmp#4}RZ3=jMm$cA@AfOjKL>xnQlQ4Pa`G79A*F{m1;HsY5TV{a}dQ zcXdP&c6k}H!coV{&spxVh`O)aEz|xo#9p`iSeHhAb-R~ofHUDLpu7^Ghv_3uA|l{a z3GfXKwvZEoWcD;9X^;x&_)mwR|CCPdi)F6_9=svw35H=#EPl7s7#?Uv%uV+p=;Hvw z7yOh>iem5zRpXefTNMe9lWG2u2O>^_<5;nvdRDyd$PqxA}M-P{G*? zQ!3h}&1%ctv>;=kD$Cs%r5#Ru-wQn}L4~Pr1`;lNm~f&@TyCqF!p=^5v;O|1f}EMD6aTl% z;%p&qyt6AP+4U~i?3O@}(ZM5rTuK&(AiPgB+VX#107^b8mtWXdzmryDc0>&r=J=CI zJ%iG@g(vM(GRMdFt4q%nt$Wi^8a`RG50;5z05*0d$`KbQbB?TB6n0HgMXUBIG?8Iz z?)B`ex+H)EbRE9wB%Cw3Y|_tQQt3Tjfo;H(C|;!+wJVx!T^RB=2ODGd<~ew9h1qN$ z?ciXZB8IT}i?$V}B@QapBEn8LC&;MH=z%5`pmi_S!F2H{9ZttKdwl5)^)-dY3Y~_{ zH|!wWi5dUz1^7ZPs0_xx^8>qXz&Ly~$3&eT`5F5#H*w4r5a2Tdb|B#0^>Q`n!`}Qq z8awWtAj}-E)Ur1i1~Z1ANbfCpNkXueduve3l7GujJGndgzZ&6DLh;H;4GJn6nK|s9u`t@J&@TDH9JW{FNaS2(N1SVK_wF5lL;<+ zErean)k<8p`3Bd;x6KY#nXa^#)(w5PUu0QnMK|q_|G>=u45yZ;gAS>Ikn1o5&@-G7 z@G%cIIZrijodAR5OHl1#zm5ycp7rvegpd%c-E9r|^S(^o@Se8)|2+c*dv5H?u;7cA zawbI6q~^#ucjPZfMn?C_9VvfQ zXNK(Q&~)a$Xln-N$?KEf_ebN_YY(o9I}5yTM<5diVUtX#)JUVc7peB zqyCVxJ-x=&rx$O8iCU7wS*k|Hd96-8lZ~z%(%x%pd^$W zmVr<%zp?KfOu)A#@A7R)eantCU&HLrPnF^4m8QqH;KizYl*(&G9MQs-&wac|r^x3Z zcOccB@{**k??2_|K3$+%CQ7b_J5+D#Nu5RM2il^DHL&M8?`tN!&#SV9+5YzjPiT!QwhvR@n|DC*1_4?b5e48uDDRLt`t7+axg34g<1Wjm^`I9$=+6r3`oe4v~U z&dUBP)L`rSr)IV_4twy_#7&}db`NU}D3J+-(}2E-N^4SanOg3BK|`E{DPw4_m?tgO zCS;gLCieWKp(a4TDNiM;_~PQ0RzB#US`&_3A2t2Y=O1l;Fe(x>qQZWY(KQ{xxYZ z1xJHH-}F`F;U3bLF)P}))i$YLedjwV7vVN*<;B1NBgt~@vXYzZo$(<{fxPSNA(vg- z__d6yzqe+&8|^g#~q-Ougg1ldH&@ra_gM|IzG?a}nJ zEMEufkzO>&uQkxPtpSbfJ$}`*o_5~JBMi%@ix=TIF;x(!Tr9>Qx&34z{Q0r!YxhEd zsDsbAwnmi{#?mxDYC#{v*X0b|9-&I+5azpXv5mCqIS)iI!q(?j%UiKU7ez2;h8W5VXlvK zZK37^ojZY@yM%l-`$BbYp*c>rp&OyKfaC9~e$B3hHxmo)#SDaUh>X37_I4|A_Ce<4 zZDJnuFk(M`{tRzhuH6_x5oEm&4NIia8n(H^SNyGOAptk^0HcTcFH$1j7?4$dSb9BQ z9}v#`)27|ZA9u~l_fG6h*R&M$%LBn}{B(lBkGCPXosU$|CCQg*_n|pTE%zg&Oq;|y zNk8=7`(hZ{`AOLDzkjv`MEZq-15=CB$epQGZ!kr;^=Br2x*%jn|3v?Y%ba~!TDKD3 z+wW+{zH`AQN*Sa+#Ki5;WBMnegALn8dd1~7sHt(Q=)IK&Ee1&b-Fd;0@Vn_HiO9*A zy}R`%TPHQTk)pq{JE|9dtjMANB2p8P)G)oRwX4;?q|@&8r2lg;wk2rFvJ0z8Y!`Vx z5e+#-#YeWXBH&X6%wn8aP1+=k{XFpKUAS7h;>Fa;E)IQh9B2+%7D@BkfFY7G#`29g zWs#lkE%M-Q&kYx?MQU0*oL6Pk0H!W*Hy+FO6wvZ5b%>px#xR(5zL(qE$b%jra9;5~s$-Fb_bLRrIS!GLOr?^?Nx8GIG zhp!F2N6KP_kWQQLyF*jez$4>-eq>@`{%*i46qQIq?6*1ossu=MiX$CQF2#z^8IR8I zpcrqPtVf^u7M&n%wL+j(mRB>So!mmbXe}V6Vi`5nI_GY)5V}OOH;>pOBF{yKWgmb3 z;JZgi1Tp~EFmG5&S5j(s9b7T;iT5Znh#N}{+AWba4?K$%%@g3(rFEt}s3Toz5CG1- z3H^GYt~(g(5z{w4a*GNiVcA`3b^mqC=g`` z%>pk7#WHOx0I>kOngrmqVIq)YpG8WX&28y`qW)DcOq_eMC>LE;Almq-0)omgO{`!i zdPsRdhL4ydvj)=Tj-6{<1iR&FBWG|{3uPIa>S>pVIos5q@S6*4GH}vw7za>A_KWPP zmfdwSht$5UQw8v7JZq8`M#|wL@o&N zYpG)PMppejN=JlTuekDQzy-EE*$52uDs#sF!bHXg|Vlvy+*#iH`R_q$f0v<@)_Pq~ zy?GdOt?9s*=cX%J|NPUeEh%b01;-P7Np`dkaa{f>koP}ODMsG^M@hu zEgo<4IajZSM4j4t-7l|IGSSRSY>sBee!LY_$Ih8|+{}m9PtAH;>Ay8!jYhc6sV*Gli9oHA99AAc;nx8QVM!-BIJB7&ZagTz0i)CJrb{0Ch!p{#2Ys1 z{dHY>E*^ov9HvJI6dc`_1D&jpTF2nb0a$umv6r=ed!p$!S=5SsC0Q&*4A{S>63fczq7=H}42f%{sv z(13O0;#|qf_(DH(YPuXFlCeEnQ2W`T(+&9};0Sso21EG1B43MKk`!Rb#~Oet_XC}s z`4x-Vl<0)_jdv`18Mc)c!9D(71CJb^hj#po3oB>rOIvc$CuzJ3-4*hL)!74uq?tbX z*^jye0(&OLL(k+g z_D1^z>hL6m+-3h^7Y2m=EwbA~*u&<-6xhZ*tHk#lh}ailiKs1C7L%bEFw&3fMud|` z$|fsKC3PWUC*yl&`yExlOW^W5uE~+?xzN70_tfW^G$={D8fpN6{7zPcD&Them`I}S zLYf))=m%UL2PEv{7xeib^#_qb9{Ypv>3CIr;q)KD(K=>O8h!jImg*>OOy1?ht)aK| z`qK@zA^>JMk{WEl0c>Oe5@oE`z~h`OE>t-xMk`K?ef2$!YB+C*yZQNNVl*mfkrLp45oz<0|9*-Bw2aBoL z_XAc(4N8vZfmN|%vU+&d%F~NZq9@D!{8l=>zn1Sy))5O**2^5Uc1A?naSRwl z#tlC@Ai=$`PrAv0_o9M-FY`}ovtzC$-S+QSI|N6wSpJ>Bv^`%{aMdYbRp&3nVsT#2 z3-jLpFQksz!5gu=%O8+9MxT0zbm!1j?m<`j)<=QMlaM~H(MBgGZp>S7LwZWDdT#i{ ze9Dx`=iPy;K-g6sueY%sq%M!1bRo&6e|YEzz}zyISCAy}BE5>0-IQCI(p{VQ?VUYX z1h^FnRu+(bi`p&CtGP3VKu4d}#0N0i=??MzxcYNz-TY>hkxuG_1SQ7Y>1R|LO4b!Q zW@vo}fcJ9hCgzU3(l`JIn*>Cz;QP_xg(z^jw}=@yPcp*K-z86(NC>oK06+ZW49fT4 z(!EGue`d=lLr@(R7D8?p-uQ1g3|}aw(NFjeq}M>g4!j~&;k$vBV_?F81zMLJDEbL- z+ZMCw+yWx_HxwQwLjD5(Eo5(l*!2v6qMmH_{Zb+IO{M52?6y^>)(u{s01eawBuQLR z)ShY=J;p|*BV1|5EX_Pb-XM%Uu0l*|O?dF5Pn?lOmeX7NN?z0s``cnkC%60RU#i4H zS})DsZCbhVmrbG&or8?>>^iSB^1i~TbdB{MR|46LQ1GPVo_WUQnCsV;&MWr_l#sO) zehI2yMip!uTsSFg-F;Xbs;I=tG*C6bL5&-me&2)M-=AX`y01%|`bhfkvo1^t1d`5WruEOTdfF{e3H*dFs7#mnsG>^j7O^2K)<$Xb(xNV`4{r-KwQ!V}AK3tXBaWka_7HqKD(fJdcfEmBZ z?>oR{MI>Xm#CR^8NnduUxz%{}Ha*o`&70)Ro-0ot$enaRdX9`x?DBd~@Bq)^U>@rK9)5Hwy^gdVdw+JFk0WiTaV$sezzY%|;m76exljmIfoJIWhk{KTIbe+f=fVHAzDhaxhQJk@Brr%L5$;^{%I@%^7S`(P&usO5c_>3<-)1CwNPp*1F9;=V^Kxz z6~d76Pf{QHzsFahjI~ozK0YAD@UEe)HDkz>Lc9ahEGed?xwvFv>h z^UA}^tX0S+uB$<(dVUTlU69%Wemt!Zat$|qm(7k^&kz#fi#E_!Pnf{e%!$2;>JN() z%qaVn^?KbDgyMC0KXSWZ{`An_2rv0hgy4Gm8Q2QED>T_Tzv}#NR)j(Ap)M|3<-qsR zpYl9Sl>Y5EBE>iiU#m1yUU6j?Z+@LyDNhXYT5Z4vC~-7$cBlhcUXTX6JZ!6ReolP zb8-!H8T0ArtPXkgz@RqPgX@lz-&21=!+8O%mck*rQd*!4y3KGKNbH99am~&bFG&{Q zkp>)2`^Lq@2@YkF@Vfob?%x{mMFYfs%PO_<;Dr)=uuU_SfWU0fakH8NBnu-3BO?5A zW8jRU<&);Zpw&Q4Dy&70A2~KvTom_&!ntiNxa zqus~<;1-zPX@e0w_12IVpEf4IFVZY*ad}vFjU>64GOa`;#;}o%E#LJ1Y`WM8{WQ_q z9Q36H&tdf*`QRtDRO?rnwB_`X7!cgVhZrrW1uLM)vdCFb+e;Yu0qUlf8yobdkr~=^ zlg2pU2ohNmvJG3YJ0|#6W(w0yHAqf-iEM9^MwEn}k@6ynqI%B6cAV7Z!{f0uDIUaI zeSvDKYIBxe_>69kx+<{o=b)@|q%OFW{Xad)q|%fdXO5@m{jE>mfh|;D5Te#t7oECA z;V-a<-54@DbfNfUE<6$bh#4t>w|`PH=Xm*fE_ak4@W+1lw>fTO#@o>t`gI+P_MP5c zjh+Mr>>1Yg%fGZx{~DQ(z{lrRC%6cOr|AhG_h&L}B1=!r_mrI@H7MB8nRWF*p2N3- zdo(1G`*eskq2DtcuJuUCRVJ4V`GUxMO#W0-m2g=Pp)|`Q1`>P>wZ@R-H;*G7>v>(i zHg*L5_#kDST*3-~3sYrXq%K!y@&}#$;UdJ2pn51Gd}xY2IS6m^*WbX;J#g#7r`_|m z>*2Y95$EV)s>1U#& zK_b4EpiG4Ch_n&rj>gs`RVUpd$?5SceX;KXs_Iti4F9Zxie|a( z^s$+6$I!=E1Mh--h`qhx(qw%2!G|uM!3srQSIh;eFP7&PfT*&?nXhW1W4xv>3ly+# zu@o?%<1EOrDFU*CyZ(Q?-DR|mh5%*8x#C)tHv#z zCQ2Yzce8yTK$zWs*}0`5-_SPp<4(=|_|D%>SDW8=#S(({2w@e^8Fu5K4mMm=*|M40 zz=Ht1_mUQi4a4a9C|ixdbRW7Q#jHLGdqU;D@9PxKL%G$!b|LYW3*5++6u|TIp(1!) zcoVK7J%nRw#V%Rk>SRb9N~DeJERx_Tsi1Gq_(N3o9(l+u%In5Qu$3xLYSDQtQ1|XdFebX62 zX+Fm8b1~06wheFK6=ECOSZ9xV& z^F{6O{`ETAA#TWC02ZS8e*|V~R%WJ_&F_-?Mnx}kDRz|#F;%u2_`xsHCG_}D?9azC zS0)BBxy9;~{$IM~J91}QczQnFg#dW#BJNt{?4hu8Gn1 z_i7@?xPU&@=@Drgj>ubHdQ^;%X)|%0?lXfp8rfsOVF8!COL%ZsY%DDn?!Xkck&ena zg5|D8()GUaE1$er;vM zYlg05Y?E%mjaN#sfLj^SH)e|9YZY<43!E}In|SVp92$OH2ApLB{bdKLdfacol+g;h zez*(M=AwOVmCdf59QtZ5-Fqyr_irqJ5mk&$8meCLoan}yTYq?*FDJ-wLj}(QU^AoI6cN zGOT)3ttp@<5Q4(jFP#bL_s#&uXC=4igP{|3Ytone2zLW|s6 zcIUKOz~9(Eq%?Z?z4a~?t00CP6Zap*{5^x&R zNx55Sdeo-+EUMy&=&ED-;sYuB7agg#UNaMlDzTAvYi{oPp7U+*UD9{0TE86zB2jgG zD~f-W3J2(}T5-1?ht!2*(~HI9nXzBo+i3Z50-pKammjltrNBo4V3dXYF6Z{^Ki+4L z44R;v%c*MAS$z0BsO%aG{s>U|qqgOCpT`k1)iVf`YC(WwiqWEZr{WQ2H$JFu)rM=^ z9i=SVTC6h5c%6x$mN{~hnwuV)f5yTQD=TaCA(q7OE%c8?x~IXJcP5^^4?UVN8&>qM zW=iEm$N)3CTZCYwCSB&Te)Y#W!EBH$DhbDND!21uc0+ot4a6qnQ0;dc$Y`e~mR%r_ z&Lo)ld*De!lthyKgfU);gZtLzQpQd zX%8+CyMD}2PrK(?A1VEy0f)J&5pVhhd4?)P??7xHgSyz^*4O?gQ+s>s+viD9xW67E z@@go2D~SgsN8fSS@Pz$$+duE%H&Osyd}&oaqVo}aZ_&JI>^X$c2#vfCi0mjTQwnyU zb20XF-L9+*jJeAM-DpN)X-eey9o&P9(TIR&n?rQb)7kO3vPJSm925#7k| zk)l*|d+HEF=2jX6cu1*J-cfY_*9ZMMxCevEtn73V z@{Z$xqwxKq_8>Sqd<^DD)rh;AP3P5vuxyL18?(oP(+G~TN5ybr*~ns$B6i3%ACcXA zYH7DFP|J^wvtE0qgoq8SduB7j&EEdi2@vbo8Df{JI0LZm;3@B@P0t}mkZ_nu!~>1g7Ka=G%&v>T$ZXg z20?G{viLG>!v|N?C&k2z(12yspZqzJuY|Php49fSHA^x=tJ9WJV4}4xvW=0kQ^QKNfbr?hxfUAZMj%Wj)A0b^a*bpLiVD&AWaF$c9 z2L6HQeRjcP^5z6KlU3TV(YNjx6X zOi|GR(_^C=v4o1GYxt#}QoWae4;-~^sW)cG4eo!?V!E=6ZD9kTfa3cu((PAe@U0%|u8c^z&p5F>_SmffL=6 zI5fA9gyz`BwTyH#PcfSXb??Jpr+VrwyCw{}mVf!BiRxF`T;nDpO6{AvS!(tAW`FS` zj_2f)2tK;0VFn_0jk4|hUoAkt$Y-Co-uHh)euEy1W&>bfyQJgKX3v$pM(TnzbzjH- zZJ4W0z>;<#KXs6dZ*K!;zQC$U7VXM%C60>iZ-E737Azv_Bd*YhOPvAze>{)KhX*>@ z5fyC`pS$gBxH*f0)>kBZ7D0^xFMRoR9xdVAMrFYe9g<&@SWn-=C!v1>bV4eTTNTi1=S_6L-dZ4v1hp+SlcxjkhkuaS6hO=a;?JN`)G6M)Jkvh% zdy|_(%w3sQU9!44h{WZ#-_}nH5PT>XM5a&T z?w-==r>9r)8Lxm}D%}o$Q(wo&LB5|y!bjbGz8D*03HlXjExNsIjUmZ9xmsj7RIK}J zhi#<|>1^gT(SB)Q(%FoY$@6JgaP72nuJkss4grcQ9TpfeaneZ$ehPUjA!f?zoT%A+ z%<8~Tn<%-uY~f~fq~t@c7$paaEUhi_Wc}CQ2aW1CH+9`_(r0t|o>J+jQ^LXB7n}LY z=6`Ei%40z&9oCe(L8HL5%QgADdO0VG;Gv-P{)iE+0_hA~0g3?y^Uuu6MAX8|{X1bOej0K% z(@*qYYHbJpwzQ@@$6;ahy|Fl7((GbO!T-Sp`f}s19j57PuQVFYXBWc8M%CUvo!*j~ zEP-4iY1i)QG2Ssss+P)!u4bzFjmH!8b8T*Zp=CxcwVsqka&p0vch*}{6TyK6$fyYd z{QZwt7}-ntd}$9OFb%llXKzcx2Q9)yX5|tT9Ka0P;9rF_9i>M2`QZUD)?H|zQr(rocao=RsQ_N)jewAxA2>aK;STvMOI%s};2KUaF z&Y1LS6QG$YLRs_jWCcu98AiQvx$(clMUfU9K2@VxFpW(z)%mSRJZMloR9&x@N7jV3 z5r=x{d5vyaN-*Hxj!4r{`ZKI$b+t2Vk9@wNi!{YAFs0`Frb6#~$5UjxPlZsrJ(%r# z_3zluaI+ZAwk^_`4?k@{Sr(dxAB!*};)$DiMhOoW5Rzkk{|>)n83+6b2DCN|cL*n5 zv}+ml{Rn^VX-4`d3PK1vM_i{Zw)5gEB@`hXP2%qw`u!e_2{kvUg5G_)%H!D|)9f)x zNP|-BiRKVN`Ab<|o;F%y|MYjV&FM?<3mSZ4+$BbR5xMbVdJ%UJsGb5i)~p21TL4`; za$KhICvMNzZzwv0U~EAxG4buYs$w|f1M~7LeMKly&ni=n?LcZB$lB597Fn4 z-P`*(7Ay$#or)5m2t#6>9!tfaGB@j;*gAPR)F=7lltu7`tQ)iQYN!_}p*OZp=4)n9 zXOp`a&!yUVZWZD?AR;wV?oOYy_s*Pjp@30Fw+-@3ENzE4k`ofwYBSCJyo{OQNVGt9 zL7l=z7hZu;Y2ODKi7n&p$WUmG<+-D~Bmmx#B@J~( zc;5vgHa_2R&PGMCRwX!JR!>)oI4ga*xJ0Z*dmBlgZ|J zMnd#GMbUo0F^1CMgebNGW5d0a)`pq@88xL~OyMrL=m@lsA@DalC4@A8&rY88CMse)nDB+vjN(ch;N9aBZ_T~0&yugv_HoxCz6wX6NWLLT`(1888iBNs_TOtAk=IfMdR|py}=yV|DLdm}g$VyeMPiK$L&}`bjaZVDD zK|_T2`t`HJ^RG1aO92_jbwNBkzjhj+W6wq^Wf`weM-bTBF0_W)Ms5U5x%*#JN3GJ9 z8+*QU?JJXg@);+}skcMO1fveq$i7!)jTN#r!FnF%#V3cDrbjiT7oUPd!Ph%bK=bn% ziMh1O#;jkDn11nlYJ_73%t zijV>F6+N;}th-C9#I3&tB5E(znNh+;pY=GMX*nh*gt*!crcGK8%yF=@DzIJz@3> zVJPy`8+pyxMqzcN4`04FKS~ywDCrNI)&zU4i>A-P&|hoHuOZve8leZjhcoQasDn*N z^|S6l(EdYE($dVtU^$)9z5D;)&51NJRIFSRwn= z`DgBbDsJm5XHbw-twXbK_cpFj+s;uAAxkD~#ZU@&MOFZyYKeH^Il5w49!9+&eEtB; z_VE@%AGIzoa^k2Hr%+3{vu*_GQQGdXT<)cgdk9(3AfO-vXS*_~gHxXMxxPw6eKR7L zVTG=qk3ut%g@_q#_UkxY^~kNoh&a!O*)aZ2*mQ#HJfV$o5gt5K84fq`=mx57jiYSz z6gt(gqyavm)uto^RT~*LjX022@c@KlHKA2NDI2t|Vm>L~7Y~HWwtwC~&E@0|Ay!I} zOj$P*-82a(A%Moy^zPLuZLvBrP!{HiOkj?jqW7`>L0n*;T2*$n1dAFcH5$MKg(blZ zFOTnv)|u*krZWPJPm&KVZg+Q1=ix@A2H*uwJ>bfp%Mns65AIj5N}i-2sFrIL*4~XA zIl2>A>vS2dnk4mj7_tjI`X8F+$%)_sz?_9HBHCW3H}OZ*1*}w<7f-gkej35~ogU>C zF#M`lYL^_i(Okh=Ea0G`HHj@_NAmvJdGK{E04^M)ZjoJkHT@M0{R6++Pu+Q67_Dwr z%s}DqAj^Gx<|toP&IA}xV`}Z*T>5B|>7CuJBT%{!*S9x|tzL?j*i?$>e9nyC%L%UR zUz{xCRQXrfp^LB=WrZ_g#`Qt3Z4KnJE%@<^;Viu+GqiDf7C`%mEKdO7@d#l*3HNh+ zSfH>R)-3Xth?+#-xeVx}4t5=XdaLOp;?kkN$Pf8BsBZ}ca0-khh-0NwlT#0B#K zfwG(QIk#bGa_v4a(bg6ls)628 z^sS$`)OWhufHKIwz_*ITR+w4Pv_UBh=j1OuD8~dy{v0njxZ9ZP;lzf|pQuP@EH0c{ zQN+uJ+4kx+_@@Css~iqmh5a`tx9yZUi7K(?_jHzPRPv0{xU`#xW!tbK2mbhy`NW{; zdbGqLrdIuRm6G2yF>rYol>WD1vh@})ZcMt#3qEf~Uu@yoR0{UhrU^jp#I*dEfv=|R z^WjC5-RhSs)9f6y6fseRG(YImK$s8DU=WJYw_q5(b*5|1`DRJQIUunBz*Pgg7W?x6 z$$XU?40^bbbiZ#YG^VZnuaLJkGW1H8Iyl!4USZU#I{;*i(Zzd?)8sZ~+|FsGsNJzzyAlBM-5YOA$>o4H?^!c~d`?Qn1 z@0fNEYMa+U0$lBQvIGW+V?5-gtgy&Tfb}~ByYd{457Njxzj*{jEftTa8%WGt z#E*(^-(SAaQKgY=Q-;+avyXRK)0jgLEXQ zT&mE6thpYb3AjL?e6~^jT`1Cfi2*JGUA)6mNTocwSxzZ|>gedC z2V2y$p*Rlo%?5BTbIxn?&yz%yTHZ#O1lF9Yxmh?LHE_U&_l0;2UD@Mo5Y&EqUH=%= zx2p<^ZxDWEtAI_ey_k?L$#TscL9$;r4&ilM(9+e}bzW*Tq5Qvm!`jCUt z_aIL!n|N~l7qFq1_#N~6KL3P7P33{#>~Ano9KpkjX2`qV02dj<(=weXky2q#SZAl^ zy5ygrE$`=Xyy0aonI;=hxd!s%pYpfC3w{M7qV?o|}Q&XDKxZS2lhuex%|TU|{;vc$bJ5 zBwAwh2uPK%K>i{2dU-wZS#;0dyrSt{nU(GodHWcXVZ51amj zj5jf>w-N8%mDBf%6L;G(QbTD&a&3p@40{?TNr{Xe zQ2!Oc)OEi-$xLHHgG1PUJwwO~et-KM#7_x-dcT?ZEB&&z!|Ux7NQL9}Y?B8|zLrFW z@la7?tn3`zIhEylF8^y$O7B{>EOme7r!@!AIf|K>~0o}3<(u^)OS@T9fYz}s(^CIk+=+-~L9X(0;g zeaW~2S)KZT+C%eiHE%QTTzCGwWxt3HaH=hwGEdF9R!o(NVdDO#A%??=V4;P#K};6_ zb#P52=S^k}5n$94QqSA0$Ld)xZgruCFV>j>f4v5=CQYT#FT8uZld-Abchb#g_;rpm z+LbT(9~blYpLR<4uip7Eop2dh^DNF>tOu&Zp!ydgcZ^o{1Aczm=vB@_StQZ60NSK9ljM}4o4!pYxuGQ ztrQzF>_xcSc^L0eh~@2Rxyxx`3Uk*TvDY@ev(HE#X%A?Vnw0%Z>A-Kqy2&NQ!F*of zGM>oI$ns4)cMB_IuY*jn`kv5 z7Bk3ZkXm}<68DMt`An;3C$kp_=W#E7fjFb-owRnfQ39G7035o(*9d_A-Hd0zEa}@o z+FNo<^|W6zWX%|bD8N{(foTBOwOFncGlCW*g+!oFp@wH_tpAGE@Cj-xkf{brGuXh^ zy|Tl^PDANiv0l}tQQJWVN-?toh7{MI;A&(*PQ1Fff`et9^G{_=ktDJs)^82HGMJg2 z*e!I-%YJ<>TX`$Vl$iRy!fSkx_!NeuyvV?rax<;q&3Ssy{J>EayXU*Hw~((@{IA6> z=dgyX8Oa6!An-96zjU}OXyraAqnb-i4xV(iHJ9!pch(pH)4YG2{I@76dc_Hq_$?-5 zohn6T>a`4?um&E_j+Z4wqPhE^oM@~ofIVq`9{7zxG$KKrEvscqHdBP4j!EHDuP= zFY`rC%O2v!| z+r+cidNS}F@bX?D9$1yn(s}c2+Mn-#Ol^@w52p*x;cb#F9P_IhsG{n5bRVmAL`+h} zuR9XYxyWwbmbcCm9(XvGA=rAzG27|@vOZbxF)Kd4|7KRYCU)kn;f-A9Wj>YsIlhV* z@`B4eDb>mgQR^C)CIgOg$t~7u^m^niecQ%$y!5%_O>DtI4d3GvQJ)0(t5@fk3@t!w zl)8Jlgln`VSKn9z@n-(wwColYbE&tA1DsLRMfrQC?a@P;jin4;?K%^0L}tG`+=mvr zNO{nKS~e327>hqVU~%X}^B(GitQA^Wm&lfP(V?BNw{}YzTV74;OjX%&h!V?DpaWAS zI7CtfmhiRDwR-S1(+2i{&S~0#F2(ir`5+JWvIseW)BET#X>iNg3d7i6DR#^zNHU0Ya)zMW{ z_TRRfn3nSAnIIjRR{CdLiczt3i$@pz?jG_paz54I!<1SByk_A9(xsssV12UP<*#WO z+EWSfbE!_W$Sqo{@7vrp?3UrCoqH3Bq9a5a2Wb10zk980go59Dm*(ZiNQ z$*mR90eIy}iYK9zGM<|-jiMD+pk8d8S9&U7iHOD!;YJbQt_bD~JX3i-J%3FQQugu} z1ykd;=4IhYP3qmop0E26PEwysX|y1?O8}P@fCp`#%!~;u>eT?*6F=|@PCe8DmV)E% z0Q`t_*Ra2N3@AxmRs)Hdzv%HyuPtIECi`U(7U62#lE*C%FseL|O>q(5f+yU!N`>RXXMY*n z_%OR8CguCt*%WbNg^4w@AL={_JAN<>K;sY}V#H`;>8N{n{g`)`T?;yxk%+#MGTMfR zZX>kT7y>LvEwHy?UKg*!B`a(|U*O%HBKQz{)0G357j%d944s3K>PD7@;BETal_BKJ z%T*&n=hx0R`z!naBi=2Q!hU#mY`LpV#%V7`=vGApcSb{|8Sc+M7 z8BNd-G_#ED8#Qtoktw}xIx9hrlhwehdG@_LJSr=B8qFRC(k_NsP!(CYAO9|zZ&C?Zah*wZ) z2KdXPr0ygPg<>x%l)xs?>kanhLvIWq6ju8v3CDf<^j=a=pAXRiND_Ykw+KQCUtKdOgN$F4gsRk!_Xi?8v1u9tWUKL(uHVLk7J@<&rll2aOZ{7)dN6~8| z7Y^ud>(V^hCdzjPX-+}dFeIEBOAw>Xf{q`o{)c-M{Qq!|$ax2O4+5_zb3q}01|!{N zH<#2%;0Av; zAbrWO1q-ntEFv0dLNBtJM1cf&>%2cP`W`fSy8n@Z2ELqUPLmAS{pv%putM%xKO5Vy ztlGrB;*aR?^>#>c&7cJ=5XB|b<6jM1h&Xv|m%TtwJ%kj7l-NCLsN?)af>c6m?J#8N zXMA3Fe6hq_eH_oWM2IwTrYLsD|LxiNe?iChf1@txwI@?rt(HEleYg^RTIQG?)WoMf zEfb|HrSi304wi{0HEQ&eX_NRoZ}v^Aak0ghMx%^$5>EQx~xD3~{8bzN*M<8le zmWGwRVmhk|iwRrQfa2OuR0xr;!`DycW9IK2+mFqv@$zqy_yvsXT13_g8e$O*M>(DG zTcf%gxJqt%tQ7E>G_kdSB1nLC%_nvgfK{0q#Tt9#T~~gMa}jduF2$69tqlV}9_=DC z(sU5=uq@g8vco%90o#rTIx2P9`DnwJ-UxjkbxFsHiJriO|LJ1VtaikPXFhOKF4#F>KIJ#XF{^LdIOlS|M~BK8zogecN>0it&j9ahOnSt9%Js06_n|f3 z&y#Rx6zu-36KvVLE%fETXH~L%Tl)g?MS10?yR5;_LjC*33VKnTXuS`7S#^xvlxi)O ziLX*L*@Y9RF?iOaY&UD3;O@Fm`fa46iLVqI+J|93j8xOUeO-&KT=?Gemb7ONAaz6} zhNWzq*Oz28W2eibMfb8ECC2%Nb$Of{X9I}=OsJ|EnmJZd)xe;?NQn7c48eZ9TI-RQ z^PqsKEaI!VjW#iYaPSK*6vZ-k<0x$Pqs@56=3@x7yTgS%_T*-`XroNNx9i5(W9WIcLS$9KQML%mVQ_|`M+9# zb31@-Jn)mNiB8UuC*VnR0fN+zCp8z3659;8^VuW3K-E49dakm^GzBY*(%%zd5p|W#n*FVsgo>b=;>-5l&sp=Qg9bObdJ&68_*GHCP;Xt|}d( zCm1}Ks8Lz38HqRIJ6zsPAPaJI+%%1yx6dA{41AKnQNy&wM9Sym`^H}qD~lA@1V?A- zx7Iq1>Kph}ok<706+dV@X%1;^{E_!jGytJ0u>A)M@GM9>N+DQ3`yV7uc~msy+NZzp z>;;k)?{OY<`6y}GV=l5?BI=xSM{^zIb_WmSQ`v<&W4;=CBm~2VPSJz^ckEK|V~1m& zG!!UnLZkTbd~A7!-X-}<5KEN;539|+gZW#mW9JBrom>@*CSOggG`0#7X*<*zGO_Nst?=HDjGa2?n8A4G}6M1#|!$#^DZ zni{>NBNz%9&wDT27u`dW-o(j?slOn1x`{vi(GW&}ksZg8YgkUHB5u~sKFoPH?`kn6 zv3A%6*lj8(Q_RVcXF3un3pzU$WcNs@8i>9y9xA}aa9tZQLq+(Yz8dRQ9W)o%I`M@~ z4NN$%;|Vm;MxX=`Cq@E0qcNueMtui}6HEu$yd<^ui8vTvN24tsAr}fjwh4o0P-Qcv zl)+iWqzYNkvBOq0$Z^wlb}%4Hz0VPFyTh*pFn!ru8Xeo`ks`C1>w-T!1#}i=(+4(B zDB{T^?%w2O^o7bUldQIhKM_=}@N_*GenO45Y)`SCuxER@kO=pha_EWRpIl~2(;XmJ zPcL|8j>cfvd!I?XtSGJ^roe*30U~o=O9QDJD)|w$(TX1k3jjRqCg09UM7fl%`(qf452`-*p5Z<62SiZA!BFBL;(+ZjMco`A5Ge$2aI05XHsl9enHv!cTvj-+;H}m5UJIu#&Qxhu`DF zv1kqSJHSkDygVeS6)GXdv?C=Z3_{9`abd^R(ak2vBD2&wq)^zo8WSJq!IOSQRlmzY zy~yyneSuC7QNEn2L@Ey2C>mZ|aPGRqu(pBs%Tg8jVmdlNRG8<)S!OsW;Z zgbSmj31h;WzCCsw13n!CKWy4vo>@EH&V{K9V&Eb>P|g^OnFpS?Nwg{W16<@%PY9*} z{Rp9+TGS>uKdITkjEYZm=c~3V1fZmWQk)!0-+|pR6|lf z;(ps+hK&f4y^2aA=_D8~G9@fUo5edn?2FverVU7&T%M8e<+X%iR6DcYb5)@qs?_!R zD|V1lW)3|Ilwe0ds3L6Kq!Ih=-mT_X=CtU2SKEe^%r;Q$ZgDkO-`r811`=c3Fwl~Um!`Ap8!U`cOVtL@5O?=Nl`b02Fz6lUw<#a zD{<=@WZ+Q;;pBe`Hu#?V>n+MJE?aNubl(v@nd~Ga^gKgvbwTbDR!o1@qFp{ylAF5E z-cFRLZ!zat3|d6;Qvno%moz)7jt`C;I5tY>I83Q%Nh5iCKgc?J>xrDZ)Nu5TO7W7s z3_9Ku28uYJVH<4`vk(eYCb6}>;2E2s%|?JQcO9FNHzTk-Ql@XJGK^eV@~i0!YoO|U z){I=O{RAIOP`1RkJSkK0PQzpI?gqp?_S7HMX0kmIXChS~-<3VvkOFWN!N@m7bTqz5 zB%&yYi^2sgAe}BXRKS^$c${Rw%OqSNSwqACekWunPB)bTScYrmDIsC!>p@gf|LoY< zfN5};MpeILmwpo~U?dG-?C1euMJTYcnyz_pW5BVL|MqydxEjDEvM^{Zah7ZZa>v}H zYi?R<7^~SYb`ttVGjOgmkSPfAXx|nyYQiQh_UgN4_GiZSODlWU_xU?_j4EE%DA!5i z5Q{ZJq?&(q=PiH9LV>+piF&CaGUs8bxlGMa;=fw)<&awXA0GpiTlN>v1eoa?{*U{+VDupOiBAmbNUPcCHj zBkk8`;@9#Whyr7j(fYOIk8GVMul}z8=4GF6vXj!OnW)376U>sMIZ;T%lv^jt^7t^b zup4SUat^HXHf9=e7fUfe8dhF6U;aJvUys~4tw~3&;yDxu2eJ{6HP~9o`qpqb>6UUB z4MC)|wP(Fz-4I^Kgm5`l-`=FD5VoZvccwsBsk3oxFlh*Vm277&9oH?K2}kyE^K#TP z-44EYY3|YrVn$DgkTYFaQkF|Pf_D8I=~$;n@8T%L08L=E+sXHj2z;-GwjVr ze`5~dh$q+v+qCca-V{;2?Y@nOp-Vxd8Nkk_kC4FtsFfd|Ynh`lKw=J%4DoazodUL> zRlol~5X+ul~}^KVQm z&UrgwmV_)=lowcv2ncyX(b&Dh6mzDVv|Jl4c#k^NV_F%LMLHg7Hv!TBH%KH|Ls36i#UFZOHvn3 zNMjk7QElhWSgLc1v*pf!hdd`(E2FO4BTxsHwsX4qb~b1unhdj!dxRYMP8L@RnPrhJG`Aa4~gEqjmT#fEP;OqHWXuoXrbIoz#6>6MkxirAv^YT z06WU3m}Z;zNOGneJN|A3k8kjfaSungZxxgMM;M!9%IpCUBR}6eqhe;4F-}AUjBRuT zCDW{=f-7E+7v?#ea?TpV{ZE=g?+`LyQS5Q>v!V}Zvbg?sCT~&bK~mAyCj%s!e9Nua zHX$#|bwPmIcD7cD8hGkk;%I$ zQ749)hxpUJvbuQF_|eN8Bw8z}G(w(t6tjbgAA8~oI61RH#TORWeB3D?A{D;Qlzb#> z(h%DD{!oBSaXOA;k2%4HQJzU0*J1?4>7Q1kWDpze%Z;KCtxaQWNT&Ww%jLdZUBomX zXSH!e(Cb^DFsb~2q_=S_{OJ*m{8J;gy*YGEXT_m&_1}){xf~kJzraxG|braXZiv)u14iCTU31lHC(3O1kG@?EghH&YyuDD*lP3hto zJRaLcNy+V9LbR#qDf)_3!NE`~50o3kWi88o06Z2lC;a}G#KiB#Y-{Ht;nRM0@E2!c zLxmlv*XHW)e1)sR{FcTAhWoBy&do;4_LAjX1SuBq`ZK=mE=^AY;AD(*goBD6F$MYK zx!+mEv0<>j516{LBh-n1P5$!gOax$fHI(eAE`^1#32zGN4hCHlzVYCM_5Rsl*?~{q zCE*|=kN=0Mw~C8$f4{%)8HVl-DT9&@X&6uuX%J~7q!FaM1`tqM=|(A~OB!bAZUyO* z?yh0xKYQ=r_jx^s9B_2cr`EOBdmW;?{Lm2ymIWDT%{$b(z<<(;EcQy^)j(K;8mqgJ zVP$6EoA&antI?8eX{cgyet1IFWGgXI;_!tO_W@C*L?BKVecT%Y3#+kmU86Q0Db1!} zri?YAJ@9YRu$8Mfvj#Z7297G#YkR+7IY|5@X5@0wT*vw$bQAmNhtLFasbGd8HvdBU zNo-QmZS~)el4Sl9W&LmEwZ^E!g&ntXro^&LFNYqYh=I06U3BYFw!gGU$$Yp7mB0hq;biO4e`Se zEM3hDdN)H+xkZ2Z*8sWPeu!}XlUvGUdT^(3eye~Qf{Hx_+G%U>ZLCHsy^nJs_htYRji%!96qY*O{1f!#x zfVH?i`9}hLWhnQ(mQsc_6Nb~QKbbNyy}`ZGb|=Pb!-e;AI{f@~nUjOFWKfeJnWU7r zk=Y z#&C2={yg|HP-`O7szb{f7u~!4F_ck24cB=1^0;G#TMmpCPP=2-m}!SE7q+7Bwx){T z{ptKq7-e@?kyu>rJl6n9A7RZJ7wVVn3ido^-*h)>bR_Aw;LNDAK^WykB&>@@u4+FT zU5I9*oZA2UkkA5o5&~-fJ^}~c*ko=KkY=047{u6Jx%EWIb&Yh{ZdCL${gBo@e8eJ} zC;iEz>@_~~B7u1b#HR8U$N*r%PiN#Ke0wiuFlp6Kydf_Y(WkF>16?`$9{n{pvg)+s z5m9q)r}SUf;0a}qgkj~U0d4`BjPIuH(m_~Qk9-ILgFyAP-WQgfr|n=LHMms3{8oPw z(3(30gNCC0RhJ!;fCrSQfqK;aw%ZOC%mq;lmjrTJ?F;#nYfTa82_SY*Y;1L=@iOuZxM?HF0VLaH@i2sB?6m9{8l?pMT(-Y zE*t))j$V8caDqKo?CtPRqk6lqi~5lNvF^7Va`c3xgOHFu>GE?PRB!$zAtfW)PIYjX zosh*-%_l5dPc~ls@N*sanTsvuj#LvrqRnqX+Vj^K8*`&k}R zHV6|uReG|Y)4#XGh-T_LL$mh=rSx{r&HtAiaXvF2oH1&Ut1>}%vpUQ~v}ESv*3j5T z>7+vkr8BZO?~B)1pT4YSB+|t)e;>Il_P6u8(1%2MeWlU!H4QhspDVQ1?cBaC*R_UD zqO{lHLAwXdS5u2D?;OB|-jheSuFC;+q4?{Mc}|PMq9h1*I&deU_d5Q>-gq`N^)@t& zaN8~2;OKLzI)+v1vLL}qNoY6ZM$95f+D$*DrbqT%#k?t7U|R9r@mmvY?l2K-;Jg$M zIZmzrOkw}bu7LCzzJSN{bt+s*5#E2x&Kgvn5_A7=>iJ)&K>gE z9dXS5gt+8pyc|hcyPmMqZ{ob`iLlAp91OJ(^7!iZ+hIfx>ynUMZc06|_Z=Ie-)tTh zU_gu$+;w6K%BqW-pS2hZPw&b9lR69uqKX4q66XYaXLEpaUvR@i|43J{WfO_a5mXmE zF>Afonez0s3cu zG6M;^_QlVwc=ru&%ru`9Zw!mx{gUX3Cf;r)6A8}uzv~7g&|F7R^~qqA;EDiK zR*Xr&qDQ-TbZzC#-37@($u4{1#K(<3wKPF*F8rT=yv!0sVcm+F{z%E%Dy8`KTShbP zDAxCpBMVrO_xn$$gf0nyTaVieRL=CvS&Q1j@Ha%ZAoR7Y=;h(j+b?CT#GyD}dr#gf zbbS9&{9CM7kex0^3f;p}MOi>QU}@<)A{X?1ef!Z@(BuU4;4yUFkve0^kH)vrB_fUf zW&MkI-L|i6Aw=1v-qmMi$0v(%vGA|?Dq#t-xn|{atU|dF+(2!Vj=ZYXR(~C2UpYiW z@_**CRs(#^TI&ZPhe7*r1gR;izUq6j1$ep`^1T<*#D?(gc;t1I7qQgsV$VU6S@K{x zznd2ENdr$#8rf_ld6vNsE~tuNxGFP=#f@zO8~$ z)n{?B7!buSNCymzbmS{>bq!kRGfYOPgU1h^kqXZQcUDW^4|Prsr*wDI{(^W&pA`w)+w~Tbw ze1Ge)PJC%D-spotjSyEJQPWjx(yZqtJ zz{&lXZNQwJ-l8X(saYw2=)!Tp+)QOv`OpTY^cKa$e6`~8Hn z9nlV3A9t{Z-N}ck#!pLnW76`^e@Q~45$b?KU}_SJ7K}Rl+u#`OZ>`g zf$eA(8GmB0BK%?udH7qT)emlT)T%!pCRLoq1JbMmr~Y&sKbRfT)W=sJ-iz(cz45Po z6-z*IEw9fuRmUMs1iR?V6Z-gpRQDn4mPL6?i3Dae3L$s~eF+<%IOBOG3(`FKqv8P@ ze-gpe$DzwKsci1Or|z@P%p`}D^nP*v05KLJCoD|GZ%PBi&%U&scXW&r9W`M2x)0jm zMs$SV3_ZoJ5aLYw)M0Ij^NRFPqYaO2W(*w`d z{O<-v*i=wj;r`PE{)SJjjx0eNQo;pv7(WLC@WIs~-9l+vFT48PO0Vq6cvSXr&YD46 zg*m<}aoM7xe%$2g%)?VF7k=~AG9i8T8S~cu{7;H2e#w-aIruyrTCn#O&y|0HxO)>W zc<|Z{Ze}U2?EjdD8U1-pM^9CXwf3mg<0kg=}&v4z=rT@J7Za{ z2-t^~mE2$`VO*eMYJ`%8_%6LB<_um!Xiq{EB$#rqvBo@AbH7voWwM}Jln!fNh_7kI zR|ORSk%nXrevE7mPE28+8moR((b2n=P zuE&p|X|r4acv&l}XTz?;B|TsXtreTP{?WjiakJr2kq?Wc3;PhG`Wxdy{H%R7p%Vpo(I_uZNLM&N~kqKUa@bC~AH<`h2Z< z@9n^6B*@nWXk;e?T8HDpt+3_OmfP(aO(I;pJ6nv9(YKFH+iB-%j-}>yvL~q?*QI`B zEMS#h#Bg;@qP6j*i61j(nL+eukTs(N)-CfnAokuq)n+TqI6xp!Jlfp z_MXjOW%9w@lp_8|2EXuA!bjW#V{8QTX%7%YA^cqbSj*sH@Z%UX1vkXzjkBZjKO?PV zMe&Mh%uk>=l10`Vi*$oNGGr!&;6h_174bU>*r0Pxd%PtUCby0oiRbMA8|o^#zTosf z667JRYymS~1al;~)S9vN^~kbd?--m>O~c;2?Y<%!rToZJK9;A!4IEJMVQB?vuwSJ~ z^VwKhy&|yi5I5JxN4PZdAq{W63>rU|k$`guu0-QRMt!p47c8adfwMU(nbLgQB7>)U1rvm`6cy+^O zos*Bh)R;ey2%6M8`eQxBaegnK&_YfVz}Bjv&;8Mcev;fpe72013Wr&2^ApP84@KK& z4jj9y!xwH`k;b#bPYJLhs*ocoZwc(ukyMcOx8s>f_bU9lWI#(0hjX~-TiOQ@r4?NcWNs!&oRwvV_Z&qiIH#;KtBod(40aDj0KejI0sEruMANTM})@@v)K zSVRNZ?%C%xzk_91lJRMZ9*rf6irC>UmMj6RM5y_T`=kRAgX`jZEJn|Rek@azb)RB*hJMgfhjpJHxJ3?%k36s7St52e=iE)6{~4y} zrn#2DvN(439%ObEcUcQA6HH6{?2M}rBx(`4iA@hWmbd9gH0){`OGv;$K_sQx*B$oo zSJcrHgVWg?av|=)inFcIl-Jhs(?6meS6eA{cT-+xI>&zs#e*CYbIVT#IOC>$q9*)< zSG3Tro}+o=oCvI~ffP9)#=77{4%0wX#abMC0*ZJPQ4A5U-v?@!4$Nw-1#a$A8mvH!=9wX7aHep=zla2LP zsApV9bFOB*=i#}E+^n3NX;ztB2O5_<*2JFkP*E5fM#1`64$n3&GLv~Hm#@QL;!_dz z2D#%(#*uI(6`peO)wDD^<#rttt@dqizd&gy8V}dr@MHfCS}A6{`&ani2_i-mHwX`YqDY~OVoD>nT7W&9#j(^68CuOQlKz|h%+^WZf!m_F%B=CTCS()6WSOraAbp#2I{`t z?v?OFhE`3IFF^Y`%L_()KkgpX{XM!Ea8yj{zd3l3y)Q1eBBT<*yeY84r{BL-r_{o60ltNJ3G{f#F>Jw}Y&C8^V@(MA#H;Apj?9Wmn)r)!Ydnpg zmuOcReKWl~YSiA6?K<;D`Mc@B>&2ex`sm^N`3AUu{8o_C&h~@pNhAZEbNCHyAM$Ao z<~*y$?&byGCcK}W7MI0BOo-iPgNI5^q1cOoyG`P#40gs%tn%V(v^?hAg(+Fj!*6h6 zU0E?r^yf?J;COtu&8x#Nq_8S00PWjaaEqBZ+7*$;$-Zp7Nw)K;q~5(w5n1J8eP;k; zTuvTYV#ul!Jz<(Y@B=;P3>cQH@7YB{Uee~@{g&Vq|+f)1TafpAVubd%qf|WkM zr0lZ;x$~lEoaaOBNMZX$o-fJ^Oy89%i3q1@%-LkWR07fRN1EG-Ib4#k3~I21z2{#t z?-6487!eAZeOc-1ld9#^36Eqw?2~Dl|9Jt&7LT22N^JOe?x7#q0YVbO22HRMO ziNbCx!egF0r0j1i&WJ?uJRhccn#hq^Ggi}DpN*%1Ms6RBtOcAzw{8a-M7OzA$E<{E z%UJy>o{J50b!5YNem%#H*bl!|rUDyY``=vROy`#3Z+}WdVkIaWy{1?muTZHXO?8f^ zSCm*4$Jq4qGR{~?Fd&j+YT*1AkmmmrT8dH;ybJR04f^lytaDTOcMrr+9(R5wESDn5 zHk0|9eeArpodL`HNA*{$b{bcsZY4TWNL^?=D|YC$#p7@%m1WN8eH^e{JXGXPE1faE zp8iAFMyBEbccN9^%`*|gK@w~$m|rtqsK1UNety<+Dz;)E--^z(_ZNoz8t5O24>;Hd zBF{g09pYiRt^1}o-=HolaK7;NAY>D|2SM;$2b93Jpm{^ED;!Ny72@sJG)b4A|N++uZ*~Yo_g6K z67U^aVH`7@?UyjN$DVXge`_-Fj=78BdkS9k&(-=*I9j-MaIC~O0FvPXF~1bWY>x-79PI%bTFo$n79Elvwj7y4MR?UxE5JFe~P3 z+=zU2C9YI49TY6ebzjwW*8^fiJoz%yMqDSu2{Wu$teKAwj1d3SDeyl3I(CzzFISf$ zY9ZOZU^{E`HR|`7!|Pj=1js~#3)rsgy z!xwni;`pIn-=W*ON7%PPc}$_QZ$)LsV`yk#vQ5Ke0m&b`Fp)QLiA1))Ewk89`{$^5 zc;D?QSqFV@dWyd_coOxx%{tzoLvMM-v49JStB^;qxV@%s1a`)`HJYyfiA~3a^^F2X z4eF-oS-z|xg$J|1Z$_ODuZ9XKmTNJL1R9yO-OIL^iup!T+eH%K6%hY`)dq=8fBf)Zv`%O7A|8|-W{0ImxNEW{@m4KI@3>&(ElD`>vhU50;Yt0M zQWQ_;KR33j-_?jj*mxQl)E+y4yraHu=1iIg%i%u!QYh{tyHfqv#)rAm*ZAje?1|9f7huc9+~nMB;>^1 zhCCHSK-NxJX@Z7q_4EpjRH(#JN5ld?CaXStqk<1<9^E~edXE}j*-slbr8fo1A7&5Y zsJY~;P5T^NR&oU`Ja%KR5zug{?jsZM?#Q3Hy*CMwFm`Gy8kuApik))qs|->NGnr6%UC_1ZGNET^5!`=mEh>}k7F zYC#xzW1Fc)C-@qlbA^ggEV0Dx&GGc4c3 ze`}#T^0^?$-2?&>JKgOIi%l2ElYIiD_t9US06L*_LF2nExWmO^$&R$O==@Z60s9ho zK-qr@f{3s)VAw7o0$b0p`qga;WU60ak)2GWsa&r{D*%SxjPCTFWQdfb?uq{sbuOnJieEhs zlKm$=BmTxbJ2q8I_sq26v{UGX_|?CCiRsrt*ytw#gZv%n@d5ZT*Z(Y>U&k%_$HlPQ z0^XWCKwI~{tRTDz;i~xn>+%dxDyLRT&|lEopU;s(@3VCOmKjbkul z$&s4mz!x`U zUP)A1a>JFjDxA=U`xeFnjiH}hMAw0pk~&&_5}4mTn%&lx)A$DY_@F@^c}%Q&Qk6ksN6rPKQE?So!z!A^^}M6eLAsU76u9tj9j zpEy$w0k|VWh1IZqVQl(X(_be@?mel{RR29v`3qtQxn|V&15dmmh0um zkYr>~AY`U>kXm~X*13ti4>m|Tss3b-IGyqAE1d+K%hV^itTkzrOd2-}_=WgN& zGCih+n2~@MeQ%@bfA7M7S)l(e_o6U3!=eL!Vm;UOSoHiU6s;tD7e~-Jhvr_7ga3E* zlFp_>#1MVFuZ&pk-T|w(*Y`caX=ZQ6EFmwuZzk5b?C7|c3*Woud#3;azO*!&5{{v= zJsN?gK}kWMCkFlq_mi7RD{X(_&JjG63#yfwex|JPsxe%dcBE@EFC;DhdnCS)>D=SC zS1szAebEk7J>33GN`_R)gz-zn-ekp@@0a{HXl)gd#2<8`XhlRXVlQ+d!tuXgRn-MD z7AD2HlSwf?o7LTaXNSL=P{kAYD(_ZkzQGuj3fIeGh^PbK!nlZucrez`LhyFC(B43> z2ER->1@P=^GxQrbX0Rl^{DHnZPMjOmGY2|YxRXYm{eKBV1fV9pzHZ6I^nS0tAUpds#sl$)h|*zpy^?n{I_k_y;38IZHy*)B4cD>c(* z6okAGDqLAy^MTgkCXI9;HVH+cxVB#u8S-YEE3;tU7azPS$`2=7sMdoCJC!%d1PUCj z7~krUXrl*>s3lLm^s}@xkA$do46kIFh&>*SIK0cXKG0~8tla-I(9YGm&_m#5O*J&> zDHF!8CJNvaz@A|$)bIYQQ1AX+P#HKwVfMWFso6gg^?t&H1`hPX6m>6OFmj2lP=E0@ zX#2ByXo#3@29fuJqU-P^kmw~zSH=shmh}fCQ%5TL0~!!kXC9Fp59jc|d^i_*Iyb{% zrmO8vOhw%yt6gdj?peH2f%4*zCgJyZWv@km$R7uV3>8ger!zjX?8@L{Tf{x}!M1!r zOa`yuyCm7?Z6Al(^$VQ5-!7V#JJ60ft+cb~G(u)E>s!6xRYiBFNzase%v~*H`c1 z;x!TK>>S#AdI^u#{zIaf1nkmJ@8-S5XxGQc6>jhIAEQav@+E8l7pQ!i02LqwL}Jpj zrQeWA?NeuipFlC;lhu{gtiOvu%B0+mqtG#&Nc9r^_OIW#z?@Jg@(Fz+y+QINg(YC$ zTar}6HoM(fV3d}Cg>PbPhL-DBRKyA;XfMvcgl?(D6k4%i)OrQs3-H0&>(P@5B)y(% zu8as|V<9ykg`O%dy*B(}>%nH(PrloydNbz|a$ZuU&C*c%Vr5HtV48PuS-Gt6xn4zl z-6dS&4pVREbccK$dza9EiDX&-`0NQfr;whsqD>E5iA(WzCJ;S?K>LTT!2?@94ZEK1 zph-~ws2@utt)xCTZ+{JL)7-&Ukq2;BIW9_8ZDB8?l(IjM_O3+OL8UiRI4e}GYW|-V zhzhr-OG|LyJqYlp$m&3nbl5(O?n1^c)^ka3*sKuyk(9PROQ)3F;aPrX(*9KTmmkg{ z@u9|phTCy0vaS`5iIZv{-f2-3Jl-CwLETFZv#_zg~B_|k01U7=CO8n(3ONtpmmqt;Cb z+x^F6S(FQh@?)}*MEuPJBU6yCLnLIkh z!8pA3M3K#8i+-u7*3}?v>Favf+5Bmh_d9P!f&D&%TutObUpu)~_fyW7M7?&m`~tcm zzm$SCg2>)wkW=zlx=5}n`CyAg#bDBpj)6+OL>^X3ZeR6^!G==u^S4VE$c*VAOn%w- z(3lFC-0MX#6>>8UouWn7PQ4a+OeOv;EH}gymN#9RR<{Rntxx@d~QDgkS$#kU>J(?cw#0Y zmIrZWN9AkWr?&D}zeF|v47v4q@wizIdI5!+PP&ktI0?8zD_^5TcvwPuxhS?=zXsP= z;@UPXqgLyueDnbH+4Dd`ki*jP&3K9APCt2HWu2GDtAg?=X%~3Mz^?h@LN5{*cYk{C|w#E zflvAYi%pDW3zK5sxF!fXZARZ~yV<*k5xY*SmwgQgu1w3LG+^iC>c2SWs`Do|)bCZhwOC7Z}_>#oiCt7V;%G9RUSF&$ZZ~V#PreK4L>1y-QP&s~|@3FRb zY`w<`?9_7c*p69Kl2F330vV87n{@tY?g0SU?PBlL#GBE$t5)YWayjth6X5xoY!d(2 zCY<-}&eJk*T!QB^-)P(}@&dIa6-e*(SAFi1|M&5d|3`IT zzYjk0bW0RnTD+S2kGFdN%%~vheD6S~xA(tx zvp=#IdYSPq{FUmcY4$F5M(O+XxrgT82@Tv(B*8CI-+$o1U8y@d9Ahw7yZk8-8A)Fz zOR3Qkvmo%b-lWH)r>^)^pkG85XN=O`d;BNG8y`w%K0=V>dx6*?xMTxM*PJWNx$0{w z=?rkf-4ltc98?lY+<#IEKhS(T=F;SJLP5`R?$r~_;1edLrztlQ6daCQI*uC%ghv~j zVXhPJA$XC31TZgoN~klY*yV&#!2FRT1^Bd!ynLpQ=`Ue}OeVFd(=a_6cSQl;!4 z?MQjC(prC>GF-GjTO7J>{1GZF6EiLrF?N<6MvxN$X{~MAiA03~il3$t853ca7p zLKK`y+N(&+&&5M1X5|4rA=}l?sKDJUDVuGsUYs5E8H+c?I#oVSBd=|Y-Bd^LVj=rF zd;F1B=6NH2=IihiKVMq$ipbEHE=V!j-XO z;{#W4(5 z!l5VaVH<8Y(e2u>1d@hX)wOTyjlRdG&%{LJq>I=5eJR?rv-()J8?@Tn>@_8 zD_D`~x59=;A}t}pL=W=qFFpO9%CUAe2^=72We0X36Fq1ndx zWMSt-NT*le`6-Mkj~$uhbVqu1aT-J&RW815i0P9YBoD?I-bJN*=SFPFf~>)!EY1|E4u#i(g_Qo78iW70t=T>s*w$H=~GupicauJ}3eP9jlm*z1|klt+w%j9G|kc7*u7uYtva@xPPQ zv?=pTQF%W$UI*tOk+0+KGF(z)8@@a9VLYnPWlOz{Y(J0$o z`YxOyjU1qKZkVELQj5y#aAtUT-<t1z8F|a~Fg$L$?MP97>ZkXWX(Tlwuiyq-zhZ%6nNwqJxCkEB`8syR zm`eO13uQx-2F&V{EJ*j3q#&Dsm0|eKCy-@G60;V>=z+O`YWIFBxrLE{3M>_@&(!Xs zrr`Ayy&g6;PJj8ecXTN4>emfQ=)#|X0*T)-mm|vrz3n>{TFzx@NZ|t;=63mEOK6gq zDK6W~S7a|A$Dv&_Y+2YdpJLidgECFaw)9DVn%1~KL}t=|x;C0EIDCY|hFgR-_%5HZ zUYXJo{gR5c#Zv-Z#T z#K2iY7bB!w>DlrQv_3wF;yE102Q*a+6>uvHWp2U^W?MKwv>P%z8T#*Go#BA=@XtWr zvq2Xhl6%tk*m_vA)b{xuT@LM7<(%oK-Jy@)eKBP%L;0UWQ@DN^nT`b#!s_+-MLatw zPkWVdm?Eko>XIy9VzJ3DmaufhP*e{rKg2o}vqZjU;(p20YpNE?`qZl{1Db>LNpE{( zSSug@K6X?}^HeHDne7$##E&G6{{2*-8tJdewT_`>XzD^fWP4>S-UR}va>gr>@qfu~ zyIaJ|p2{#G08&07jv8@$Ww=o0eF|u-GJI2O#)LJ8_wRd zCj(&F_^hsbn>#@{x=*up?xlPh=staK zW|ME5E4h_O`}+MT_cVPPW~}#g`QmTUb{gwrm8Y+imG3#)&03De2+!!@s2UETIPjih z3*k=*9}Rk`i+{v4dEcToxfu&ecQhuWH;0Z{($}C%hfcBo|Mam{LT}R&h92N~hurF& z=1hEGDET6uu#nblR=JhhrBMeKdX|xAKkk%Zo!Ea<^ruJ(C4z@mME)a0E8|GpP(C!Y zk7|>+R)%QJ?ND&abw0jVH%OOA5>S>*QDv8g5*)&fmeM+~eMTSpRm?AIY-OKX&>M4R z=hk6kXLlt6hQcPnI`_q&C7WtC+GEA?07$XU7~TPMzOM0mi-%`Fa!C#G1$42_;4}Pk zufgBBF@3VTrgx)98!6}jo?`Ke(tjcim<^Y#u_G=!kjbmicNWIZ26H0(J_3?g25Rug z;u}4run|+X@b_gZa;JxsiXAw}_H^ht#ocL2M?R!!EAk5Gpk7^Q4PkF$M z$rv~4O{D!_PxlsFq7mh9Tpx6o`rn2-$FaW=cW~EztK#yWUYB||4-VstTXy|WdQLhP z)}#6b+JwDGo@{3@KUBUTlw0_4w^?Cc#m<^BC7{N@*N4P9Tj<3?M?6nTw8N=zod_Ac z8`>Y*4*t6FWVXqv0(2jWh2hZ&F{v}0KPfv@%AqUJCrqXrIbm&ODX-(cn>i&edu97b z_r+g=8n;{tW*z-yAI%wg7lFrqLc?5ib%|#!Y1r~ASbLfCyHnc4y!@fgO@28rx?6Li4xmvk|-*{Dm zE%BY-7h&-@O2N!J4rTnYE_Xa(I$DXi%KyAqE4wB^16~W_0vy4zU8TydFdW>FFdgP- zvYyX4W>2CsHk?Pw{^|E#mW;jpI4OZHaPQVmDRf-GW} ztonQ&tv@d+Wp$G##o}i1-M|_0f+>Ho94QDpLDGMj9hl!QD)*RF-lQe zC5SYWk#~eVTBjH$Txq<_IvJs6Ny@>*!;w!RMVGKipRy7(#z1#U9Xmf|gl1l<8^C4yTeThi0EB9ZD46s6w$CYNq3;nKaHK7X zkI+N^meY`?hB0Aq%v)0L!ejp*B3BkB)ZbjI((PD6Okn(YVHHipFZV zjD-(LIAN$oOnP`)3izjQTME;#DqN>~r*fRWQ~BH6e-zGWjoFehGjK&|_TA;A_sD{) z4641hs0P9bKwDJeQrZ zHi;|TS{{vlI4bF>d{jb|Agm?g*gGiHix(<9;#f4QAkl6c!dTJViX}1r=tF9Qoo+s> zbC%w<5$F}R<$<=1Gp@G-aXgzwmsjz}+q1^JN9`xC=sRn)IBMLB*oS=F2_~PA(29A` zz4T?rk0QMi#Z(urgyZcLt(Up98%6? z*#IX@=_<_C>vkWAtZ2O|y1KW7tN>KkKg#jWftZP7@r*sXNm*KgBOpeTmbKh zYPMW15GGdYM)aL~p3U41uW!;B>#B!K=DP)!&t+T^gPln7hyEvdCuQ`-3OkpnVqqQM zlT;WXh>>MsEDx-nXHanHgA>TE9eJ)7%L}enWNA`dyB9ReTT@-p&>Qsx_2;RXh)mcd z$n{$8xZfD6p*e-aOVNQs8IKINJ+L)1&7*j9GV6!TnK~J6J06?On&~6@whHH^Yn5?6m1TSXuUl zV#8ih;9*s*eVB)(Gl+_5zGo!NVBqX3AX!6BHnU3M4^-oNbr^r(S^vpnC`}`KlP3HJ9d2T*D$+lySLxunFcjIh%e*BwxmZ8wOFVuh5$NDaTeQhG8+QJSUmYV z$d6RRh{b3oc8TSd7b-BtT?~qid8O2*RR8!cVyV-nn8Iwln#s%{oehTG-iZa#V$4J9 z!ta%VMFyZ&sazZU{h6$L;my~;IkeBK3%B*o(!715fXRGDwQyLuPOEBB}ymb zEjup_;PKIzR^1|cA}m3DVE{rgK z`PSD>4^~6}HkT~D(h`sY-7+d26u+|+@EA=#p&_R1bVgHHFyj$u;^zg>uxMV!U&w@K22BG__H@o5K?HKP$I4nQve)?!K3o`FqOg zqxL5whe(wiQ*3TcCIC!+iY-_21<}xui7-gT7H&-YREUN=!zvLyR`PxCk~s0V;oZj66Qmr{_`B7jO$;;=g!H^JAa=hRN!E$o2 zmQ3*=Wipmd+%`D~MLZc7h{AzBlZ>NoE#GPWpf@0=X4=q=MD}%z7F^JJ_~)fWfYYU& zUu@aW%@XE3`>9d}xK`eaCn!!y^A63rObzy)pfnaP@tD(BzEgxb{hLPO#D)W72|kd3h}ZpWSb{T1K}vRFX?mi2Z~w!j`Zc?GM|K*2Ff*A= zSjV^MWXnY`Kg@g}Lcg-MF)(dOA)u(X=Hc_6qgHq1wZl2il`G*F;g-_Iwho`q2ycIV zan$6W4dvM4Tzt7-9TZhMOQ5C?G?BJk`bDQ3vJSi0A zO?b)?{K%8}%Bco@&#`v=kkl}te>Y%1OoqQx!wD5&CaV+SN6@}Ea_RmJJ3?gSid0A5 zQ3Xt={fShHZ4rSrSS?PtyU)y~DnWEC83!j+z=(_3Ieh-3E{mBx4=_Q5O=-WAfZk;${>D!Gq zd}%0a_W*>$A7cGbc-`>B^Yq8XUVGz(FaLb!_qVBpd!l_Yrg$jKgAE2@gS8*gW?zE? zk57YWXq%<_DRMEoHdgE;o4T78*#F{!`|2oW4w!X?X)mxJ^%-H?U(e74Q{o{rG=P<# z=;4RWnu@EJW|#Iy%vTnbhQU>%BXg`NFS06er3+|1DdLwmDmFA~eyh=HlDq;1fy*6w zm{n2QDHQ%VV*v%yQ%$L4PGK}ZbJ&6DXFwh4;2eqVDy|;Ly9AuCAZ(Zu~?) zOS=|RKxn2Aln&p~irxyN8d)$C#TWM+&pL~C`+9twce@CgJQV}sLj1WGTg(?{8x=o~ zZGaCV0}=IWB!Fz2N6|N4Ri;NeA-3aBst#8aRk+s#1|AxJDgE4eW;3KM$;1ZSQ|7-q zFzXF#QJ9Xx-%o1kaC_W5NRj#GaY_Zz=F3OI6BNJ&qp>~jZRm+=+E)dIauuWWaw{|Q zgwzx=hcx#JM>j}MacfGbJA8Uf|D}=kqrcu+w8f2D&e#OhMG5Rux|S;qdKcuhyje|S z&ppD6-%6uusM4*Gr$PW*u8X5@=Fz?7rSBT=o_f<9OplCWjG1nT^$yI#Oh5tHPt=l1 zv^fmIfnN=nBFYx-R_UCxF|Dv)ksH;|{^7qIt<}p$&^pb7G#y+qlf*eJ=3)heUAoIJ z`blYvsYXuF%lmqXAm^jeY-wPJBD7B0gT{sm6pzzlE|)QtjH@B-QD;z#=AA38w4xju zKy20S3|@`L}_mfTdcySVwK{`$bkQFD0o8QqP~0`*j~^k09vF)vuqN0mX+ zQfVJQc^Ys$6<4-KIVpno$*L#piLgnz`RBeUL2e@)wm38@2LE|*gj3>QPa!jYOo7Sq zO;GScL+?k>n)kiYL)q}XE2l>_XctB%Bw@MXEqssEaN#{`P+p9vZ~AuYmx3RYZLcIW z1=_cV(WJ5orh;KH3PsPH+M9nJE6=()Rd-)L{;2FQvq5yv;{5;KY@z_d@98q&1v8c18y4kF+|LV`+Uf6zG&Dn&8 z9X2*07f!TO86KiWM1J_>H&ctw7EvxQ7G3O;p^zs00r4R4Ek)r?*|JISRC&dvZC(t_ z<9;hgA2gO?xoHGcb}C*3@pitXyrML{Vw-RDrkONO1X2#@#{9w3t|~B%`5Pixms69w zkzxx((?iVXwUdCY;w|U@W&x-qYjj4c2WtfD=DG0%GwKzJ0=a@ACUXJuMz?X8?19g? zC(Hhy8^`+*!Q{rSi}NhjgrV8}ZF2rn&De-3Z(*!k?1J*y*14Q}@!S5Nt#w7l^fy~7 zJLg|hx1NOn?E|6Aej?VMlWTq=%c*F*f|j9rNNt_qy$JhtykBb4Z#+arPSHLzEf*J{ zHG!+Tpxdf+R2=QW)gdZO0)rBmU9v_85X#Mb-bU-hqLT~S?CXNGi5zOjUuY0#B_*&t zTWk|jRczDSUEyF{LJf=Je0qv=9&9Cq3-cg?rQKj1w6tu-$2MFphn?ePxp)cs z*jqT9)Ugm{BoI+e2o9j|PLhuh*E_U4Nz~C{`%Q}p#=6=M$j}l@W&Zp~F5oW~E8Ifd zBDAl&9h=Z%Bwu#yP64dn#%d65lQx=qCb_3$QaA9@J#*Y_9lp9(nWjW; zmM!$FGv`olk$6@F>)sRJ!$g1{smO+?EQTPS;yYhnC?0c|Tn{#QorX2V<|JeNVgotg zAFU3j9Bt2y0vPwlSTa_oHDj$fj`Xmwy5y~Buul|1^d(kf0?$IU=(;(!^8zo}Vx3*i zL`qWw&oRCpWIETlRI(zc2pZUHTX+Z?S>uNPMz+v!pKOEW=a&|NApIKv(i_gmca#uZ z*LCE{bWrfvG8m_{>pQxmE+nx{S+%nlH>&e^M;L#d435(q^Lv?BGTMShV>?rZY|4|C zhn5jZh*PGH#%WlCldsO9sE^>iCKuFWaeoe>u;$LYCUtj&nL4_78=i(HefAUh!-&S5 zzAxZTTe$b3lnwgeFG-w{+gVE;{{FpH5q(73H#tAANan@ma>cS|Izf74N<&r+q28k-5mzqp>(N;Gy>ACfQWSGF0Fu| zlF~>^EK2uEDlH)0-QBVKjKBZ=yqXU%b6s>zh2)KUim+{KHl&!(5n1x7S z^v1X>#Y&5D)+^N859PX%f~8Urz6a|JSa4tN9Mhivn|Yf~1G`!F~YL<3~IAEMzpmf!S&?&4h< zm(=JH6n-8|`@(x$&9f?Sr1B8l&~4q?->*^19b|F|h91NC5{8BQ_^yee3&89Ty_YT!cS*%2$IQ z9{0c06>uw$n7d^eurW@%wCGKa-(6L-hY^{A$qp2#rS`Hj;S@zAe)i|R@ZEPwu5EwlyMKsCewZop`@je1@KFh@*F<;0_k*uu4xKx?WZY0Uc#5RzD8tElDSxgPg%`koa#1fNRunX{x}zo&ynrug@oXBJ-4#z0b&>@}wtmacg8t4qGtP+C zd}3G$Ffelbz9(2|jG5(b-($MV3|g(mM3o_wpmS^YcqW*s=SJ2!zaQ+NGFT_#FIY-D(6&}}A=zz|qjYc4M{+m4 z{K=)v`X(y{{8dtnP9VV}wm)X3qA`@;v|Ne0=QY?3jUg91cW)oc1B|yqo7XR(2QT!o zh|CUHk&XuLWsfa`F{^#3^UhA$n;Tv@J!{?Z`)rs{7LYtbj%z|9#Cq6hC;`@-{_J|h%#pTF}pgm{W(!LNnShWo^@0E z4%{lZ;7m9rx%Ahw$z)36_05)WQc&JCPu{ExxtFYQ*Mg{mRv(L*<&mr&VwcWWsmSQ9 zMoI#CP7IT6_Hl=^3trmD;tGu9>hay;^fwrFMcB0U=PtUcNeskh4L%B5>!^G#yzB$^ zE}V;EFrUxfT?;etYWisGW!7<@+H|tnzn}fSW*tYFvm#28p*Jza{)Pix zZM1*4&mnw?FqS`9o z2X*yme8~Cqyf>evTBP~4+b~J=mkSaFixmHCcb1-yqB~*I=fe`FQI%ZX4=fHQ_sR7V zZ+<{hemZ!kePD3HyT<0prUc{1rCtuzvW=Kzs+?eH$Hb_WWb?x^icN)Vuf7qq2sow& zf4Dli<%L{aH8HU9LU5*-L4QP(1{<(*UVB)HykQTxK-)!8DA+L-#503!p7sxz5HI3JO%)LTgY?Wb6ZEki8N!{4IFv;}D~4m5 zCLTz_R!I87L+cbRe?Pv5SW|pr;s@Hv1fccv*oVps^T^8LbH_hM^s*n z@q|lSS3~DmXox;l`EojoXYJ>u!ygF!EBm)_K;Ll#h1H0*BdfwA}R+; z*_%PQwUVq@Q#*cLr8Le3%LIEn?_R9hW*)BF*+pcMgR@1UJb%Yq7RzC^-Wy)CN);wua?z`vhPasOm4s3}ac5s2;P#}*E@5^e^vvV3x z|LS?50VK3=#H28foK+n(C6@r!MA@MVks7g zd*BirYs?4Pi(~CD*uSO2Nl^5}+1dj-SrJB%uvGxOl=FgvMrKOH7LbGqlbM}bwHRg` zvN!AXgRelloHo`1XFRtqS&=l!XbKC_TLIi~+bzML!Yo*?2SX*I(6~KX($X~*Sj`oB z+3r;y4Y9z8<@^gFwt}q7Wsyta)mk#HA42V?!{lBPWebc}W0t~qwJnR@2q|4ljoQLt zNVszs7naxE{zD5-v|TL5Yx%fNT*hQI64EYYX*Y-dm-TR^6AZE0#dzAG{cnGQt8dkx z5e?Z%Lup07^PRlN0BP5OwQ`AOG;dWD@|m;HG!<^0c3$o`hA|(< zmTcB;lHA^^i$|JSwYMFKynj2j@2JFpoIz*o=BGD^VTycr6oP;NX%xP8r^^jzaXC+D zNf1!yR*p4+#NQI%Y5hYv9M1!}(!TQzGUkQ1z{(5h87EH!+AG?%;Cq8h@q>ZX@%*?c znl|(Rh**vOkVa)3Vh8M^yM>;;fWCeXzI4Ex!X@HH_tVGfngCrRJN0i)o zZvZPX+0mFYROo>Kbu1{|vViiZ&x$-JEwHU{R$w0Lq=l?l`MRHB?A zQ)fu@li*E2Mn;{E_dyu}&P-Mli}ek=S)*Uz?pL*pxyP+{B+=1Mn2!VZ%pOd) za^pPwcXVS1dOGO=S*Ne#&q+p4G0tcypxDQzOQ=tdJ|IA>l*o&sBI^A6qjf?d-ayea z_lcoBvs^;)QtG5{StVTo{fjDmiwBU2^)0jLUc;0{>TxYi+AvmkD%C8dA}>Lx8tW7_ z-K(bahiqbB^v*xgy!W4SpX3tN6@cAUm~Py+cM1MsjhJ?foyM;$=*L&c@>TZp?a)nL z&lLJHGH|FP0q3TeQ!e668rhjLr*ZHeJ7V^*kbCJ2a291|rwC`FeR=q2v+Ex6lz zRRi9`I6F4uHMjRhFt;F1`8$>gL;U-2e#0Eo9lqS^E(csCK?2^MRtIDLjZBd$`w{7l zI5HB?pt2>dgw|q`OBZAE-`X9jJ*y5a5StwepKW*=Y7WzI9}gDhV`G)a+=`IdZY{R> zwH7|JEDi5d!&#xXD_3OD7)-%6(Lvo9qnl4eGS&G6hz^SPpd6!gk=`G4v2FY0g8W&O zQocma?rQNyMAtIq=-3)TKK6dH5qdfeFEE1k#XFPx zNV2*PVZ|Pk7hZ-jy0OapVnioYRGjrKIgY_eN;{~qA)M_Rma^@=Ndw@V?8*eZIZS)8 z_xPj2Tb!g5CZalB%yd`y1bMVWOvw=H+nXN*8(z(K9dF?urm+Sdvb;OOma;MlIG0D+ zcxRGR5t<7o*)sg1ZPJzX?7IGFC!r!UT~G4&TJ8{~UhDliA4}UT*+SPc&m&mjlMzak}+WGdj)(Gz|Z{ zho8zvjmEzpdG0RJQhU}L?0+u#?va+WrQgMyQ;a?{@b$xkktd7xLd2q&FbqxS%bWId z3c0P2)tGv&-R$iR>OVd`mpYk*tnQMmDy?C{_3G&~f@Y+Yb9OFyhBEID)dQ@t2}rk_ z*|B!b6{jD$Mg_On3r61AhUFn0FvMIkNYL?Mj$!v9`M@@YrBb)(h#VviT1 z&@J6ACK>bbTZA|8E*2mjHkG~3+Du*44|P-x{!p&GA5LLum@eoDEl0x00#0=?cUn$9 z!SN|=(`n=&-(D)nF62?3xiQLLE*wupD^%~y3wxFV0V)b+B-%{@qc_w}SI#7}Gkw#N zmD^t59xkL!il1hYi-mr-syn0ib0nuRzjfez;KFGd4Bwk*7JSwCP%*%K|FEtvP%3TG z-|Iu8$KwVoU-d<|NEctWWSVi?>D&661H}QMY_oG{(#~-g6?xR^XCi+bRrR!P^e>-s zt}15st-y1%U|A5Z;^dXq>zh>UdEaA04NS>8e9}tZ+)^A|YtQLyIyyVG5(L#cn$i0a z_B7`++|T*DFUKC%ZsT( zQDzRvWH|$v{h(;jHY8wE#x6ZYgrrVzxi#Hckkq5CT*hSO#mMO_pN%s(^$+&D zSk6rkUn+1@TZ<9N9&5 z$QP$iw3V2Qiidmx!y_I$dGo|+%g>E8a6&tT`@T9oQ)Llc)E3b4E43L$x!6nc%o@Q&9!ze)8{St9TpGt2uF&!;HK?r)bfT8D5_qkKm2fS+m2&fC zXaUB@SEH9SYgKyw*?gj5fG1}>Ql)8d?baqcE>MkLxdT#$Pq8-pu#i1NQCmm+RWJ7a z6Qhm1$O9SRRx@iK8e4O0N*v58SL-u1_k|ZPBI$fdR+}ZAGr4vg!{fl0FkIBF*t^rp z3!zGh4XGue1PKGqT;%OMG40+d{3G+;e%f*?6usRC2Z!ZM3@*LOA2Ng6ZlsrrLI0U^ zRPcWgMQ$cvdTeU@osdixHvGUqLG`|xUss$==9tr{d8)gA=UTloNavaG6jYi0?_J>D zU#K(p;_xyg|H>{ol>!EOpSsd9+>?xRBF*~g_Kl#+%9q;$kC%HOm2C8rYl>Y~{d$9n z>yx6McbMw;RzWQYYrXon>?B-z6HpY-2%{GHYM$YRMAJAC zcq1UAf{))SH9L+g5>Bm2gCVu2YlU_?D}+$l=Kk^-}g#{Gl7Hg&(D6B#<| z>%9x5XAr7oxIObOS#FiO2BSv5aAL?CXE|N&(1O8ddzbSywK@C# zb@oFhi}5E1B>!F}{zNrKPCP|0E5jV)KXhred%S%H7XmYJ;x8NG96W{iRn1jDw>3PU zfBD1MCHF#N10nh0rw~&IkTKFfA^Nhi5BFs?b-?dSLB8Mg;>Vu2NPa2?-l__mpMQMLPTqgi8}2dd|A|U@^QFDAzv5y0@4@uh8OwKvjE!k6=@xcMMo`d(5F3Yv zG^X}Sm@r{7RIyKja5EF8QRA4> zK_DLcukDM#SQl<(Sierl_RpR|VNK_HMdum!mb_%A&pnU6OyFYi=O0v5@d7WR-ud1- zJ7D@zyGJ>eI+mP&k1-V`t<8hzxOVK!*;XQj5Gbk%9k;x(_OIBQ8KcCmu(;pEt`PSy z9QBXM%~jFj(TZ=%R3Jqv{5jU@uB1JSG(k~iOz z(P9WlVT01bowwvZMhSplTMWMZ;R3Hu#XOd3EF_T+RNQ7W@Im?r)aomNsQTJdSsBz2 zT;+V`amz!L)C5}^N(O7zx=rAxzx;XK(x!XFNvSIJ+X2=je4i*AOEO)&QSym0SipOP z&jd8Hy1&n)V019D`lh4dv{%5uWxl-g6KGVf8{i{maZjk#Pa@+BdjN-8;fCBde6}Ud z-t;#?dAHZ`3*PM8-n$V5?MgiZST8EB&ln@6uH~)ecY3EVR&gLIC9%`HgXuKrJ-??D z02!Txi{G{mAI={&#&p*Cox!pGlVp%)m49|MSb5pMI0R8j$LU>U_6_)?pZMIPYq8p* zEXr6H7(LVrZnx7@#nu;UP4(<7)e+RQBY1nEN+;QDjC9AAC)D1yTV8dQ7*g>t}q|XX0_piXn%%yV{)`g5H zT%-zv?Zr)=9o};qz@xoQP8A`~=6By`Z@;NNo9eDOMOznRtvko1AoEM784w)k9dl0S z_}Th*d`#HU0IJ#{cgI>j7{90y^6P|S<$`}ufq0?u68-~P5p@|XfHmu4yECoc-rZ-6bw!)`ZFwBz#bIQ3rrS|`qObc}86^3Z%Yy@Kl}CJ`h2KH|ON}lEFwef_x%2aokRQ|g zcN=ZiE;~(gpC9jtw<0DCiG*u)4W6ygCb`-pb39t!|4mM2B zwwpcds~tP)bH~sahpZ({^WFsMwe2jD;P1=tP=s~B$#(gT(1C@qYAYUfVmllu8?wlNkJI{*&5+O6mFHR#H1r*qZYy|xlCAD_wO zrHt8TW4n~!wuj>;LLZfRgJ0}LJ*1~V2KzXYluCVX02`H?*=1e zEf*_N2(r4$N+bywCgrv{r5TQZ)Jcw!MR* zdmAv!XT$0kn~BA}&hcIYu+})j?7v#$$7a{_5X|LtpU2;~m;yTR8~>0Q)KDHU6JIvi zDT)}08+i)vJZyyb?nkNjSFuKoNI&jy0LtW%Zt)4ftt$y6T3MQ3HCC)7t%_DVWG`~R zw;J#2BE!KR2;-|<3XYQ`Zu!e=uol^?uo3kAQX622<1N#S{m%9o#7sn37(Kev+WS>E&4;qww6s{R@;% zkL|8Dq~*4m@+m#YTDcn7Vj`KnJf~X~pT;!CEk?{Bkk`o#V{r9p^kfp;>2akgtO9M< z`N?Gle9aI28eS{a?EdZv*!kVb=YJlg^$LxYXgY&G28cqxVA_)bCYBn<&cLQ}gAh&D z8OYHtov;?;0$CS-wOzBT)_1us;j=AEi}IEiY;b|V2ze$ienFDxh%|qVM_&ZH!cJty z?NDK+PJ^Mi*Lo!Nr%z8IF5CTG_DiuToN74Fx^*7hdKkM?Pgb(m0dJoB#L2wZGC~_2j`{pzX z%s}u$V(@P)>WLQkHhV5X^2_qI20qzO^wceQfH#<2>V-f?SDhT}UMj%z+55SE&~0&f zTvy*{QU-9$->m>!`WD4lh5I=J)&_VlHu|Ljf|RTohHO5FL>)yoQ(?n>%bIj-es5hU zm#VzBbmG{D*RmY!kl?`ASfwX*(lz36Kn>MXaOg_ZJ4WTZRC%)yVOrb(s8Ln zg|hE8^)0GXk@Hmcr{O9g<{d6SHD{T4q*#8fB1BvRc%>Zfz2Ov!)B3u7bs1r{$*Yor zc^%2G#ggrQ&SVU@ryu-~(H0=X*5|X(BY6tUU>B!zJQ%ZmI8vzeFheA=TVHtvQ5h)Y z8HxR@&}mw2p184w$P#CUo{eLN<@R<@1ZZ}nN4-!*47i)6dzAazXZG6Vekd;}(Ri0^ z89Io}#gSNNs&FKyO9Wu;hYNd!2ZUJ|_L&wR9L|gYZ$$6qyLQleAh(c>YXA+nI_tf- z=x=lcS1`?}hu89htEL+G+JVrzP=(61LnLb@DF^TT1~v19wSItPjS^xYw>jgAZH@}N zIysb^j4$>Y=ODnOaGnXrEvr;6mB)MZ;We^nN9m)+J6WvL)sG4$j07}Lz-~zNdOv^v zvgog(*llEG(eKnyD#%$-bIXGj;n4<$7r%B|4S^zP>-kl1&F%ChvJX8@e|wwUTdTML zXYxi*f55*%4qSFSfB*aZM(*;L)jvAnL(qjsqWZPO%iR}0T%ZbZR%qus#fZsn7J9^` z$PyuAc{7}*juZi$*w0iij;xb@iNkq8<9ZVm={<-{HorZjYcLKhek%|u_T4(C&TzwE zXvrrf`~AB`Q*|TX(`YG$l19tntXH`7rJ3=jw!IL#&|;|D2o>NxhvyubvIm(m-Ha2R zv88;LmBTTo-ZgT6!6iQe=WemUF<}qU8H2&Mn>NPatDAlf^DFb4>q|El3S)*gmEOiE zF;zsXTqWtx$wfP?3RWlVgEBwp39u1%cqC3kWbRClNK^vv=M2KYzsa=}uW~=ku{Rjw zlR-L96G6$9g#kVyoMK@wgSB0-=_uKCf9dk;zf@taD8v?>c3jHZJ-MK3(TmXxTz0Q& zXo_veDxiBxDRfK|jJ=aaGg*TlCgM{%FKLD=dQn=he?b9wlccyJt%YCm+lK5dhg<{_ zCTEb**dT^#LWn+B=d0Yi6k%F*9≫FGJqFiJSWzqOHHRV8l%U$R#@bOzx-)w$K$= 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 Ef@s#zga7~l diff --git a/taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_16.png b/taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_16.png deleted file mode 100755 index 225b311de322173d5456cf0ec878c6c5f5bb71e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1183 zcmV;Q1YrA#P)~n;^kfunj1PHeQAP1=ENjm9?}gn zFFYnZv643&-V|=pQ`(a7v2dp;2Nb?`^q9*}4kumyaM)C%5%4w%E6HCR`^9?7%m~ZP z31-p-{pUQ+P3q&>nQ8HDG6Gmc86}LuIKxB;(aWIN5MA`qEi)>__k71sk&{?mtU(cz zWs;=G($W-b4bPkDy@=nlI%uEVwoMS?BN; z)Kz(^3)tR)4%y$h_yq)TXSuOh7p(vQ010qNS#tmY3ljhU3ljkVnw%H_00P5FL_t(I zjeV0%Xk1kohM(`;JGnEN$xJetj)t@vM}%OhM5K#Ssv;CY3Kj&ZAgCKfEs6PlAHO-%>6lE7jx6pg?$g-w|d|6zUQ3p z2teGb9t>|^I%&hkZX&-N%=sg&j~gZ>4#9y2sl5%dlT+#p?OMYW>LyiOba%b}k(+z! zSBYEIgWb95Ur=ih5GMz2f$mqUG^@982L(BjV&utvXtaq&P5KZ`SI#^u-5c*;wXNC1 zxCOk*8BC_KO-KqrQIwV5zjV5DjMdIUxPi&-`_U!1y&F}$%BPsjqxXn+pukG7#0USp z#g;~#-*cLwzh}^1W84t>Wg&-2SN=!bUzcm2)7C9K<1sWiiAWv-zic#UjfgYM{cyc` zjo*6LumZ=5ui_aGaS9N%hmBY@B3|M{(s$fL4Y;y&4hu}Uk8q@P6hRP4Lkwn{A8W*N z=TBmd?#5P>A6CC$DXI|*yg7CpN8J7)6-2=hgS9Zba*6L2j`Pj@du+vBq%LvQshh5NTku`xoENPt%Uq`F{Bvq20nW8D1NEyC41T6!gnQ zMJ4XZ!-c~H(K^-o1vj0g6uq@^4g4}u xdw5%{3esnRo7*6juS>dElZiJzG{yZt-2uixF?oLr)YAX}002ovPDHLkV1l`GCCmT- diff --git a/taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_256.png b/taskchampion/docs/assets/cgi/icon_rounded/icon_rounded_256.png deleted file mode 100755 index 9717c8d704017bc4db9b1b158ae6f80ddffbcb99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52056 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ⓈXVHA3j?O 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

x7=v$!q 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

xESMgA*8LDD8PXO6S|$|%#= zkKTj~N@-XHlDSq^`W=dwrychzEAxf%7d1RB0002(&R?uI5B%KQ&6&IYrJ`mH%P|(a z<3x2=ia>0C;}O;XC^`z$Q&oXvuXPtzHR#Yc$r*=0CKi2$<@n|FRxsuP(oV}5KTRa6 z_$EnPO6MV(1^6X2aXC&=TCS+u9$o%FnLeWrn3)D6)433EO3H(*niG|yU!Tan9Rh`z zDImA&ie)NE`*_W;Qop4=n9&}z2C)7W?`e*H*T34l?wj`A%;RE&rwssnXrB1PXEpcz z?9cG-pMR&;OlCGgyBOKylo=ourN7^a#e;(%YPf=RS*IAlWH)SML`@$*kUl47#+=FMte}2L6hr0+#bE- ztsB?hF}Q)}7b86F0N}&&p%#i)3p5b*DDR6snO0RFm2p}pI{}k1p)ia0|Cjy z7TLZF|5HpsHd5};>V;l|yN9x~<%}U->(CNe}{PHPEJO648`hgJFI z+_6f_C_P&R7AwloOIMUFd@`WD#AiHQp9KIc0&$WLr}s&IrFW6>4LX>52CnBI0@V1b zd%DBl`cuuuZSOsN;^q7G(2E|PF#zzPdGvQ~X&?HvAL8?W@&-lCl!#z6MP5AkUH}kOUWB*-*wFVJYDyyETy^sUGhojJdxhwhlO`JZWN`I zWSSvH$l?t$U@5(!{D$bt^C(GSnP$SEoN7cR30(q;5|ih&07!S&9jMd&>Oz8n_}<<| z5U|cY1J+BJ(Jr72ND_xz(?+XpRyk@^eVe?gPXBw#5d>3dLMB+fONK@Ug@ z>Fuk9h#)=tMFuZPOits2h8a-Yrum{DrzaKnlfsfA3fj#OGfPQa)?7H&jS}Cjs(1{% z7wHYZFWeWu6+1`~gVBQP1-PCA%I!9x$LDx@+b^_7zUjZLpM3qjxCFu_0{}jl`2%Fm zeeA_%`$M<$?#FNE#oxS=sGZo3qrmeCcu~Xd?;a0xVCHBZ_)&xcb0i%Mg0T(2LNbJH zq6O0?>bz$FsRs)rN&1ZktySkD40PZgGiG#$xIlLBMZfHW@mJ*Eqrul>282bWLx5(g zp%v0E(UEH>L!rp1{qg!@`Z9B|Z9V$}2s(=NlkIy@PRiFaP}SK!q=->p`%$dj`aUy# z$z5i8>mN;zeZ@tc$n#XgB?ka`=yva2Gut2fYTo|vt9bUw+tGgEg+#HFlTqNN0=E^o z)c|V{+bx}kHhYMjHZH%ks+w4iiPZOy1j>?=00G+}SadQ=YUI{I#uJR3PwO2T7VCvo zjJ$&Biyr+<5|^|eNnaj60D#PDK!fT5TCWTP>XifUeTVIOAtqEBS@E<61}6sj17oNI zbPLoiNOx%&^pX2{^8CAa`jWf&&{w^Ga_r@gt)k+Y4wpOt@>Q^Ps8k9>A%T4W02y06Ns=EpM8e#41ghUQ#;k!v27wsd?qMMD~=M688A(S zaDJToK6PFe(kWb*804TsktK%McX%{owgU~rT|k_ne8d;AoVv-Jh(hh~`Ih2m`GFXy zmiC>3wblKRa1^)Of^y$V8eEb=>ra9{ghdM=?BIqUj39;=slg` zZyf*(1+)994W57YG|%rpjl~0}xq0X`nn!McnQao5Yv5)LSWbzXHK18zV}Bw0sFZjm z^)r8VA+>j*-w(wp08Vlm&8mD>Xa`B!rSE+ntC&uL;z9KNp|re4meCWmzEx?<^gIw7 z_m`&=uB!!5??Kfwpq_#18ECWz8qGlCUDn6$M}74c*C)3yzIKZy*Kbvazx<111 YABG|t6a2)9oB#j-07*qoM6N<$f(M~n;^kfunj1PHeQAP1=ENjm9?}gn zFFYnZv643&-V|=pQ`(a7v2dp;2Nb?`^q9*}4kumyaM)C%5%4w%E6HCR`^9?7%m~ZP z31-p-{pUQ+P3q&>nQ8HDG6Gmc86}LuIKxB;(aWIN5MA`qEi)>__k71sk&{?mtU(cz zWs;=G($W-b4bPkDy@=nlI%uEVwoMS?BN; z)Kz(^3)tR)4%y$h_yq)TXSuOh7p(vQ010qNS#tmY3ljhU3ljkVnw%H_00%`$L_t(o zg_W0Gj8#_^$A4>|b3bS9oq-uz7+_`uEFef*2(?6OYfYe_(Z<*aS{pGmP2tg|4<;r& zXf!p(n1)(gA8LX$E!tqz+7x0iMy(b`q?nqKPWdRE;j1urn2&qU*?al0&pG!FOs95F zcJ7&Ta@P8qT+`IU@vJJ>>iP6g4p>jKIVu*D+#>{R5`F_F&NQ#pg^=a;Z;AFYM&t z%4pHJDqtdNP%V|X5RD>R&rvMJNRX%c@pT}Qy*GU+ zro8mE{DQ|H4$3P}WB}~s;L7@2j}EAdiwNM7C#}~`alq8yrYIe7)S^j5goeh{`^Tu< zdy!(#GVVa87&$`uLW3zAQ3~>)6|CGu+wzTQtYB-ncns_q8r4u$u!(5{>TEt5{^7GOp4cGq z<$Vv;&piDL|5Ory1#Hw%L}g~LOK`SFT9 zw9kK#`0UR;y+m;1r`8#5TEX((+sC0CIK7X-+#uovLAaE=#PQK% z1arTPm>gNajjKHd>q4A>0|_gMV=_MczblRYa&&>;jlYzEk_1?9xgQb1gcVRtDr!T6 z+R#U@5L7_jtd3@P@7O!EhebbeFg@yUW1W7ylGAD^vFu2xo7g}QaZ~zVt@8^u1K@!i`E=K3F&IP)(n{_$Hm8I_tFn57kxuaD`IiASSInxD<{9jC3g}B9DDD1Hurs> zn@TG%>0H3G-fGN&b9>O1>1%;4#ZB}TKa!+gZg7qROxgvlH-XXmX=?7jgbFzXOO2P$ zJkIdMah$rOJbuvpVu(3I1^Q+nl|21dAiaGH$Q($IE*$B%@yad0}h!L$n9&n3}k!?Jnz69hSqUEJf#CB79SZE$G#yR5$UCEgf6NC?DPB*M0yowGX!aP{Vi$#;Qh zIva>;7zq$UmUiDyJ}5IV@*`q(emIMOT8`KDlNWFZv4R@q-nNY_Dcv$N@mg17G%E$Z z%qB)%P5>HXUa`biws+2bmxbJ6!`H)V>Vlm*&Q+Hrieu#EL{zCiFnwNHdb8#N@$KoA?r zcMfZdH0sFy6dr;mAcQ$>Ygym>1f4;JvN+ns(H#`|%=}%163}#g{UFn9yYmMiiZb1i zT*pW#ZG8hWK5|Q{1#ak<1at0&Qcu>iUS5?-aRqDpp5pT8D@-;{b9480+H#99>48Qq zP0^Wtj@0^j+|YAPMu;M9{YQ;-t~scV=7~F=!XAAD_wI{{L5tuJwTM~@VHY>g{R+MF zAEOv_W7J|aMqC421GE7eC))xJH~eetk%u9gXzHSZbgVigYNw1n_UK=9bm-3J7fk{@ z%ptidpYj@F|o-aZIMDpM4=s5_mlx zc-E`meH`#mjpew1HEweWRP~N}=|_Wmk3TeQk!6(nDvOhC$s%UQ=bGVWaPRYTvBtr!%0Ek1pvSz`rm~B$jAZ$0JH!_S*ia# zvrap`Cb*S*@yKU}#7Cp>%N!33k^S0tyF(s49zu;nHHB*d^JcJ%XupQU?CL}NIWK2KP>lO$AO+SJ* zOk^NdD`ApA5HipVyU&IpnOB;PAz9i6G_xk&e*6}E^O5B1%U)9Z_`7AxK%p=vdz_tHy&0 zRw=;(nQi7IEEdjv{kQMdfKvy`OJ5WjnJm|7Lum<<EV7FM1AKiz z89U;ko-g`N$b@NW%uIp$&v8Y%zsbGcI(i-}oj|5{MeEKecT`RsG` z!plvQtsx*-&T+Ui`HLz%83<|<0&w}?QZ#Tw@n7v^F`tDaX_b*}Ek+0<7$ORLfc$0L z8f&ygl9)5(*r?d(%PIJXNI)<&iawBcr77eS>fTgfauZsml+tp#({f0UDYU;aZ`0S= zVwfHoh$B6xO3a3`X4EhAxjfdJc|8=nnhgnYSA^LM!v5O-fHr(5bsq!7H$XS58NSQu z$qkh<=jmG^3F_|q4SmA|nQR}CpLgItpCR7^gQXOh6tHBqXy>FOT&-0M(9=m0&hYgS zYy(F;ULh5d7Mr#Bt)H6wvHsdyPfxm!UIq6WdRF@gkzDeB3b-G0)ojoCns&E?G%wy& zz4#MN{!_c-nl)ZbS4VuSPe5xc-nzDpm9zkVfvUhmMx5ZrQz#Pi@*;tE>T|HOA?i#3 z_CHb7_tK-efi|u*DVkV&=XfXaZC53+2$eWv0cBEV{+XyM8xqxij0rS3&cMmUx!qWn zOuMos%XBLsZ6tlR!!qRoe~Rr;TiM8N+86PHKkQXwR7w;Fiesz)00EzuQ)RWjYrpHL z5&K)2sSIEIB-VT#P6G-xQsr#$a-rr#8<&2Os5CmRHNQb+>3WKSL2r&cukL<|jvaCk zN1Scr?v%M)c43y|@@WP+(tqIb=XiCtj0}BqsSw^U9ri0mx=3?s$12fGZm$k~7k|rP zkF-e*`TDW%-xjsl zG-9QOUN>fkf>0}^+Ps*vE`(yG7;$URa}j@u@XP`n#NB!jBGjf8;&3?>E`gHwAPhyB z2hmEYhgO;>B@dihb#D2Q*uA0K{hOF*jh9#wpD?U`z#^f3ZS1A{>mJZEZK^p~ zo?hj5)nE4X_p{sU+Gn@trOz=xZU_phwpc70F(Vu1_5NP0P|s-l_k5+LU8^DP!45%8 znq}0`L;Vx5)W{yVU{t*U9kA6cI${6r&jcKnm7`6Ib?V;;)C(vzGK>Tpo(9PIbH6wJ zk5C1L8Ld<34_SlVOsy3AOc!iP-22Iw-=V9UDcU?G{oTb71_c=V*S8O#vV7ycW(A#Z@AP6$<$gEK^Z=F?pJtv04Ls=D!Xblz7$o;br(=nI#E} z0-b2av{zeeZAx%;TjA3jmI?|z%6yQ@sqjcLtr`wLodVNNP zGP8R|3!*-T3U-@N1f>O|#;@9I#q~E=3_gN{pjF_2w5d|CE23e8gE(sh$)^a7HCI!e z29goe8cC+QTp3+=htZKlWfb~)e^pEY{K4FfuSJNJ*x!F8cHSiYPVHObSF53r2&byV z+&ic$A>BDrHTc2Pmt_2q0q_t9{ z`qOls;Y%i;d;g-fLU>hIKrTQ6Xso}*x_2XW&EL`mUmGQ2ra*z`euS61EHR#!EGjo+ z@au|2=f3Oi_p3349rk|A8?bGnXdS+>X0XX)^D8KtkSieQXN8??1ng?iAYh`5N?}@cm40^UdhN^_4fss`9zE0%rM0|-2_fMbzoz+q1 z(6j2<3_VQ5y+HnFtdLy(Yb;rojt>C& z;C-+{j9{$7Y3QMdmU^Wmk z=eap~6SNk>3gL^Kp<$<7zM1$hBNR>`;j9V$8#|9Go zN0MC6<>^w9a3}KQEO+@^m=zQeVwznnpfoGO|50zgO(-MZR?mV=y_tf#0{8Le7KNLJ z5+SJk`ymI0=t-HWwr)qw2S&m=qN;k4Je?@FFMP^`Nv!{#Iyu}arw=B6CWoKc72;D` zh_2aQ;w>+8BqgJT4|n)I#*n6kW7Y&IBVQe&5;`2i9N5Y;G99+# zlnWfTy>IFMU}2=+wQ4h1RVq4Rl?-;gRoF|DRpN^JJ)bQ&&Qb8#5j1RaCvTk%l_;W1 z3=#;^Be$xhO(L0oRA=0zBnsdQ)5A0Xe{iW4-e;+8=ri)p>2zZlNQ_mz2x|Gbk!xvLUX%M8vo1OpWIr+n6;Nq<&9_j-eHbn)UCAwYc z0-w8*d($vM0`znE6v%6cDIs$#)+5J)Au{?&?y}m%VIr`X~-^jx*h+1e#?&Q z-bTZP`k$ddfBEO_e*#=^=lT97vKk4JEuolKrx*jgOH z)IM$4(dHr)`3ZQg&ci^!lXWig2l~qkcK#v~LmdP-19o2>jPzgz1nzshU(5L>0s)Jp zbAGsfDK49lcM&kGslp`DpQ@kC5L0B$$oTn~v|K&)ID*A(p8P%NF+ugqoQ>S95T&RK zjBiqZUo^8!mbQh%qr=Cwuc|7ogiII?r8D3S%wm8-n6v|Slq_7ut;d}Hu4f32byoxh zzpP(vUqq;Wxw3Djizl*j?%T1SvLjW8gPZgcGH6A$=!nZ$(|#F;NcyC{E)$x*S|zdm z#xR|%v{r+l7P^(<*rS6o$uPY5f*+HbxpJHZGKq$hmi>>*1%~BV(i*W6lo_Nra3ULY z0yyeudnw#@;b_ZVg~jHl9%`@GlJp#_DrF=Wt|S{6iKw=_s0Tq$ru;{9EcX_DE@ewZ zuw>s78r>Y)vz(SjW7|*e+8bU}T#knTDV7@$oyHV-p4`B0fk{k6e(zTr0Azb5c)HgI z@7QciK-|F^6M%f?{Q;P^`gxBm;51;$$B;^oEPfE5|F`DQqj-M`+qL_awAf5&j3bQ+{}kQT3#jWz^309 z=pnIw)PKl7IeV$fjz~T{f1?%vGNZWPug5&7vc7yCh#>IY&OQ#5FG<|!duZcDRxz)* z4KYyFi$No8qe<<3vJ%V{13J02TjSGG+bwm&g4!Pmiv%l|z7l_|6R}FS8qgvfkKl+S zkiKxOm&cT%Hq7aaPg)*w63D!^sJF^yWCe+&b9-9*PW~Q@vR=n&{3e>tJ+c=fHoi)1 z*uV=%HJ*@5jOiwdI@W`_jl^O1chc0uYTkt)TRPDCDa)J$9ky?4m= zE-^;KK-2D|3*NKz6bh3U%J!$s2Q!ybHw>vST-y&^yS8Va!hWX5I(l9|C5gq&4qeRc z@Tv>homb+vtT-s1_n(-&{p~|`@6H*~%zMl;&ygjj zM;x@Liw+#2>B#we-ubFXHZ!+kAP7M4>+%pFnvrL1NPqYw?vHhJP((9my*X0wt2V7SF_cIaCUF6|C&&^IOZo~iE`FC z7{w7up!d^rc7c)aAL3i~xlo6z(}0_PO(i>g2^0#_PRu_Sz!_{k^8;(~pK&#(Mew-l zm+$%{J`bl{x`S~7QM|w_k)1Y<1Zh3+r<_$!WF*o-5<28^0U40Uh*d>-k`8;^p|Zmm z)$lb_dmDWuy~7xyC3eX*${H}+eu?t0DA2Q|^Dx zaG3LcT)$X!%*Neq@MHG{UHiS!PtQu~h%7<&NYeuj0KBOzWbEIXVtW4k!P*n*)b#@R za~BXW|b|5Q`wVa9L{C_jw6yCic`wkX;I;4gDEkRmJby7mvXo3yHpIbkOT)KK0idv5I%H_ADJ`%LUa@x@K~sA zcCt$&xlBumDvaKt5q{|1#M(l*fJO)lJV*EV2>(c9a}{qBw_Vp9;9z(i{@;% zLASkn0s{hDMlPmKZJ#K_7-j=>bRt+moCrg)%@i&;qckgg)mm`MZ1k?sS%u@2{M9St zYRYPIb1+HAj^5070p8v1DPjV@qI+g_qM5VK^WwtetKiN-Z>K<~;XXz zFW+7<#nS(j$p>u;yv*E2-1Jw9qKBM5WNJ5ZRxZK0d;94$?@GaHmb#JnUVq8`gZ1>* zPSGMkVzT{76j=NEZBLuJvU-(?X}{VVjnj=3n5yt`xC4du*Al&HVBouzSMq1m$edbo zzw(B00@KE41>mH`MHNF3*7@q$d()oV@`mH5n__>c(nSj0@rdR# zj77Kb~N*Ig@9XA+T2Wy87M-+_ST^R z#)s_1VMOeWKy7gQU4IsqO3bRb?udr`p3C7!(jY*AU>Hdy`+wRpRzED2u{X=L5Wo95 zJgnT8AEmye_v^m(cRP=3cjzfMU@>9xo(1hV!>;~A_I8a&8=0_gd8;eNWt!*{^+Q|?U53t){IJUf)}>x`V;P#xwJ`dI&w#CFV0 z-*wc@SX^x9@}QVszuVJi$dGjULmo%Sb}_uob88s<6rJy!rZe3{(J42v6jg7wJEx)i z{^hiV!yVq|SJQGRywvZ(R2N{CN6r?CGH(?HO%Y%)o?KT??+P%?fnXx1N6| zo=6HQ@EMY;-KWi*WtAfzrkqUmdD>M>l|ACgF{|l~E$6V-PE7{)kJ`_2BE^Ih3X+3k zqCNC#%I(NE3CBl#e@NY2lW)DPPy{=&Y<7z^l_MTR(neOG0I-!Pb`{nHVdh8eXl=4m zh;|qkijml^=cva?*j^U^*E3J@L`3q%YMl%1{va$;)}z8Me4$x&a>YZdqsGaB-`Cm| zvqM7|lvzB_9e8+$UaTvhGhoy!H~?4==`=cr2a(r8_Gjk2-0-{unx*@*AB1GXbTRAx zaoad2F~=U=u{+!S*3|51&9|G^$o#<1_kL~G$AR6vg}I@#fh+AE$NeVkPXLfGC`V9 z{+309eE#c|;L*fg9y3=A5Z$YMe?_V9an4fK)SVQmA!fd5@VaYZM1V&~4pFANkXu{Y z>uYw2;D-FnG}61hp1jhxp=p9G8b02do7aXx|8-Vcqez-Q@KG~jX9~?{H+Gw?{|z632?tWAt1iJ($vf~b813I_m9RhshkRZ4MJWN58!C<|ql ziB=Oy4dWk8Ini+ zo^54Q2?wE$n_mf)N5#}dVNcB~`*POVq=+HB1BoaI^?LYN^9V?GWgct7Qu$P7Qt69# zl>v7|gY3b^BP_M20s~X0@vqs`|0$UDX7h%PR3hWwfNn$)V)BrP^J;F#2|=X2aN-Y; zm*RxZF8sz(zTx>voG zD6ThbfCXmK{nca!YDcULkM*LnVy_>gHKQeqwRn&=IA|2hYPSz)-<$_2&=w&inovAO z!>oV#oY8#xalG6$=8XbB4**LOCdO}%uv~puPWR%`EfSmLK#Ety94*OGgGnle(x_Ab zrN|H{9#y*QBXq8S?+@vVpz8uZ{tY!?gT4d4>w7=N%rpaef&xXID>P#go{0-qWS(}? zhK@chSUJp!qx$tbZGggv-*EU!$*A-YHm>;gwOXE>$2XJ9$ire z)BPeSsyq4H>~=nw3Q~ExRR5_}wP>VA%#@=9?aVLM} z@@@X3SVtRNQWsN$6WQ20f@gi=kNkeJmxPoLZzU-q8KpINDYB(!q$=O;&mlJZsfdE5e@3EelTm?THb@oBfaTb+$~#0+rmSL->5An2{yu+sX@p#6dv9Bd0$8VEbJNva zZ+%c34~ZvL>4sIwy=p0|pyH7~(JKc+cp47f%v(yVG?g?N_aFVcZ=`(k`PD=J*?NFa z+wm~GR04*YKJE+qH~{7p#(_8dTtovtnY3H&ZiSp1Ys+bxYLg=uTbB>9zE%uAegGT8 zy{|}}x%h|^9#D~&+6W~MwSwsLX69Wx4i-?-#GGb}-bNh0#OYgcsgyB)!NXf)@W2EMRWdgqZ0(zTt$jCqpquegpb!pPc=A(R;pqqE2J^`SxRz)V~A!@ z+rA+3j6vejz{T}EtcUR9A4HXQxrcp40DZ7gXFLwujrKV487>Eg|NZg@^%Hx|r?lIBa1qc~0z4?ZbLZQ+xm&z20h%Y+*c98Rt^*TA^zPwNbeAP$DAcERghBl^|b@EY>Vr zi!eh>M3!nDubYJWT+A@i7ysbUIB&i0=BV*N*@j4&vHC;!&qv?2w=qY9+*LPi$4J{b zOI%LKR~Fx)o~Tdnrp-Slm8=U^>ikb)Qr4n|e61b(?(P=b>tyZLW2j3CH%ZLI_s%le zjrFLUzMP&Cb`Op{9h4^t#H}v9lhgh=#9O($g(W6Fesf_m0-Ey`{u*P#Msis6ghUS3kT@WfG~#o68_53n2<56eS0y-Ud9sls>cQ3^}?dsQC`TfC5B3O}9gT6u5SOot-3zoFuj>si2BZuA*A5!h6tObFBqDqKPZ{ zxO%oSmUK;3v$?${8aF7P=5^&?{@7b+b49>byboANTO6ZO5-MXXW*w> zi2KX7F7dS2+H~txJtkmb+5d2Vca-KCY4RZF#gI>^k3ov6VfM1^8y^o;Su&cM^O;0`WZmwqM?5bs6pE}F3O>srop)mnkojz%Z><#U)~#(B zr5Ur9aXP<@!|=-Rd7;^suKC9L>rD&k#ZZeLV6OtfVX~z=EbxeC=}&jdzH8TC^X8ou z&Vu-nUkw!NDAUm z8ZmDtr;DUt`f@DrRjz;-4k(swAB~B!ON1zP!quRcM+Z@IRvsc9{-(Nw8~?d44;UHM z+I@?&#&ReN+KpZodz2g zZkdB1+Ck4G0o}sYB>^ZmXsz1sNW8QbOG^5W#y!W#s5 zn2pbh)H76>JB@~0vD)Ex#QD_7=@=SjZ89JMrPKyublVU3+xr0#xLb3x)o-7`SNr~V ze|vQ$@a}x3l5^exa#vp-@_p`Dx>~#5GP|`Ste6XbL--mIJST+;*5o(qI@JyoW>GFN z=n^!m&k+$M|9=)>6HmaG`{k9~pZgc0^fAHN8+#64rmC8g{8`-kMW28LaWjV53dok8RBm~R;x6iIEE5pHX5vZ24=EP9bg$$ z!j(pK;S-0G&3+$w@@bw=&rDC=a^ez>IQ)>`21 zO$ZmOd%-b-9ltR?cv)hlQ>`YGC%#k9)bgjIR0&R@Jr-Jb*(DS)>dL z;PMo_xCR~ii>o}HoMbDBKWf7*jjck(e*mY|iU56%k*^#9Mz|}P8=pk(f>z*a?{d{g)Dl>+9{$o)>I=IY7PSaX|X8}?7!BAc??ObcZBka(N zyFW5yXeb4+Q`=9@S(2uBKtdSG(MPSU+4$?zTIhb{kx8xH3wh%8p(PN9K@AxG6 zQGs{=o8!Fc5cyzo&3*SUxsJ%W6Myny01l`|nnq^*&eRm2>^02%8uE#WFDTe23?Ssd z`NAH~0)UgZrboaWwxxi6GHvL#Q`dImHA@HVhKn?l0eU5&2Vm@B{KHWd?@4U%VsRM>Fr6O_|N7F~-gW65nkzsV%zVrD2{!4EjK4Ws$ znkqz^?ZZt3YFXFOkgr^U1B3K>SOKRgNIxVAJ|~rZ8KCOKEvcgMCIzz6ih3uW{?ffO zw;yT{9NBl|SUCsu>v^ipx|pWWi`X0Hw}-kORP){c>E*mHjpf+iJk;9`M&A!w5nAQb;VfF>M4&G@+R} zZ15HpI|nMI_D)ah1u1g3Nq}ORE7%8kUZIs4jol=Kr`cX{c}~!5_Li+is{F)A_ddQx z60eOrVkGqE^%Gdi(;jau8TI)+*x=VQ7zuH+Di8sE=0}7_zC<&_D{p(?H2oype^6k^ zq<@$_2;&xMA6EV1q0dRMJ8^$nZ^VHMe`ex`Wh;b=k;FlI@+2B?u6eoLGR|_b zy&X|*KK$Ab`d3UB3lev`Ox90=`=X?SP3!LnOpQ`EOsK9b;6ifK-~5#%d3U)$XXa-e zJ2g`FDG$3$*&}xG!E>9w3B$p$@uF`F*p~HX zBu#UqL+`2&%ku8YyzTtbGcMlUF=GlMk=7@7prb4it&jJ`m~!M{dyoK8Mx!A&2X0(m z{wJ}iOnxgkm;Kr*)ugt3PfIXE?1XOIx8;Y}1(CAeQ7%Bk4O-{KY`!dD+qVQbsrbj) zX1A8?sNb%u3#6(K!myb`Twl|vaaq62AK}2gTm&)=3`->mxn!s=Mx}(`T91@+ecL1rqdGy#zhcOmT z&Ip^z?{vQXeh@R8|;wUS}E^fuN=P(0hl&s_^fDc{-r%`gBlgLwU>YXz57cx2 zh+jkce6dG@AB9<)1GlDYd^~s#bI@d^bFokLdHUYAAAQv>bJmlCJD-RG%*4VD8_Bws zB@0+Y8t?7*+K9%YAjf7Sq|c_BL^h^=`vJwdwjSSi9YU0{w zjIH|Ou>07t|I?ts%rP=sqPqk|g+MvnQ%)VY^V;0tT@Ibm&6IvMBeP>R){Y2r(LVW| zZkpsR`(YDu&4+W&0Nbvnri!ni7pLwi!Ei8iL_tp>d;Lv#+ezWB+J8CmbV$=yYr{E z8&03_Z(?#?f|YCeDxoS_w<7N49r>w#EsB08jv{`K%dmw3BLAgd(-;5^$V(5_dUN-F zyX)&{^)W%Fk(8F>_kT*L*Gw}vA9rXl{cZQl+17w#O}=~7OUd~L(=suN+3ClYc~WMoJ+Ew)UzgbOdd3f}ygzzWA93rO!5RhTAPE!T8L`wt({yfNZI})NFpwBo+3DWdo zez3q&fcWy#N{O(VleriXNRQj6$uL#=sY`4OugNL}J%3yH=KObbb@0!@J_meaIdr-- zCbq_JyggM__CnnQ2QN7+Y$ef_sbL;Qx?EE6`HSw4zwxx9Ll~pY3-RHnJv?v62amp_ z4Pq31gUbmLx2FWrjQc(Nv=NpAO;sY@t@N?<+4jpeAb1$Ba+cA7rA(x5Z!ABe47}R% z8&Tr+h9L9%;Rb%vaHie+MXd%LI93=Z_p~;fCo9q1=!EI5xeouLSYI`dbj`I{UK02? z`R}uO?a9?|cQCe_oelASV-B=`RiT=uTeiuTU3(+`7z>r1)i^%S;)C@ihES4=V&57_ z{o?DDRCf+Q?;6bbv_h3*07SUF{A7`Fj$lwgR^fMy+;LontqlA_aTUNDi{dsMR6dU| zbe|UaT)gY!2e0;d=B12r{)|a5}{j=u6Bldo$K$sM9ck*}t zPuky4Q$vWT_taO`YXcQO> z9W`h7i7wvZ$$__gP%a=&7fr0~!!VH5AoD<&Fvv2aoyjLkY_eqw+-`&m<-#jn3uDQo zNS-x%+N}e_Eo|XxZoMM1dtCLFjV&iJlUgnh*2>j|clwn4%39=$bravWI$F1T|3!qK zu*Y4)lU0nWATGZ}nFTYs@(OqAQNNNNCpkdNnMbDyWF^+ipqVLW^J&>{fa{v(H41_I zbdQ##XZf-l)I5NHZ{+(Dc>m~48i>ZBnZ*YaHTlLeZoM1e&=po1aIe=xOnd9%{621< z?$NRUl0$smx zi8SiJ?d?7~hynk0t9|UBkMQ2Oi+x|@v_4dt1kPc!XEI6#74&o|xJyGm-$KEy6p?1e zPvmh&g_y!u&AU2p~CUxE(16+Gm0 zE2vL=?b*&8ZEh?szTdXOr_N`W?U9M}3p_5B((a39`}{fsGO%9BxW)J(IcQW!PuT~HR@ z)h19*9oT1PgfFN52bLjcnyAkbQ&ae@=x3BVVcu@ulWL^x{K4eP#&^)NR<4nA4ahZ4 zmE-Xv^-GHLETe1jWl zBMEiG;|W<6xL!Y8LV3f2?T1vq)YzT8T?1vx1wBa6I((ewct_+L;S@Dmz&iUN&4Y1u zT^ZZK&crNr&-T=0?+eQhqR#jpsAPzpq3xa)9iE_%V`c5@dQqga1yh-8D@w;S#NTfJ zD`D)vYrKF@)!1qFir?LZawmD3UnTD#s9R*#yGnxj+peEEa zlnV20#EU&lD9-e8KuR$(G@=TXYpU}J!I7^3)U9R9;X2&G!a?VFeaq--gQ;HgkNpGYl(y|8^{_VC`2MAYtyE?KZHj`s|0(jd$@=`n-A{?IYchgr6fl zkqnL>%zgwzAjz7?Z9R_ZROe^Wca+UjcE zb#2O>jnYrbk6o;bE=IuC&q`pzkf-2^iZ#-8z;;{z-lq4zoI$w7HhFtl<8iV0>0rPP|k-rm|tw3&swE}yCSvkbX!!Ao#mDs!3C!r@+W=jAC9 ziN^h8H^nQ7Hq@vtRh~C>p9FN&#H$7mR>O5DpFwyw)-r>xXuFL=WP($2R3XYsCMiKGB9)yrGz=2;`s;fdu@0XwUzn-H4FCtJ4b8 zDP@}!C~W4bUF5ue)Y&&HgI<2p9z^bMjb%}-ly*A9+s(=z{^)>!k5p9Z{ka%=B1A1P zsLSkiyfC?QeZMS&b>1UrdgLzIp!0QpP&ge|aiw*=EGWf@l-9;&@~`gu$5`0g&a>aL ziRYW?78TRe#23E z3;nb63V0GY@QYD?$7#+4ke6g*n(zO)j)LfQGbAy6N%K^U0RxYfursEBa zp>4hsPOpxxVjJ=)66JFaTpszKE#SbM^CbZ#7m>_)a(KuLe#%>BXbT8t7SpE?gkLi` ze*FX@DWRM^_*-j~)G}se;e1*FNRnN1C$$X|3WK{u86HeP3En>c|e9?Ni0t z&Y}(#*Nigdcth(G7#&Cl>i+^7EWE8do4-`!09;T0Y@XbGyv1PYcy)+U{JVsmX6%LC zOq7B<@hmX?`xr8V;LMim#td63p>|(pu=Ri`$3c^}I)zp3{I=S?+zgM@MDp$?cA5ib zYj2&Zk4=rCo1_iPvE%c{Y0WL&o77}6YXq;5Pd$9ajRB6Hy`M}IRpv9uVx2G2K%odg zy$?u!VxHW7Vsw+?lKGA?YIzxmjKBA3fd$CbfV)l9TMHg^ZvK!;21^x2zO|+ys3roK zI^F-}r0Y@Q8RrrTqwza(gm#B|=z1-;!wpKvE0(aM&ov&p?_(fqQ=iHE-t*Nga|$<_ z^-MY6knWoUKQ;mf{$D)turzGng(J!S+u=3Lvr%+IZjoWfIjrYTY>J=lgP-Ly+&vsP zTtA1jhR3z;Z~SL*Hga#h+=_bJ8Ocxl_G5v>_xFWp%rSwxu5{&ygrTQey<;VCIo(|A z?JyY;e*wCIV9cW^LAv4D&6D@$GkWX;kC6ROcseQD70E%;gEh`tB;q7T7s5yK7Ruyk z;q_8ZW-|#Nk}&#CVJV>QK>;V3W2#2Vz@1ijhKYgYaNvm6Gv?uKw>`xr)@lAeqW2l7KVj@Vg|G&hFpUkBGdi8<$o)(1t`Wq=rhu z-kND+oqxcDqFRz1-Tp_f=)td`PdgIZ&U7=@BE>r*LE*PbTC5lS;zN+x>&+eu;e*Fi}!Q@NlO#B9v3q8R}I--}6SXvJO&y~5( zL4OoIvF!sWM6?Fri4~aB0z$+IDMk-@lhjZwR=qO6aS2)pd^PR6OdF1|_i>y#0Ngz6 zMfhG2i02>?Hls3yf`F-|_kw<17i7U6JhV3i^_Rt;+0F{#!;FWi?$hu6&j<;7_aabdf7i)(<1-hSg3wdO({D(^0qfAxV%3FHee8)I)n#WNEDS; z*DAeo9G2mxku5Q*_fP5lN0;x*^A@Dg;?X>Sd_<B6wA>~)$W?R;2 zkU^ftG6OTqlQ^oBi1q|K@Wj|D&fAZm3k`PO=Up;Ht%HHwLS|>=@RDaGKytJRsa2dc zae?yqRBu*liQv<=Q5}kT&U{X~8?QWm4qYUuU!k7yXCbG~%TNgebd=fO1AzU@}e_>eVB7<+05e$mtt3{zFb&{B)!3EmJot`S? z?-G-zv^3z1)SyVjkfZdMwC&{2NgS3(N)R?4ZrthuQ+2k4S)zq|e296lAJvt0G?z`C zt>BpC;x}@zjerq}#GX_W(N>z@Gh_(?B@;_(Muc=2inb$0h2-U-{%6u-iBCe8N#+Uf@W+ap zlWNGzftMwR&FeJA8f4Fi9_YOibNG=6>WqJ4is-l7GekQga?0|T8_AFDD8em@CIsSk zVT5cm+j$=c`S^qehd?#UUB?@Zohr53br4FfB(_p9X?C+fNu=V}f#s6L=C{f5X4pSA zXx~*8GT=lVcGz9w`NRTCj!~hMqvf|*CTmxv#7{Uv=}J(@I-o&0d9e)GpiQ&DZAJN+ z8*hMX%}phBZr8@}-v{QEfEhVY$ioUACu14!4kXOe3^+<*{Qc(!a)9Z!`So&N6H=H! zsk3MP&2n)XMS=J5z)^Wb5&ao+$W(q=)Ibb5(enAJM}00!2eHHX|Lxoq#IIqn`}_}gP(d7N&7^4F?@3zvZyWL3rht$1n;zwj{Zt%_ z>EE1Ns&RQ^tIlVQ>f8T63ozl>)CKRj&b=fzBJ=R6L)|5>1wR!fnE0ML|X{VNAr2{h;TIpTW`ekkyq|y zt;26}nQA=ubP4xpcdLw$4)4t>Mv`=1=_i>JNr;UcU+?_@S~S`rSfA?VzBD`!`);Gv z*3j;RdrbtXubOSelVwJ690GTfwpt-?#bo_+D!bt-&~K+8O!-pG%7)dA=i{c!FRlQo zLc#Urp1=+mr}+36n>#BDCle@J;^a@IpTOl%2jtBwwcAt1=74?wmBF^k_6h*B&0lY& z{Vuz!xb^O8E27hUX8YO8_`;i1VeXffY^&}Z^69(3kjZnQ|9LQf zZMU^|C=v%jpoGxYy=>YugJ>P)kt;8^3W`#CZ{R#0ck*_BB^(siQ|9o4nSGTzV!tX2 z?-S|g4{LOt{w31@>K^8K<2R`(u1WlWceE(1G~NEsh?w92Fe%Jv^i4 zW1wq!;m32WAB9>mM$I~2j!GDDJD3dgurG=@?Ehxt&4xF%it_fKKMfVutx^hari zeiE*u)dk^{dkQw~xU~y9^2pmU_w$Iq)U+$`%%)V@N(M#RJt zuJBii?L2M&BF*5opt2rHlBJEuvCp30Lowc(2Mb%Ce#Y7__iP&$ImpV|w;UXH>MpI} z^{sFD=3qQE$9sOha7AmdExlN6^o=@EOcXo#_uHJd_=t2~yDrlnwieV*U(jjzEMFe# z&pJ_b~7ll9a0G%Pw)a;HCxMRVy)369!PPq00A`HpP#{(PtNcawWkS@$Qtt68px zbhd|X8lBf7SVg$?MSKx&I!reM72h!7jpdm-jDlehI-eCtYjS2UR4)#m+)YdHb>6|t zetyC4P(x-vIP(IudA$+PjHSWm7W=|bo)JSXxHp_R-a2EmMlZ|oiWftUF#wD{_p3l* z;Fs*lt$&XPzW_SdZ|n7HfG1%krbRB!niN)LTaj`p#vMKAY;l1;%=8(oTi{A)6$6??J4B+A>jI;6Y#NKH z6Tou@u35Nm*?!Q6%QHj)Qmy|7T9^X@X9}k8f=q%IksWszjL+{^UTm78j#U{;WT-^m zXgy`U3_h5>g~?GbrlKB2i?p$Sb`7@?PeE(-_qRWvSJj;5<}UAqe1(i>NX3CcxsJip}cYv4bBSJ?L}>}bw}7K%IFl-ZH5F-dp;-P9P8 zN_%=m>qunpO1r!in4?6=l&rbYPysi@&uHDg6H!rthnQPLV>CXp(3uu@+T!Q`6$PcM z^{!pPLR?yu*>ee=`|15|lqzq&S+ z^M49)eHJ1uz9J@M^a8>RpMyH?CYv6@U69YEB|nlglhF4Ks!F`#;A+#h3vGB4Z|@~$ALahrK-$blt6Jn-=k&OUo2en?KuMAz zYAA1kAoY&|fNgDJQVwa7L8yW&3{DJ@ZkT1EwTi!f@P_igo%Lo;Ypa`JJU{opDn zV_oN>Zv->J-DrW;3>NWJgu}76TUAE87DPjK!;jMG{DnB%Vq6`m47$*A;Fx6B~D~ zXYDvoR+uXgiOZy?-{H#Xw8{6P;>G|ic7=OOdYEz;)}ee6PGrb?s`vBHhl~>UtH=kE zfK-fSnc-r*-mO5(%Qe?q^J!7_Cnb`7W08jO>-(tBC$Ams-Vk-SfmuGK^h|h{0^}{8 zV;^)HiMIdLGYyBFxx8$wV1)~fAjC_?~Yq%3x#)y!!NxtB!_)eBy@{zkSys#jl7S zd7<9&p9Z_{tz@pr6<}u5WwGcL-gb$r#8|t}1z$EWO2z6bX;S}NpRtYDKYxL{J;4ZI z#qk_J9j|xM9zoytj*6X;Bzqa&SMSoJP0yR&JXcvq`|;N(O~XhlAn54!(M{Y)b z`rf?j_}CS2;eXG-e(&{8brkF?*7Fn!;e@-?QsWNV|B_pC=D50^649)bCXkZ&e0uWj zb5A)!*kpU(M?H3ns^PnHxS9hMcP_H=E4Wi=DJkcA!rgpjR!TroBE^GzWz9XO<=cUj zYsw(`9q;Xpzw8$CXfXN8uzTHF4BOu8q79Mmx<`Th7vyYJQmKwzv~p|cSAt9({V>tm z9|;ia7M(G%j>nUhntQ!~)E<>IwC0Q!)*8@hOLjy7?AjxZ3F~beF?wV^a!`M^ec|Y zKdw3ts+B|+%&}zq)Pb?tS0@Rcbw=zWQ`!v1WB6ks)km!SBd8{I#c9tGHq*`BgcMhK^*- zM+A!GhJB1Qa|Elex!H;U6OZXvKiKhs>Rm$d=*BaAcONh{bRut0k^?zeg%4yOJB1z= zv&VY-IzG6+55)GAdi!4yuR8dhQ4zAq_s^PF&avpu4~1vH3~~Q0is*wectf&wlDw`V zVaQZ>rO1Cl+aCIo7H)$a5IW@h1%xGy2^>6tBwd6k@UJ1*wL+vk?3>T{0I@0y7=f!4G)FH z-d1(qrcX$Hyy8L%2`D5=2mEaWpfs5I<7_Vb6Ba}j=AplcrRiO`;IZ)F)b$6GjDdvy zGKAflv`@}u?rWEtq`E{O-3@5tGxHk^=Yj9Q(BtbW52fCirW(6XAfeUE-G~M@2Kw!D zyk4Jtk8$XQPhKLNUkIV*a**>qj%8++t4O_Hpu!nJIpJa@b^##DAyliU343K^bg0^-=t?Fj z1A8*QPg0h&ll_Jl+RI52~Iw9s#d(3Z9yq z;4o((5lj?Ynsu<(R~0tY`&JV!PVdbx#o51YY)JaXon)ezWMDz%M!Db1A&wT)601cd ztCA4*wThNnxxSgWnsw$dr{L^D*KzXA!>#)mb)5;mTtq42Y}?t!`NBhSJ|`mbR1kkO zrBqE*LpwAIEhrb+~^~@jn>G4zl@wUJ>e0zL+tMCCppt zsc%#{zq0dix+nO_?Y3J@ex(_Tq6``1nzh`HzbbH}8nCXisly@M`!sfkz|qzL+ntl1 zew?d4AbXpLI;Z=LOMyjgC2b{ko3joDiJlI$w?G%e%rw7P3|EW$T3$)~P_*n5ItCeq zSN!8VUB)3rJ$Ge@m8q4;0e3PBtkfU11?6R28NR{+2WcJ97a^$*p2sf{lY|Qe^f|>4 z8&h)-Ti`0iE}W)6J5|7SzV9e#@~;R zUK-%cx_wv*>eAYn^&6kl>}D@T>WTuWQ~!1clAjiDD}_w0=->db->%JSfe7LdCwBge2Kg$-={-_pTCkjLt1@nEVEs{f5m2@d0Q=T57_wEp2M9I=s3A^&(V)>VQULY*xgTuqFz!wgVfahnhN{o3Yd-l3bbvgqKURh4zoWO&cK0%d zSL%wrF4(D4E#z0Ayh=yd#_FUa!HqjqN7z2^qwSl&%@;=&VcjW|bm(!Ao*$G9%2!Id zn6uXT=qC^n$*pFX<+Bt*q`nzPA@lx_@#pbNx-D9v9&Bzo_8bY>3*U${!3Li^yPi4z zX4dfmh|!>K^W5ylDfY3?IP1+|#lpV%2jJjia`fqUz}mt1&_*o+4Z@FUrfuUz1k$U+ z#JBOJ_-ghL_x^hMd3zX7&RpZ-;&VhI2S#n%Wmn+%*5>h(4wD8GUd7hpic0|y^QYR} zd4|rVmXc1^b(e(=Gs$ZJq<+d;lkTDDydujZUfK;ZGQIY6pDItSHK^Fhj#5QU@~d8g zJsCN{o57VipFNZR9v4Uw-T$b;jH8TXPnPs%Cvup{x8Ap8HsOr7yN$EB1bjzg&ddaa z(5AbbsoKl{UHLvkR7J6$Qo0zSfI_A-Xi|bGL64RSgZ?PQb>rro+=&B^-13f zT6sO32>ragCqB$-KPn%*R|I9ka&hG?h-)lAYgINZ1$ESt7s_j!)0|oLp-f?e>G$6I1z#3hV@Yb9#&yWCaG7+(grvRP=rUu_ zNoIrgaqRkCC%M8*YYsQ16LPOd*rkqA?O|YJ%#NwwKl%JJdf2G&Ut+wSXqV_gkfHTo zyc3}2HpP-NsIIF3RhcxRj>B;-e{viTom&f*OThNXJ_oYsTwNMI!DWz!j%WE{2Lh_u3g6ocs_ra@CT4py9T>N{zgp76BpBDb)2kzh9J|9tH{)~Fc z3HC86cI2_f>Uw&!Il*^v`X3a0u+_=moma#zB+Fd4 zcrZNwyJuwD?kQ$Xf?+ycRBd~)FJar$qLROKgVZ}Mztz|oT6)cRgPx>G;lfO;58#O9x3K@-LSO#JoC5;xtbOqONC$O3(4L6Got@+&Z?on6`*)=Oo)B9AT1U;>8t+!kpI!)7l)iG{3B1>c z@Mc+FheVO(q%bl;SdET@#`C+dTTj5iGN?EX|M~!i)hc#w|GBUUZfB_5i_P;@wA`+3 zL`v}JpSyYqlF3e2Bco!`EaB(3_to!xcsXxAP|067ui}X|>8h5W48@jgMyFl(vb^ja z9a?w|b6}&2&^{tpWurbaVIC%uu{peAe~n%#qg&vY*u6O{N_4d!8<=yTufGSZo&8mJ zIhBFQc|9WjB3-8woGxaGN)p?P)8F3I!5d z@Y6uT_Rj-zny%`LyDK7wj(Ww$U(`0qfQ-vAQ9UR6+$F64mSjIs4o{aa=kHrkn-7w! zLI6<2!swq{W4xVkdTJm}n0M2=#bpz6r;$(693}v^I~iS%eoTnAQ3i#59UM{B#?;!5 z-nHMgQIZM~(^poF6?P{1ppaFn5{~>Hpq5=-gekGI+6b1guriL)Rp^f{%vyLiwD@Ji zzR^Vm9L*>t8{E<;Y6q%+s57JKCv|-1kxnsh`ZWZP$;rLD2wKQRPTUIaLS2_U*C z4Bn^-I1m1KI@@)g@qA0paz5FkVvPrT?0G&3n=EDI!=1B|35;2O7l}qV^SHtkQ>z(V z{Gx>RO^h3#2Pk|9U&=C>L{wDA#R7;}ed$ZUos%uZX~G)9lwob*=8}mK&%Oh5ugt-; z5KyAIVhoe~tyF;w$5SZ!0N!-t{c_C*QE)u5ORQ)fXm!R|cZNBO09n34`s`vCjBotW z^C0#xYejwn(2LH&` zo~gtc;n&pf*QTsTHpl6o0VkS?-PFAHbq|c}wM}5ZB5-%~kGY(*oIb8Cb2Pa!zED<7 zkvB3-42($i-CCVfUqJPmpjdW-Y76*)2lnh_7MhqrDFDre$(`REGjhEixmIc1i-3vz zAX0$oPd7LEpeh75^qzF2eBfulEEJJ~tx`5hLIjUQ-M{%~2co^aE@upYo+mCIDTu2C z)zzN^(`VK9*>v?5m0N(C>AZd2y*~Nr6}L@_KZMb0ZdhuebKqa#UVVewA+{4S&_AmI zDiNzSL=5fc!5PX97?QqpZ^>~OrAuy4kL)BW{S5h|`DH(lZCC)~a3IQJ(%h}>OTg}m zGnrrhTM*YjARk|4CKcJpjx$O4g*aqi>pdoUu;TA_4GXkd^Mnay;}>jIsW4{-LkZa` zol0r5Mmr+k;K1jAHq8H+Vl%o&aI5FLQ8-<{-7XV-%UR}P@?pXnvt4EWL0i?z0y5xF zdD$nqQILR)2&d-d|8bK;`ND;!0j)Uws|v73hn0Vh`8l2l*( zW=Pr{)|!1I!mq2p-lgCIUe4_{f9xi1zG@wqU`9CSx;3d>Jd2C^AtSP#pFJs2vyc)* z`J!coRKUrBo~N=A%YZjNC+1!eCeA3}a9ZfVK&T>w1D6Wz@3FDMaph^gTFD~;5ZAeO;JqB62&+mRj|DgJQs_|Q(5@@VU} zxhCDyWlE_;ycKWE!d6Lc@Y&9q{QlR%_=`W`OcN(bTC@Wk)`E44(T@ZZLEQ7Mf|?eG z{MM`Pw6uS|uC3l8qS>Yp?(aikhRC^VwQs|-A;RX99O602t_Dh(7Q)dP)GxiP(D8cZ z#Yy~tA0EH*S9cyNsHR%V<$JU8tNz9RcZWwJJh@xiLr9d40W0$izpYnOiO#McN z1%cGc*10GQBkr!Q#qP0Ap9NTU)gdpDp}_WMM|t>ezn!aI$vDj~ z7WP^Q4eq*N*??!lI+%S+CE@Vt+TMb+wDo-&6iv}{olXvGoTa9%?^A32gRWt;wqQxf zS2F>{G|4_LCPMGfZ$4<^z>6_%6G_O%qS5Iw!Tq6S+|)gA_(*yn3NYuJ!ogTDEPXS@ zcA0EO&*Z-NRpaM*mzoN_-?uFWD&{45&FWS7$Y8)P;YJDjOvre79&*l_+c z8H64{fA+9+GCMo5@R{hHwsit$gjX*kRrP8aUxHTZdzFRG0Pa#SmdD_$2cL58yxGz* zx0y0&hkSa{pze#kx!KIVFEyK2$Q zuJ7vNNg$kTCtd;7!oXEBFWEs6sr)VpTD>~061bf1zg=}Qu(rG+Hf~9pd@^?8f3o;G z`N12!BfOgW*PKFf>jyFU?yCBm(0!d+5{-;DPCFD$JZa$v5|2iDR;B)A|5>6iPDjkw z=@@`2OdVAmCM@1I9@OEEbQ9^~S$qd~sp+?w1|kWzui*zP_gwozAo&?>Vj%k=uJQER zoh763v=uVGzx_*bOAFLb5RV4#JsFb8<=hh?r^5nsMn9M{jznzhEkF%kr>aDuQN@QD zW3=VkRLyU!gGL9G{^c+h$>(JVC4eJi`rW@v&yDgI$-J8D`N*zM zzK z&f&ZvDjcW`CJe#41L=I%C|4qV|LoF*13B-q>vjR1mm%VT>qY4UoqBZx`6lf!Nis! zp_IvmF%+=93{)VR36r_z%ke?fC&5v{KKl{e!O=if&chj~P#9B%rdX|wDkq8rp}e*x zZ%X@1_zLwYHQK>gdUM^XfFC6Mc&pP9&UdVa2S5@-% z=1DmHTZjf3lZN;AlIZk(1~gyw;#Hw(ocd1iBc=w_k9s-$+jY1w=AVbI^MYcFHm;Cik>}G=Bgvhxop16nwFO>%|}!%wS!48}ng~SKRGnN&i`v`g2g_T+-%v z(I!C-M1)8?j-EDv}-ke%8Q_-*r?Hsb}etTK`+7fxIeOLj9NI( zih`94OraN%CZsBz!&j$AKhsOlP#&3zj@^JIDYREUF-np-1b~${&aNmL2uY|#Ou2Pvyj`F((vDYAvT zs z`8lP8cypFsuiuic+gQ>JOupItsWiswy#BtzIt0W0_j%{m<$3=`UG1@oruD6OtGNQ5 z)Qa=^M(VVjWdZdERU+MSgUlO6?!05N)2)cT`B1I5kDZzmzSbMqUqNywMBL+4P)2~S zQE_Lg;iVL6(r46!I>U++9yU)WHN7@)Mr3zL6I(xk;vRlYuQ>5M%bVhQkI*$Na?i*j z!b6tZuq#>e{q)w_7gw#D6qqo{geh#|^$9sj`Y!urz01SMNUHYg%4k&OQKo9)AB>MO zt96f#J9>$JdK;Cd8(&RP%oCMGa{Cf{=#6IVUSAfqEGQw7d;)axS91`VG%t`ws6xF)D`}iGcs3?bTMap3a3K>V5tDRW04+X_hS!I&teark?q0q@7)49%0=qqg} zc=Wc@yb~>>gy4O$Gx$26o>qM}>zOyH((c2T=JXflBeGS4gBGTwj>g{T><5Kf6a#23?FFW ztXQjfi-3??735kDD4dto!8Awt;@>}Z(LWrR)&4(**p*+J7s!6X$Z;Kg8^F=~^%pMx zUbbs77AWQ0FwU1SZmT3_nH5`=+5={5ER%kSFX|{5*3NhJPi%)CUQ-rPkq24l}dhFV%Gbnb$7KfA7 zTQTY|C8POvP%cg#y#!$UVv_71FEukf&c<^otvjso3jIH#fALySzW^R9*lkW0@qb!$ zOaJ)ux=ncXG^_o`A<<--$@QUk(ioEgflS77dAI-QT!7C{HsSsptkG&JjxxLjzwg|Y zVk(|U59!gAb(zJfMF(&?|B#)ySZ?$U2$Lv>+CjffX(Yt^#%Dk<4Z4!e4;{muo_&Dey=E8Q1W1{Mlbf2X&9zO zg%lOKsG?)lNnt3qw0|Ax+Cl&!WyepnNzI>~)bF@%DM?Ul6h(fC6jrCls8P)(wl#z; zUjI5%j~oK;y(QtRcC|8_=D^)Azd_-N(Zq?nir8f8i>oY&@_MBT$?de1$uA5|ZR8(? zU??KIbs}(~EU#!){LblZ?5IU-Jx5P2RsrR{FW;HCRx*5shLSn-FL{?iDlx<-{|_TCm2`HAaZ zxve)4SIU64jgMmvQNQ0rU#iOSo2E7j&F`IfyM}+dT3$l`$X~Sce1(+odnzS3P3lox zM7P&^{%tJE*SSAvWAQZ=@ENhiy5a~~O}IP73yL;=WNEgz5FSYw3@3&R{(TxG77bSapcy-8yLHKPc|Vha#qsebbZnWvKiHO21@VL>1pM=s!{zPjA#3g|qxf2T_V2 zi$dyO2HJQ|A}&8db<;U=Kd-3y$Dq-K=_e_yewZuL>oBH8Pi#Ak?Y6)8SbG#@hvyVU zkxPAYb4FC-GJ#6ZC0@d6Fw_5ogT#-;d;@}_yE$^a90AZ5cyC|D+*w9m{;@kT``!L6 zc4)zPZZzy*i9EDGJ~McUecnI2bcRu`9v2uDw;JUXq$zZm&&SSx6qMKCT=Z48YX-62 zTVyrxGr^FpQAn0|PM|M>p8D~}%1{(7_F_UNi>yB#tV?L1o49i_ax%n8I)w-4)5b24 z51Du28(lht`}&}syF?w3q&h!QNK6*brnwzR1|z z^XF9%+B8ZZ&wVFAX_~BV^((LV>_rdhI-l?+6MquQUW)BU;CXAe{$=xl7|QGYBn*LP zSJ`ljmScr?j5P7S&LMQI9F={_$XClbs|yqK*`MexAK8zHyr5bJ$sW)o4*lTqU~BQ-&aCMH*GCZ>F`Y)iDs^pSzi%Jz4xFY5B0m`VbzYKHy4m186$K{Nbeph^d4_Rs zF3_bbs-FpyCro zI>iPUk}}Zbj?X7$^S!q&Se&f5OusA8G>qhvp*n8?I60MC{Pq#w%J}Ua=YCL2W-1px z`^bjZ3zp#)qyoL<*QOW|$Sd|q>FZPCLd_~Gf*{hkD-dt}ExzO5(lr%um^1#1&8VNl zBcGH$(KY0*b1jqCifR(@-|~2_EoY4ES>l z2L5k_dHC#0sCaQj0dF>FTNoo}EvBD=G*yxz+@N|^?aLYSCI54|fP2$-zOyG;)NqS) zS@+YZKwNz}eJJPAAh2gloC#P)|$h=zNu-X}64s;vPXEEhgwt+}7 zF0OB9`52iBI=j1-)yL!|{Swt4zob3zUG+o355G5p-0sfV+O{AG$m!FaFEG50)PP%h zz%BeyZ3;jJ=7e%U!En0DSQCY%Kckt&o+fiWwy}c9SQbc2PBK0A7s7?pBq5DcyR?<;YtBXO9{ld&- zd7#M2ZQk|t-#S%=OYGxmB)p4m%z?7E2LMw61@bks?Q*&V6TnN4*w}YC4-7m)4FaGkp7Gb=vy?UdwE(4C3u5aD z)pHEK3 z09+mSNyZ#~ZsMM>z~eN}@5JhNn;<@11Wu#&+%sZtYI3}HwwvnQB(l)pp@>nyD3lpY z(f!aRgo-I_U7?49Q1YmB8P`8eW--{=9Z*6JEavBOJn*I$raqYV$Z_JUT!d|#sDi^& zTq3@3*Xi@%l5l+WYQd{-#PAa7-M#xu;$ig@*wnbaa{<@ulIMKk9p-h1QO{0>DyCc- zZ4N2%*z)dV+cKM&25%d^Vg8*riC-!OxExWLi%?9-X)CiuT>xbD_L9dW|8S!oxmu8C z#g9As5onaUvyf)<0T_V-OO)`xI|K0p6Mr*lBK^@$XGp|ppd(dU4t!{WQW#8*uOEm9 z8R+3(vWvUNGP)?u1m;e5!P%qZCD$Gu-d!Hq$OFI!zc&*zwlS$Uz1vxKnk>L66{B>QjVHQ^xm?-z7vuvr;-45RFxC&kjB7d`|OlI+R|Ye8<%hh&I=Ht~Sk z`VIWxF8PS)f0*2%7vZL8WuP5(m|4#buJ#LUeTvurVc9!`n4|M!mV}Z#$AVnolkgCxFuS5U_21PeGNEDY^G(K{CnqA%)5%dYI16 zG(7&ut?Hen%#WQ*c<*~OA{6M=nU59ZeC)}4Zn{6jPopZ(#xbrA?RNj!2TRr6jxC6= zk6M}86vjsn|8I^bG=5JzmZXXX5`hT%**A7bt(#_3hsgF>EK=wCK%n-f8x{3PNx|0Y7DC4cn&R8T*VQu?OT>PLFy z>b7g#cey?rX9ti>6n=Q;5$v`P2JUE>_)Hm!+tXj0-{@_gf!QQmIOAm0FaRYX4$J<( zF`Tg#W%!Tbh1hQig*^4!lnJ(8D)FQprw|fcO%8Vc8{7s75N3SCSo?7NSgzc~RRFY{ za#nh4cGu+BA_{qr*Ulow1ozE|i!yKIuro4-PquN>{cRSPV78 z=$FW{A{9hM2(Y#>E?hw0>~D_kW%I9=Z(^-quJO;2MkTezXd|dU759}*;K9xw@_s8! z>&Ef82P1N}4`9;%+;uba^kBEbgjY~jwT7IWXMQB_3b{$~xZB|Pn3}`s=3sWm*ozYn z+M~aAq_Ptw&(|blM(|sX>0K6|`AL{v<^^wDTK9eN;V%oSC&i;%FZv9jr6O29SoY|r z15_!-Fl2cj72#k~=34_^CfUzQ$A6t6zQ)z>p+GMq1bETgyTC_iA>3$x07XD@d?-Gg zCP@hOu?J+K6`}Qkm9VCIo2lzzbViM4$0SGOsSfKg9m{o@!c)w2wt>#|Rryg=?aJ}5 zEe(WH2<2`%-#>51jVW(0GV=R|uGe5tYF1sqOamrGzuXUftijPZ^JLF#7+v`e`3qEb zMQixk^&Ss~UEIX~9=mKtHk%&oBHvWgvy;ughxrXT&oXUxzA6Hvhl*5|8!InuWiSxK zQlVYn1%8mcA6m#-)vNjG)zpkjkiW|)eGo}1v9j#J%|&F2Bye@@c7u_y8%ng)rnzIW zxYomdDIuB)sfnY?D=Xu4_?@KB0)ydeuyHrUf6bnw8GnG9;l5kZcJI7^$u#wt)-B@u zw#d8m4ZF?@Q3i-KKd!^X({2OGD#Sldug@P&2JqC$en%(HNIraQL)I1yOFg)ESs=N` zp{&+$q?Mq==ZoA`r3KPslU6%F63axc5ewvhECpn%2L#!8wkBphIBa!W;{*PM`SWe^ zsoDW*l2uE2m-GeSBLsOMOC``yXzhDp67MAkx30XQW4@3z8f{tNg%~W09q*EB~dH;Ktep6A>R36>KMjJjvp&>wO* zYaB)}Zw9j(jNi3$~nYhXE-N+oF#|C^-e)mm^@)#zgfW+b#VkH+QtmHV)Y zCd2|0T?4AV+QNo##bZ~RC&X4%S#>yD+x)>~$9iU`g?D48(yKhasjx46`TYtGai4u)nkMj-|V`TJL3y+(t#L3AIx&qWI zy){zp-YL^yqyTPJX_KIzVZdyo${lRlSvL*VXy1VkiOQLP~xlEv5RCp`kp5( zFstm2qbrX}KmTNeAIXA$`in(g9xMDGO<%zk1;ceaK@HvAARyA9G&2GMN(w5{NOuU* z%naQPf`Wvglmeo33=K+mHxkla6PM?`>-!6PowN4Iy|WD;E5g2XQU#glsd7VI1kX&G z%<{d~&1!`?`&=($)0tLw7D#Xqla}iSOcnk>79H+jzY`EO|H(3g(R?fLQVA7pV2P}H z_u+`l;I!=|DGSeIu6w{7&cWy)p1WY?p6#<*|+? zId%#s8iXr;`e1XUP9(lsQbA1P=^MW0$-8(DtMtH+r&*-4lV^dV60QE+Ez#gW^*cIS zB_NlQiEJ^L2-7WU{Tjy?*?$T6E4PxEFMM{0=WIGdH)A%z7^xA|`6^?X`iL1AW#9Z$ z@T9&2!aU`M^bM0K^9bQ?x6iA8#Q8?Yt00-?pO#Y|Yjw^WqQLX$E(J%g_tS>G!Z$P@ zsZa)yyZm*azEOJ6uU8I}_iY@L@IRv`@|F<&Nr>NcEe#S1ly>shpG2|6UlGfBg3n;D zK;?!k^>K}%clx=wFYDNFma25X6`Wv=<02ZpqA&L${w2%)>QS;YDRoBb?a)O;hu09P z7>IpMB11=Cu{~~`##7c1s(nRMsAv_DMEq-S>V^Sp&%3mRviO^}j$+tEHZI$sbo<5`Vk|cU*QT zG(X9n2Yy>I?p&x}p+(*EXgYIu$V~8Us-Bn7Vcm%edu|D

@mr5+EA^8M3lAom#7L$zsZ+Z`^EoI6P2AH))@;AbMKtg9ib$UaO)tg>8q zDV#vxG7Bi$fA?#w^qP>6YO=Z^L z8YSuS@&Rc%A`*V;%=idL{1wqn%nFYiCE;?5%QvGN`N(5|jDJVOkOjP-^NMqh6wgEy z-t^#&kOF0!86Ur@)LE?l$MyiWR{IWi%7`Z-D+vov0^7yx87apT9Alm;5pqfUvf5}- zOAq4tnE(=eWZsb?0cCZV4Xg?LtlyJkwN-p(wh46;HL+=dHZq!jfbX!h@ZhC;Lpldh zOFvDmDtFP_^JH=6*jmQ3M|<5d9n`_2Z9dUTY>n5T?UOW0#ceUr;N@OeW1c1!)NsZ@ zdJ~QKMVInWLDAFQ$`?tqBv$w>5JTJcOb zlz4BkeEFaE6uYH)0+FgWL%ax^R2*%}WhIT2z#D;}+|uu>Hhb30TXq8(~%t6wgEc5UHY|=#~fK!a$)e$FN z4#RNjG90)c&(97f*RBh38D=hNUw5(N-A;^E*M=zw4kh4$52g}*h?ctg@QSV;Nf0S1 z&5|en?g-Rrt6bwy`N6(V^I+LDMS3LXN7D)Cj-)ZwL(%pZ_g$U`Qg_bECtbJ~?B2 z==9{zB>vja_!V#?JWHM&l+F0*i)}cYkeR?o7Dv72UD=7LjQm4#KfI=|gB+W~mfbh$@dzua6bgBV7j8NNtShUVXLA2a{`e4c*AzvAsAp~fJ` zspRE+d;euT{}Ngz+8yE6?VeYd&dvm)Kg_U(Q+MM1@D2hCTKIL;%|Au=z{Zj079eF6 z`uE!;+EXmKaF&UaxcvUQ>K=WyGZLcb_3lsj{ua|?N5_^k(nw()xjMQ-icwLIZJ@EX zrXTjnHdoK-_>%h+X{JgWnx_YR!@`;}R4xI}3^T-2#2F@{Twbd-tG z@M#*X0WC!vSNHg`ZX_4W*!VLq%59jlh{xqg(qC)En|J#nu|NH5KpGB=C%*JEX&_4O zMk)#^?o>-u#@;P!7IEa;FgruWaz)EBw5scH%sZv@943#@2Mjm$6VMYO`KQxF*nf7* zrXY!8mD)U?===9>u!}Vz09q;a^5KGvkmT&U8>2tdfU8{3eVYA-OjSS?9BXFL5PW`% zGkiEn%8;76HKfq^2Snh`gp9h1h8Fa^%|=_(*3idGSM=z&Y58^iP~g?pT# z3`P%L$Spj1RgxpYw{^yK%%8URlSf>H^rMxIwO}z5%uy3n5=$ClvOgO_8k3IV1|<*5ftDsBW~S z24@anits7wr#;A+tvA3q zpclCeY1S0iSPdS>{ghPwR3t9Mu47z7EjSAL$=}L8SyS0|@b^&N_hOjy85?8Bjb+?s za#gD>{?qk|*$t|nkC0Rsp^qOYzW4j1GkPJoycGq}g4NUTY3zs(Rk>|-vBEy*QfPsm z$Wf_H^qxNQXzbW#gsS!Z?GhV+xOb9#)qpBIKlK53e0|Ub>hyr{kmg%luUlQUh~gb3 zd1QL%X1N9K_F~T`373zZpE&BhfccK*FREFSqYgzQX-nFcWCelaf+(}3xIKmMDVh5D$_kh6@UDsr5ICc5v zS9~jlM!hZR&TBY6%pP-?&^&>ke8>ho#G_sBpQ?xhD4BWL98P9MY5qpYcDPgR!BzcI=Y)+a7j~y=RhfTp4U}%6}wW!&wOE| z><&R;cvfTJXM;&8bhwwQxY+CZ)RAQ}Ev2656SKI$FvwLQ(}6OK1F{IGjrbS{KV2qG z%CjM@@Y9J_Op+8@!rmhdk*4$>e$O~;c9qGl1BxLR?+6<~ zymK2G%Q7^UuMs)coW8tOB}B?o0k2!vX_9g=6iZR^;Z0VJ2WxrDcf65@^LT!d@eNS( zP93-M1tEGJSLIF3b{9AnN_rpyh9xR@QQ!nEC(H&tY~zHgpu?y9p63Vnl|IV%CPrIQI1rJ^(4<>ebmY? z9^WJpz|H^h^O|2P_fjqwN8`*cZw@0UTM4ZEb&V|CyROcsa;24-*&%cYvQ_puuVwAl ze{1(Uu^bFJo6I!Vp1D_AF#;}#nBq6_uny9Vc;u*9WB0)nMPbYNI;Sk`StSO~r&#jL ztXYVhAf!S=%^Ix;1{B_37!s11KK3-4CR%;XRg8cAG{lsK!=X}DsX68>&jnx1RYf^$ zp&QEA-0P`(flE=Oxjn~3u+1-efQ)Eto`WXyZnAIPu}kh9Y#nBkHLEV7|@if}QDaVI%?^9;_L&!9SY>j>b}-RyJTL`h^4yZC>#=T&I|qZ^yU6!rgF{z$@09 zSSH|la}rf@b?9?NQb(e57K^}>4k-4)-L(CU{Nm?8%e(K!}ot}NgLEE)O`myk>}%o+a$@oZ}%Q*md4P(w!A>fM*XN9+Q@*}gvRe&ZKC$)?bd{b6pzj?Y$dc zO9F?r`Ha(jYw@xc{EtB32RrD{A$ z0B_u<7#X*mdz^8K>r{GNo$Cu7BC{fPU6CX?=;=TUGHVB`r7rrmt=0Kn9ZI%5?zr`w z8_Gwx`3SLc-@4{U2hwX)hkk0C7Bzh z6tar;jwoAg!<8&vFpG^ioXf)gEY0!$eu$AA9mKMdK6D$v>!S+TnZPIqWS}x{#=X$v z;W!;t9C0W@-3kHK*t5B) z!8x^hW&^gJCP=lEcIaw|YkopctfrX{P(9PT*5I43c+KQufq^sDgxn{p9(k=$+kWut zaota?u&F8&qAunq9mF1RektmbQ3t+?ur!PQWWD#3hr_4)$dpPZnnr8K4A!mHchv>I z-a&|4Ems`rr8T5xMcZ;XY(b^K@Z>hYu5+71^lBZ7m0|#3icS;daqPQ2;RtwE&%y0n z@}_}z^IX)u{!oFZ%ZJi*DZcy}@3Bx;4Dd}&VssEvx&rHqzcJ8LlG>K>LCRfhB3cW! zS8HNkRK+sHRaC2w0;#@yY-kZvfK|xU<^2c0U(R`*_VX;t@G{A5DLeV*uzTk4Stw2{ z3n?ElEn5)$N%}D1C|RYAqcSrlFLlY_hbTOIX;Alf)0-WAKCYmGZ{d(gWIxQHODO*d z<0$jQe)GW!kTFz!{9Zxms5H;m7hww3((XSkw~zzKwjXy2p)YwbE!BAm+g67)PrN zb2e$DyIJswsDqvkdbpA}c&jGq_i&Cx#)NEo^F40zUFVOPY{#>*Qg+qP{rmMw==D6% z4v*t>R{Vh~nX7Rx0KJs?sriUx*&{<2DK7!Qbh`&NP`2%*I*0G8$UR9(f5tce@ey}ULFwXp44=GZ?Chn~-6QeK7UTO7-C-&}SA z7TiBZ<(*r98Zeau%q$ zWX|7lXT4U{Hm6xBWaBQzCACN=)CkKDb+#HiA9@|M9G)+@R5wks-Xs66M{`f2KwzHv zn+!WGXDTTnFtv}!l8Yde(G$=K4d)C)awezj=yIlVf6;ia`t*fVc&+Nk_q5vS_o`*3 zvYjpzblu^HOMyd63ZfH!g z8{3r`)VP$h3wVB1vdV|P#~?HqnQS@$0#Wun?C~bmfv^rOYIm+5f{xt9ROFeE z`w_#RWKO$r8-eSBhOI3#bYf3DPy69GGBIszeF-_fP+=j=6-fOtbBx)}FaJXP+T?!g z`HqY1&)8Gyo-R{NJXPN`og`N~W8e3~Ol2mG_>{VtA}`r0UEXl^{Db|O1DuN3D}dv# zaySSpc)s-xD`0J^e)!Xq&)1ynW-=68!E_`cxlYGFi0{D=P%~z+Q77DH`jU(0R%(*G zUQ{#XX4s-Y(6x*T=Cxm(M9jFF__-%f0t}+-%7D@tA`0<_@Tz zIDWq-d_Wh5Oh>h+dwf$mA9)Df~KV% z#L|9igr&v-xm3s+7nVm*8}emAN*>#ezHp;Fs9t8oc~@H&W#la!Fg*as!7U!DvtCR9 zJOWwUa=;W+mITO2Y1NXT?E>)hop`xpFa*Ho{6GLKZA5c?ozA8Am1|P8sJfy^L0~_h z;XJen`UWxEam{=+A7{mD$IEJ+Cc^0*J_8g7vMwhKVv}GYIRe~Wbf3SS65iEQgl%r6 zI42~|_;FrEjt%f4g8qht7iFUG@hAOezfnK&hOpJFqB|qL$i0byC;ddfK7(f`3e^z2 z->0-Y``#$kL0eoHXxU@QPkzC_h)|?lY z&`s{ppO7jwqAHdWLIT!PP8A-5V(!{s<31%%zI=6$O5kJm@O1?74pdb>_?S0RpHfttsrz39dRQ09QXgF#AXXvb_*!@HPGx1!sbBX4+K@EQOfChGk!) zoZ3Z_Fa|vOt!BDSX)DZ8o$%nLpZdh#m+I+f1ASq?|9ZY*uOBJ~3-62OWVVvxiqlw> zj<^U~U$mhJWzg?jDByZMf}G(F1aR>RZE*e+j((&#^@goYR|*)gY+-Q_wE0owbkA9t z#Oue11^vhDzrMd$4bCy-GpIdOEPnpi0Uvgi+i8tSmqH(N|5?MlI3Szlu$D|%zrwmO z>`td)Ar*j}Bs+ZKH=eY<#)D&1%K^fO7LjRnk|M43L;Zl9ghk4f4@dxImptqtRUiEc zUh0fg)$P~n%5b$&yzTS{qTK^5IW}&vir-El&10l~EMbn!)qm~A-_yjZvpsE^PrA3$ zMpFBCFZ&{9r_|2!fWHzO6so>f zWA+0wI`-m4n$grV{666mI1ezEP1%&kCopXb^aI;aJ1D-gE>6|7X@ob8{eyX9 zZJH(D&#yw#j#`q63@5i!zWPgPe>XddsN2r~VbqlNhJu;XrN1LS&U;69gm7 zh$6A`UzXdAt}5d;I0D7bcJ+kN_EWc8Q#e=HYOdL4&StJf484cBLjg+QBM8^rY|(%7 z8?%hdZpt&g?-fT>Ve@IYeu{J^@m=yYyT5NM;D@!cIe|`cAZN(wXDYEBdI5b`+aLRc zyvXKq&J#4#8LOAx~ zy|~s1dGgF{!mga&-W2+5PV4u+&|VFmQhEz}f4xKY!aIDp_G&Bp^b-^F0`eTSqb$mv zVH_I(r=IPe3#>odIebMPLBvS-UDf<8r|*)@MHxHATw+scDF2I8Y2wX?NOw(A3BJf) zI3R7k|uzv98JUJa5J(jQ6y?a*@C1@`_?B6w}5)l-i&%S_`{4qpv`?m)XQj0 zIOxfzieBE<4FK_#0W94*-(f4@EDp4$STSBgnE^3bJORva<|@)~lJOA}VO(Xdzak>z zbg_&*owoq)@GOs$3u{xqH~TjoNI$}oNKQSZ8mWXX;2jkyWm8p8{&PT22Y%py6W-EP z++d$10ol?Ab<>llzdOLX4jnILLJVub-wr}#AFBs4jy*Yg1U!y8R5+jDZ!|f?F10hr z_XFY?aOhxKCN8nDuR5uO$)?DHwS4#`$f;uW zprBzZ=>T2!1%rRYV7%?Doe9Rt668l-P2_y}jWJal#2c9W_BuW5iES?48I3-(c%ub* zdRmA~BnU08>Rr&U{-;+%+(!K*gn#oRFjJi@e zOh_kLs-M9zpNp0dAO{G%i($r|+5weh$GhWmMEQVLgM_Nh%PGvhrliouNox5Q0yly? zfAkFcf@`k~3di*CLE0z$9rf5@`!=X1_J>K_vBj&~>6JsC+kza6SN{+VZ73vGnFKTc z`9TOS!v=2VD7tcJxJ+`y5VPEW%e(b~nqR?d^L~z^LR_oK8@lP&-E=oq%Bo#iYA(IN zbs5fMhkUj^M^HF$TRBSUV%yq%It9f2q!*781cW%jwkKLLs2E_ZZ5p3dD0h;;FbM61 z$C@^dM?~Sw!kRS~576b-)jkLo`LzG6DnC8`7yvFH(Q)#5#|)DcGXCyto}mL&Xq1vP zwnzC@u_*uFEdcG}vr}`YX0>Iqyt(QKHoX|<^C$ckzg)BaeS2A zT=`|*EN|bY(slOPHu&Z>5@1>{IlM3cH|Gl< z02Ad2n^P8m3sjwLsl4sh5hIO}5mC5F5+fG{T6VPvUl4SRMhJL<0H5*nb){bZ<&UA> zZdccdVYD@roj862duZ3-L-j}TuzhN-a7s^oXoP?JHXrRfaI^A@xEIm~Y3# z_+ie`E}wD2d)KgzdntxZevTH*uUznqvhZujg##GPtmlrH!U0C7{6$y6GY7$yXZI_w z%jcjbH{}^9Fhca)aOIG|ws-;sz$ufMxo>>Im4k5?IN_@#fk=4w3PqptABNlp4%BHM zs$(k`MG=*)$w^LR%3Wvou32#Vi&@uZKS~>18@1(mQ0nD(wIjNEdE~kG{nGPxu(H!k z8o{;{u-j5!iegd?mmzRbj?!Kr0$0674G>icvB0E2Mt=9+kfcNU3O|=LWk=po4Wl2gNY4S0xu1K)cSGt)OKDjQX8NeKHi>sZR-LtG##nH+lS z+jJg%C(mN>+BPwPNZhVQcj8l)a!FjDT&})%KTy`qnc>Jp2q2vt*;(=J+5Q+gUcCd$ z8ToGS%C7Zv(cH}NpJeYCCiV55=1=Yv;fYABeAOcEcI-XtIMm0vt3LA{uHI)wGzHV@ zw1Og8U>LD*C@`cp6TS*`MCBwUoIyc8-xAQdau{0pZa*qQ7kcXMakKq`;59iB(X|69 z0APz(=F{jUC=#5?T#JW9CW2ZI-nsYqE?~?Teq1&V5b3GLQw4ATcyD{B#?aGfQb5rn zOosPH5tL3fdh(_p?5{TP19Z|665H}SIdeREzZ=@w-HRZleMF6j@bGh@n75W#Am*|e z_>7$CwrX4Yc*rt}{^CGbX6eOVh?hs3UB;*+m9~VBlC)F#H8Reyj?^V%T217e2`I=A z{|YowiW^bys_O7thx+#lSK^X5Mn3a1BagRJE+ev7nicm<)lVOAAzU6e@8>&ThC6?F zJ}vnNmVB7`)-aLhh;a${30=*r~k?py|3(dKY4vFBCN5Dat9}55!t9no`c1k&-UJaLU z<(Uo>`t}j~bknNA!@c5@^__5c|W zv&P)rp;X^szO`Tt*a9f^Gm370TB)Am)I3T76R0YjkFEP1Oc>Pv?U_FozHBCRZ`onf z$PxQWrNVp{Njym!&4h{Z?20fdp<2CJuY_b)nia)C2`J}5hP6aV89fwv1fT`ZKOBkMidZf^((WYE&2^OD+#AvJ_%1;`QJ7b25>l^i zJpdtXWj4+<(!q{1 zSpCer?B}nAIF$Xau#@opTM-lgCIW-Jm*9!`T2J4q=#ZOLk7xc~d5P6$%av7%SI@<6 z1}F^D$%4C9bWJfQkxJAnTd^xB@Y(+&bvL(F;)p}*WI9IJv%`G>w|iAX2#xI= zr5f=ZpoS9JyEjA^(wG!wNsJJJx{>)TjlW=<_W?8Dj*m!rId6bp=ZaVS{y4aYit#a# z2BS>AN(aeSZZ8Vk;l4I}cm--b)JBlpdRlOwCNTPUWl`&k&Lz|<6DEk6WC0ed>wbM_ zWUCej9?1S}-kpb2u74S}4qT-GTHRFwOONlU3s95VR}pewbCDIs<;>K#o(TK~7Dgpj zMuwh~fr^L_3#)ykqKD_NPN>}0l<14V_m)v&?30rBcda2l!) zRKTXl@aP4V^5lQ9TXNufqQHt#@(F%;x}bmEEx~J`ELqaax1&UUQ@?ATfU$m&?dN0{ zIt?WqyvLElVIayT1%gssQ}q|o5an;YILhjFi@#fl*@rlKba2#fvmvss|Iz=mdv12S zuUI_%k1q8Q=BI`?Ydt5!%CS`9`ceEAet0NvZ0FTM z9Y%N|*h4q*VKQ$IAc@|OhzhptLQV>xel8yBs|Dap?tEM!A!x5a&Z)Y$e7Z z$?zEOOq$g?@K@4vpPqi6Pxq`-Wq{rBgT&*Rc;L8KBH8VfVDDw0NjLDLNRRl?%X422 zbyuw!5b90EF9wnC6XlUnf70>J7_W)^aS@X4O6LUG86%|JybX zc;A>3wVJnv`qt5p;b6SOp!KHX1No)wFw);i4~8E^t9wrvJil)8uX+PIk$gv$wv!D~ z_N5^|xaqCGnt=`qB0%JB@*NQj7pmkNE)+0o9?lXYzXN;orY+<8?{oRnE;;%U+T^%c zl7fwl%~#JoG1BXY(hGRthcC1Qifu0}O=3A?WuKk&t)#^C*xzjxhLMp#jcohFmke#I zGOr)QPdUaNnt;{c>4WVtry~ndnj{l~RG2Go%i!QCJ=#pztpm#rhjwmRM~?XyEV?z0 z0fv38c1KgBST@+Poy!GQ(x1s~5&BK*hTo~<7CzaI0AeHP1MZDyOk=i+u=HrcDuZiD zFzP14qDP(m>IokCYtJY;xo;&Zyd6L5^15qTUMzs|Ayf3H)J2&$KdCdTCz1%llkZK& zM4Cict0ne!Iwf`!1%_iP58Kt*$T^s*6q#+Dy?e5#l}@)g)@Og}uH7!7>pVZpL)g6) z(lxL={9;c}jZKk&NBGG?e;&wC+c*Ey4Pu$9!*C{A+&rNqnvlG3XZGCD`sJG<6UZ=< zWm91XHbYgo6-v-7v0GM2(|R+$WI{h=2XFK{RA*+cBzj!8NbTdB)3yG7Trvxx6u=*M z@BnpOowZOBw0%aqh%TWrYwee#lL>jqHsl_JfoXRUl{%LI?W|)X-u2irZ}R~6KPB-* z=j+b@vKX0f+9m18V>b)BjV~qsLYb_4JmsP&C9a|v#r7}^mUI0abnoalrOR>mY7rxB z80I#MHbg-LR)6fp+VXc`78utRpLa8!*Ch%+W}uX{OkgMc)d7FKicW~~TN^T%xnM%y zr&i81TU6$txnMgK!Ljxp0jJcFo^k7|j8XF{tZt&K4l*5f3sx)bLL7S>J1=s5 z$)z0GYzV?_bq0r{K^F&Cx6!+GwxY-ss%e(9z!Rk`;SF)gEeyFnkg@h05g2kly#D?~ zXii8r6(sew)eVjV(-S;O!K>cz(t!r!lwe~w5 z@iP}GsFJ!|Q_|`a;9we<<+hoYdkiex%6mUo^Hi@M%wG>hT$=q4T8WXI#^t=>Fu3zh z<%%w2FBNW{1C0m29bHzAA`jg5M6x^oJ04&w_QY;*A%*@%h}ax4tRw|r{618MRn-gQ z5zm659#bHJBbp|!zOfLzAOm_Jrye&=4>d9bnZm?lyWh5CM#aC}7)47{JF}KmC)0r> z>18+)Wxt{`WfiqzjQR+fvE#GUvVJJoJ^CE}qL%u?OVEVmbV0MAvt1E~7f++XlT6UF zq}@x0wc)r9m#P+i>Ji5k`P{UuylQB5Xxl^kw6>$7_xlA=W+h#j@1k$0upy|nljbF3 z_|T2^&TL@KOb37!8srY&kzJQ*jMcjFrPEtQ^7W9KgOOuxkU_H4N6>m}hm)7_;OSc8 zo`>2GCO+kB6a@{BIh}MtIYnP zrJcOr1ChdqCvVT&!ihoXr?8CBZVFC*$Lv?gpZ!5gFNid{qWOuW;p@z+Hf=EQqnVZD zI+;LkdXC#uiw;jYAFVd3#7GrZir$&8u09NBZMqN1Z)Cm8@bxzDaaOdF6Z&EWdbrf_ zHqxsQg);`c_?T~Gea582PL^f(S@N&;-;TF@^+dXQOjs3y>NX8TdtaF-8CHsD*Z-n@ z9yOB5n{v}pa75xo+D3Dz_p;;H=doC;4+0BWZcZ!z?a^hWt6pYY5yjK_-U&+}Uph-@ zkK0W?Fl9SaB@>>3z6xQxR+-&yL=2SS_S=)%YU@b5|BAApYt#^#V1#S`|Dba!y!-E$ zJx8!@6hFM_uNGpw8p&(rplJ4n-od7>cu}gpN0hX9OilgwskG{!v0#onn;}APV)Ww| zXt&)9&en5gsu7z&H+UHk0E7I^1t4l5tkoF9wPqnPr=w4a9-Y$PMi%GZ9=-VWL813aGY^1w7Gcx@|AC}&- zpIU;OR0*-YZ&^&wDeIWGvWLgKQ`fHJ!|W2cptYNkkbTNpB4H%|(Z|t$vqg{i(j2er zlQ^KOc=_$W(xgg2FQ90>_;9jgjL#U~N!-}!^H^`$kGW(!&beFTuZLOu6jx+Aum197 zNx4@<3Rmb~KQ-~XX9FfTEcN?77D#7WoT-N8#RXfy=+D!$hpnxS$^knoh6^0vB%tlQx#L)F|N4FFildS?>L@JlXbg+2nxHnko>h9bT&OiDd(TXMz$8O_Xz zHR3TQYFm-Lc7)0LsfxbL!pO{`Pph9^mQth9QO}Q}{?s_)v9ea7q9X5?WKsDe3N{MA z<~-vh42RJ2^fhl#IR;&PM@|D=a)YCK)Yzrnq=Ejw{{bx%#?}1T5)P}DoCwr0mzdk? zT{}I&%{CNXy%bs4V-bs~BQM9FzqAh-+(f+)z3ccXXbA1)kHuSxd+vRahyrN~MA-|R zBsH!mr1dAs)m^z$p5Rl8b$*9vC|nuVS@S>cIK4tCVbX|LtJK-<0&qKo-(4OYEu#Mg zj``5Y(Oos%+r-~ikpLOF4fX6gAp8{4thzs>xSrn=Dv>E3rws$CePDJE(oCx25VLF} z04I*q>$Y5pVNv!jq7*L*w63Wf4J%Za)sU%7P%*}8~XD#DCwz%Qg+3K zwSy^pikE?YWNjXs+QduiYUZf4e8{`rL9@?5XGyh@Se`-OF^Xqth%p|h-_oOk?icSf zzxi)uhA5$Cp1s_v|Eg+2nZS(!-^Xf;vGqlzli|wV;W$&U6;6w&;JAQB$NTvk#8TXmcjaqc#p4GZNMJ9c#{ifeesk)F z-8PEPsbZ>?VNUc^V6~QX%MnK6rUl|#Zt_6&KM0Xxz`#lj73Ts{$|h(AWh>C*rB?~4 zwFLynMUEvT8~OkI8&b-IpT4!k1ArM-UQ)Myb;dWZ7=P7&s?cLnZh-N>bSbs@DC%y5&k+1_??H)AmXc?OsKesh1MQED2 z(;&)1nak$D?IZetqVL#a^eL{+dIrb-n^N`Uatt9nL*Fg#hD68(ex@q}aw}-zgrW`OAaAgXp4x zy99X6;}tHKazMIAcN%V#B}TBu?9fm2N25q+&v5`r)yAm>AgbSC3xsV9;2j~!D6YbK zp($jQdZUV=F^1{D3bX62b+i2j44HGiB2;xdUy%q z2pYTiAm5Unr)HQ+OFcnuNj_4OCSuYJY*2dv-XQ#LOC-{{qm`JUc{hPudeN} zq_{&K4i)|t6hnZij7%4wn`C`id!EJNrJz540NF7Kr)JxRU48&lzWL?wuyz@PUR||T z$y-fAo&IEhTER9N0Tu#~5ep?_2$H4s4FwN%?Ol|a!H9jn z=)=n@g-JY*giN)YXU(;5=&wpGixsSUJxUTxdoGEhT?vjGngG@S^LiT29S2v)>|5$T zp*v%3rUI%c$&^yfARE((9bf$~b|KaTejQ>Z5eVm_;{E5FYB(2HBP7%OvDo1W10>oa z=?CtsDS>S#YHFK;cAuhjQ)vs{3T|=$;v(o9a~+0`+}W{lXp18FLv%mo!+7u#Vwn&I z`FcaacGM9=BCAb#)&lK{wfQ@r>FeN3;A`qTU@wYvvzpZ}am9^<02+_mHtnl-`qGxu>n%pa}N= zawk69D=7Hqx|v@&Ix2*pDwH*xFd+NhMpaA4`C)V0Y0 zG5JhQ=N}G@j{_wh-y>7@z#KeykNR- zrb&eF+FE5pYp`B9c5=J29*vn}KtP5q0>~BL8J+90u{T}QA{9!bR*DeRDp35@eCh_n zsU{=!=?w+^ZJkycM?9H(;YuF_ zp)tW(!jS094`f;Rv5(uo`y_8(6pkK~N?sc$0RN$jSdMDuJPqzoWbG&dgi0Bb-({lk zC`DulEM?wp;W62lbjuD;dXS8?{)8=t=q-=8ms&x7fp^I6e+(%_h4`tUI%IxeTjo5kQ5y_-moA z+GX5^yJi{sPzik^j^2&?w;YcF)ag*nd>CwTYgta+F1jz>Ld9US>Ft=aVDVL7Z4}Vg ztXo}}FHQSG9>}Ns?4A`T{z{iwAY;lp*u}5SdKdu_bv3{H)v0wf*#nGOibN!I8Yu?r z{Q7Z95VAXsEnWdq13h42<)+OUE0cNe{2m#t@yQR*-tFHW zTZAw1gF=~(fR??0Baa)Mby@<^@1I>bA4@$XC*9Z;`lVR|1+P7F^U7ad8t>AUn}qv+ zEhOF-AWfSIXEeAoYZLGa;R3Axt7O>8`z(pw)pI>FFj4xv7VCs{e7Rg8|u|VGp@4M!0&AUV-ZVa~@?l#*z}u2fTIf7Iv{x7KiuX^bik(DT+l=7Qae1Y` z0P~)4TnW_?Rq@W#rNwYCoR6y4kwvJ(&J;#LjT~HYIZ37Foxyqj>#;#TK$M@adx%S%Jo; zoYNMruxU$5QJC|t_^jk>O#OrEyKS1n_N)5fe&)3o=U&sCabqf#qk7$` zC!sd#sG;r?-xYV3a|fqS#~)xX8GT!ZXG0gC9#00{@BX1wA4p3Iu201ocgxSFwm2VH{5`4$3Nma2*#@61&fcPdoz`feH{fk(z)ZmHYK z`XHOEQdWF|(6HvF6vuu)QT66c)`JvZ%KI1d~xZAs}C+`DT1+wv! z{8jL`i=m(9!LOuoWO~g;NRPIg9tkafU9cK6iN?zdH@eIA3{C|I-bMxg$Z9M=Z|xBzY787ch&2G_U#;}>c(Bip6|V>V+4}%Emre-W>WSb|NgXr(7q;k;JC|o2~|qJ zU{ax;Hj_D4bbk5#;6o+qHh;1MWyKuyF1-4JgNQ4)ttuc%!C)QAj~Q-2Q+?ch&voR; z*JA2W5K&>!NZ*OF8ACkBfWwo5cMOjQB8q=d@1Tmwbr(J80Vj=6|49_dVfMm}$n)d% z%%*RnMg{c8SpY(!T2nFq%`vS_^QXF#tR?Z_4C}uMfH+|Q^}63HfA*QC;Csf%Z;-1K z&C9K0O`gbK-N}*7q?SX-1Oiyk8=u@y4#1+|HxZMA%V}h|9=v~V0rjSSqX1r8Z{Dg9 z;;gitCBuE?Spy-ATiE8HNML`B<4o|iD=D&U7e8>mK&(eatg&9~?HjU!U~oBrP|z~S4tdt}_Fr-vuL0abnV3;H8dD!mjh)=EGWc$^ppV~IA zB)(KM{_0#yzrH(hn94MBqDG2iqy_b~9HunO9wmjKRMF*i(r2ld1@%ZivVXV#(f2z| zL;I_(L7dYhl<{ikRw;UYpK7WD&_(GHa41&HWq-v0{;abe9#ceS+2cVm#9-bx{DYX? zwHHkr#n*zv&(bW=u6kN5Pk0$`Rcu+JZuw`PFzIc4VED*$f*|%EOi9Np4TLtCtyG-? z%z!vTvEj^#d4i@G#M)xCAAa+c*!cWkbVKnng5rZJ31W(ZrFf`Cu6jW_e(RGe+E zuf4Ed);|?#ejNBR6M$1K%s+gc6kbhJ0r{I9CdR77iu`{(y=6cY?h`G%OLs_@bO_Ro zGziim9ReaPU6M<8BhoF6bk`EnDc#-OxpDdZ?|a`b`*nApd7e2lXU@zxTM#i$*4rSK z8T4Hmg$tCv>4rX@ z-ug-N@TC zq`Ah5iG-y;mfvTHUXxJ7eU8r{ds)DlD8y1%HeoP(*TM-Qq3I=XrU;5P`*}m^Wy)8L zyoP-hDTd-I`+gzC`3qHrSvkA44CAwh7leqZa9UE!W>F}&a(Z}03T2r0euG_wru2nc zxDRYIWFknszyN)HHqr|eoko2U*bk#jP=YtS;!Yc=cxg2SI44u)E4Yh)Y1JqSu1i>e zAgvJ>yW@Q;H7zx7^2*qeYrv$66wX-LzdRnU3ih;J#~!6+-LhtW>Ac8XIzQgJt=!Uu z^ng2deC6*=0FwARcG>mc%u4Wb;1tSShssfVqiIQnhh;+q+8XQw{{8yT<{={X0QY3f z%jPSa4j!7YX*RE49D&25id0>E5|nCxz1GmYf1GJeulg_9jz7}Hyc2U7159eBmm zRwTv5(5g}%sbn@O>?8JL;dO}eDni~j6x*VY9%;#h6%E?**OV2Lnj9PZJ38iun9KY! zQ?n`N#TwbF6J%cK0l*NV{6l5Z)Hu$l-9IrwT!2`VZZ^sN5Bf^bJ1x3$T`U8u$1C{~KaKI_~Zj zX!ns*u4T6-#U9x>I`1KcNxXeOawDbI-kZhwGWiQ>DHZ<(@_H#rFB^t1(k8yG3ss=ohrMk*!J}qCZP_-+zE5;qnpk04N4@{@E1$t;}(=Z zv#!+RY*nnBRVciFS*k(`fThBv>7sEmU!h+!aA!HkN$EGW^cvTKIzB0)_0~FfEIb!z zg7jZoFlOy&*Oer_bG3IFaBm1Yb1dmeQm5L&(FJmX9JG46F zZ;TFAco1?@fzbfzMc3p2jxmyQR^QY=E_a$n^&INqAN05d99# zIgOjH(FHdNuLnrzI%vP0LkV$p6`K24DnN4{O-o~;b8PMNu4D_LTNKDFuS%I) ztZoC&nQ{FU01LYyOidY< zAprRdhKv4t>%g&sYzy{bm-BB{<4~vt4h8gSqh~2TRFYqu0Cybz{2`)$RwI_X(ehg{RM!y0Q#BDWs&sfilLGx$hyxv~djumS%yo{0z4@mN%MG5#7@&nEA;Xw{}N=$Cw_Im;)0_doE0z1Cn7k>Vk zOQEf_$qeFKK#GRwCbIRFt|2TSw$5A^Y!-5JtS;l8CglDYi%Euk#+F1^Tc8ws;QgT1 zOvFz!15oh1UO`&8&O6+f42|F7@i#h8wu|FXd0}+n(mlD0Quy<`dMx$f*Nw*^*6i5Q zH)VwwMGdDWFTtl&Ud!Tdi?~^FIQvc}-s}OYjg}+8#&VL5f4NmEq{QvGlek%ewlxB$ zrjk)BG?{YUyoA4>*^@~A`JT!wOEoBm8^SkvxI=%6tk$(sN z1pmH}%%L0P$-!Xa_<;DMK0fbgl)9(#M2ao+#67tA*C02_7xwBWJqaWE6!QLAw9j#) zu4Y7na}87pmtdOG_Dz-(c%A_i_W~x!cGnC7{%7@y$f~tV&KJl_1PJuF$b8jZhN}E* z5=!*8b4v?Y&tV4;c~b#;L-Pi3J1tuFoKA>Znx3_H80g((V$F1z^ar7!DaONGFne-I zY@uT~_Dgi@tFFk_dA_oM3`k2u6E+b;%Xao@?(p5LE}xn2{=VZo=Jkjc|6ss}!VS)M z-_JJUcu?}$KTZs9tx&RjSj~!2S~+JbubRAQysWDIkczIAh8l9UV78uMr+6DGYM6mrOtV=n^RK688f7h+Fmi~`nr;>F!MW!!z|MT517ZV2`3)bses?n zXCBaS4r+fHhyI7?DW4V`9tBHalgTHRLIZF=_+)-^fCSsIlfwmqoT8V1WKS8aev7*C zCR$HWw;evr-&pSxY%%g)nL!S;F}`Q}frct~+%Pt_eBhva1d*_ZY3I9LxBJq_C0=)6 zx6E5`L_k0Q$8GBc({FxdN$o~QWuLB!{5yVp+8p1KGaSmupM(GE~boN53g07n$OS=u3TJ@@9uD-ye zumINSU$w@6Xpetz*zQIc?p+iLgDV`l!r>`CSL3FClZJi)AXT7q#B@^jv4059|M~8j zISN?gmcQ#ae#=ezT)2Lgm`HHSC%E6?)hR60C@=bP0ht?;ymtHj#mKa6t%`uXH3bGO z%DjQN*2AhlSeb=dA@6uN%+)1T4Du5C_dXQVa1PjEqcs7k7)@Pi?Q9VDHvBzlWQb1> z_<;jcq)MBEwy8D6S@J#-xev@>nHpAevj`qlb>++>dP>Q{%_Q_J*R%uYY1}DF-o2{M zaf;u*f5Ib<^$?xTrnc7>zpcL2eMRuVlyrlo)MLjQGfh7WCg+!)G_$sjZw^$L7B%I* zy;qCPN2pS$@rq=7#df57PD!msa#8II@DhB^jApJLuZ-6Jfj6gH<1{>l9)_Ru0Zs_p zGY6k(!KrdOq_E(G5E;oB^7B-zN3pyfs^1zOdHXrV*|CS=BC+)v?}Ik8xCEF*ecS~n zi|#fz$&dEEJje~dei{qT`r+!#0#E~bQm@kYCxytzp(Tgj*roegb$^;5lHBQOMrg3o z^goRrNiX93BbQe$#a&AF!7`_#I;u9KN6F=gka`^I-vlz^Mw`V-=BEEz($kCo4Lbm! zzbCDpDmHrcBTwi3cYdHY`2|VY*^N;wz$nQ3+W#@ZQinKjFDKBw7k#QO~CgF~)YWQ^$z_oQliD7yOC zCq%?|D*#!%`8AB@N{3OvM`Xwfqro-unMm%(IS(!u0}L@~P&+q(w(XqOJkkN@TLuDt zqdl6zxS(x*Bf@3zyY&xw(`f*L@fPrcEi#jI4OJQJ3jj%V#jkI9_l;KDZJTEKXc+*M z`YVs0wK5H9gDjdw2=V^B)uY_l@g1i;DN1dDI$}5B7xT61MnJx=(OkGo>&-7>6th?6 zPtZ0N3VCwqlZZ58avjJOe?om+RmvTVidxyA7tBHcEFJ4=!-WrVpX^;MCf=2w@x<3C zEI3SCguij(QL!|Nct$Y%tUg^ho zo5jh^!Mm!zBS;IZfkA;?9~B#j#|%+t7uw?(jSn4pal=|%l+Eb>8sab;+eNbeso?1z z1eP{z)FZc9eFXmdbj}10YV2yQTjqG9yi>49rq=;bj4B3bNG*oXxI|RBoC&?$Fqww> zQ5ZeUC0QeIP~CJyfL?!I)F%tmhJp|+XJbmok5svS_HnywQo9!PYLDKSllKL{Rp@E-sa0=X<)_)?lDw#D&idi*hr;UD-m_Diy}=2XK>k z-!o#Z7Nn5Eq?Jv z%jn<2wN(M}k^o1XDDO_PCSmt}tYCF*RtL-htPl~OJKAU5B#)nh54HI=sq5BYcl**7|320mvZc5ool%?9&r`Dodp(hn^U2H?IDAYms#~ z)Y>w+p}KQcPNC*0XBj_cmv}AweO36Vs)fff%LDi{G1E8w(0_zW>N-4BH<=h(_By z>q0LpJC<)qvR}H<~OM)-U04FSSzNo59!AtO4AyHPx{W zp8bS(APtGfL_7I6|2LfRE;OD7OQ#Oipz0))*hRD>z3gq z&I#6rg2-Rqb?$P5pjz`eW zV6Tv#OK8<0_PrhKED%Jky5t+<(!@VD+lM$~2$ zZVMW3-s$iGR8LwkgWnIuH6@k;+HGYz=lOdrszhw}mUv6hOD$`Ng&}VOCMI`B${7?r za!`$^>T8y`P#Fe5KN-)XC*x?M4y9$)@SE42KAf+%Qv^z*z!wXnJ4u1hX_4!wd04x@ zP14$HPgr!#aP!-1tpOsFD#LJPcI-ZRA-TMic_<$6CJh~)l`VSAhdpn^ns!__{;+04 z-oYe!gY?_1(@wM_FwlS2o-wD;h93w^f3aj`^Yha?#ar!0+RzX@?7|+g{;}V^+dfWF`UxzUt=OdQzG+W2XhZOzlJl*8`9s z7MUBQ((9-4v+2a^BmlkNppguq_R@+fNMw)yCWFr#)|$D?<{~NjuDTH)P+r$)KSy~& zit)2J!^F3¬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

ipz5zO136gFLxxqg-sH-Uz4RHP^^< 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 pW2Uk6N^q+%+ZC^^q4n1fQ&i=g=@S z@W36J98A=ri_gRaxTO~I1 z<`?S$TVbwtWCSP&5*>=u-l`n^eJKfx0=q@;1bTQ*zk-i7_-(mhkD!}L_TbOy`|Kw5 z2nnxhR6%A%!UXJ(D$WPTdniU};*uh&(=jgpmkU7AXTqVha{_MZ#Lj62`C7B(6pZ7) zgx|yT+xPC>(S1DkPPe$UA^6XdTicn_531zggIYgvCD@-WJWuROY_Qt|iK*pT<_CE6 zMP{N6stz8pb6GtSw`xPod{@~fNHhk6s)G={v5=Q_zo-49(>hxVhwi$hY+y;;KgiRm z)r+p0fu=d_NAtwRH@R6dU&GxgW{R)nZ;x_1dMa2i{~B{NXtTzYDdm2lD5|vIu|6sB zyXk*<*KM$y6+kV09;5cw?3HHuLMOZB>c_BpZ``jXk80t}TKLS7(mm{^ zOl!U?qBwjvEH5=A*%GG_MlAi3CTg|2+VI%=<3d9ZvcaQ0!G*o2->|?9n&B-n6Zh60 z+a~SLzCIC3S31fe4F*4wt~b3VoNT(InGA$E&uL)7ZTsn55!i>X*((q5|Xb>IWv6 z?$l6$HId<|>&LNw8iuDHAvnQk{y+H-_K<%3Xl4 zE-?HO*XeOrYcGe_?*FdV@F6XY5h8R0ZF4))a?@U3=p)LLZi;3@p`tb$Fhj>RMR@gX z(Go$CXwod~2Qe--;;R7!ffoc>*#+E$_#Xb`_LVOlVyCTpX=x9QhVOc*Xtex^Jx)aT zzG!-o`bsXP`BW3cadvt7Ro`~uIjZR- z{>9Bk_;$!-owCDg(4&d)T)8`++y|JQ(EW&IqzM}~=Dgatz4s}FtI`SDpYai$E;#sK?0}RI>K}xGm$HA0=?M)W0=l8*2j0t#uNzPgQvPW-V?oGuJN&)_G zo|Uea4vaW!$F_FBdc4Z6gv%eMER&u;uyrsTpK&@M)tSqJ;TS8^<#886*#4DYGp4`3 zG@S^=Y2CJUcknb>Q*#Q-r1qZ_x0IgXQ>}=D|HaXR5aZ3a!9oj_kT~v7tW_N))1NZl zB^~s3Kz&rC@LiZCG7qdHt2JBP<0TLohB|pj%6npaxy^}TD{%!SEWE*Pp8^pQ@jCE{ zd-idrId#cAi&USXzTIB}ojpLf>LVLQ9Z076C)?IfpKYxR3WhD?rl=)8*+>X}wkw$O zUkOk8bX@+a#w7n&pY`gtz8Z7r_4*g8vj!|JGzMvev)B4DulFLU^Z)o61yhEtaCC;TN12iQ+*q(M^y5w7Hjp!?B8`3VZL(AnDrsMyJBwf!Yu_p_ z^|`9(?7ba9!K3tUH`|wv#9~zrXhc1F2YUQ#+zV`K%NX_EZ+%X%t2O6tro{OnUp&RW zS6(9q7fyXPYB?RRvv}PswEXcc!q~d0l8f8kd1+)J->B2F{w1Cw9PUK;Y&&D|;`dnS zuV9l=p}-T6ARF(2p>AMfmhi+X<%fpCdZ=7ejGSoarGQv60hbpY=_1H~af!yAVQlz~ zJ;c|`dCqjs$;nj&+G)AU*ALR3;QQ5ilOHq7xhNf^7Heax(f7-VaW5s^hH=B_ns2;Y zVwPI8K$&?b4G%YICC)BLWpnBb$Lhd~8P2#sSGAK#B9)c|iL!DTGwXniGBat>w3d;HCoc8(7{TS z-FNOt(PCIp%=JOM#s6=dVZgXleUJ6Hz0c;uyo%%xptCWK(mTe<7?NL$>T*oQAG8{B zFvo0o*ITIJ6ID*>IqwTMx%G1kFS0iFKCIuW42$dmRPT(i8Jx}OoT2RU!N@6H5;LNT ztwmcUjpXBpaNFM zaynj$xH~uol-g#gU&oL7|9h?F>(4fdzddSS8?U+H51n0m=JJMVhm#YI2ieyHUrveq z*HPw&0g2HlQ8xSSJEoZ44IvEjwaykKm{9a9&Kr^U7IJxjj2aV*JP?4|V6mtigi$!- z>n&Eq$gkYgYAD;jzf=`*g%vY-wXQk0>hMH!L~H8u=!}}|tDz@*yuGI+bG{-)=9~5T9=+=(;TCeWsdB_h`bsJm^3fM&QX`q#Ujo`_eF-Bo*37ixs{JZS0N>vJlm9 zLy4l~OJzK2ou7lWf8Cz&Jn_1EK>dSJ$i#0Q<$~O3)TrsCsS2fE!asUOe|$t0_G=E= zGhB7#N_Z!qk!@bH*I50?-#;LT{fbSrd4T#0{n%Z8vIOyf$DJYt zDAw^hJ`Iiwy%gDykF7m+{QE%wm2O5*+oP*=#M^~Afa6gX%sH-cr%%Cviwgccu|k$k zD+uSepe#ZNVlx6U+%bpr{{5>lB+i4We8efB88LMlrzRt%u?G?-zdzn3CT&R1uPM5^RF;X2DY zL4L?9p_ES2T5&6p`_N{<_Ft7w&1Me8&j_kplb=@G4QBKM2slv6E>pZKP3#jQ&?d$v zi^2A-Y=9`_?lm}m-ct*+j*M($T5uz8E}_jR+ii-$dQ|M)KNU4z&TA~`#aE7zt!#ny7D%geVI{X_Vz9-1I-S* zIMJ__i&yMfU@H$32@ByOm931Ze!ZCb?~=mq;s#DzTB;{zNlYdPZ93(wdQ5_cEDJD$WOFzI_ed zwxbSP(fIB2Ht096Gr!=;42e8B4V@CwJJTB)jp_t7N!-ZWKeL4C)M3IQ5Bskqzbp|A z(qX8|b4U9}_TW5zD69#|%Ne&a%1__vT<2Tk+#@>l6%P$}m5?v35WU$pW!KA!oIB;wKAQl({?A_esCF6$$qc3QshpiYfZWShBU}lv>i^s9x$8EAZ!N z?5=QlZWkE7K+GT03(mb%$ZhNu^yK?~b&uY>k%P&3LawNDE?`FmV;2h`8k6)<&Z;kB zvT}9umx&rI_!I)g`y#fHEJgUy{7e?;23sA2G5ne2=oK0pdWI_{qxWZa<8T#dm(SG} z;F6$@6hYxmM$`+)kA$rAiopO}lu zb(~Il-%(+yQ<~IS2Zx@}P-)`{pQMQIZ}@}x5Yrbp)o+^M94wiFppQLkZ(lPgS)>nO z({<2vKQ81FzZt?g@5Y5xpi4w^C#zgBO*Ls=TxIBBsX6{Vs&`klO5$BOTEf%{Eq9YyBv)uD3;FCWv`SG~G3FuyqZ`U6_`cYZmzvl^_ z^)xw|tU?YJhIh)bU;8pLKe$v_P)e&QcO@Qu*;JSq?|=v?U!4E6Q*TPEUi6(Pg4tjV zxChbJsPY@w%TxE(a8s2Vv-E-3H^U!l{8%Yr@r&VG)BYQ+_u_A(=ZT7f3sn zhMWYTUX-S*0+>)A4nhTKN9KI1J1I3Dz2T@;%n3;t%969W246JPB&*+gybs^i-F!Z~ zW@1Wrw5duc<`so!lir5vx1;psnfTsNR^+3$-g}%)M~(TFOg$|(N%7cAheC38h!*Rd zo0Ia*!F58$Onm?3M#tOpqv3dulXweV=UN>n5=p^!6@J1FvC~@5Y&NY9A&M63_Z^_) z(nuO%_SxPvBQ?`ax4@an`btt3`sv&n{P73Mm&_Aas8*w0b$Sr?~w+dCDqqa zi`BNk%SYJdA6tt~h3C7h$I9~be6DXt4Cx1i4gEd)L0)nu80fdIokbqWN~@}a&PzA! zMa;K%r^$eE)_;wz8@h(b4HE4oDd6sf^~O0uxrC#@vAudp>iPl--Xd_<){XKfawRo@ z=-grx=UWF2x`|I?6T}jmkJ3DgGM2%2>V!_N310Fw9L<<7fu=am0mkKrd#8!7IYeVj ztQXmJKc*1BB}v+vHf6<)&V;t%VHmD{ zMfSw^q)fJ~qxs&_^k(UO&_@gO@f4y5rsE%TFV=3bSyfaxI!v>MMh$2?6-pRhM9owK zZ?xY^d>!#|{k>4giehtu`twt%4}}~eN1MC*Ie+(5BEb%kfp`fkPQ;z|J5=Fmg`wa< zjUld}fm-=zVg^=aDJtHA8X7_4>))12PdhiKH`@(yyH_}r`3qX8P?Fo)aZNq0FRX>vE0#NE+t;bo`Tg>PK(-z`D^x(E5Cys5 zv8--?6x4)?1go`HQ10%q!gC6j0txd5H_R$F1Kg6hbxEWzRLOL-rI;l&9uGklic^|P z9y!b@=!Mr>7I%awl;1sXH@HJneEk(#qSGl09usF|gq~8jW+OyheARVZK|4N)jcN1Z zmUk}tJ6j)`v3Go93wJC!vCpUz@G&y+P!@FN{c^j0&OSN9J_?1h1mk=x88<|97NC^J*$yW%^m==IJfP$`xm0pLI-G z5J67}QT|UVr5~O*0SQ=teJotSvZUd>gUNcwyoQDy;o-PKobW=#EalJVUqLB5+uze~ zzou8)Of^+VMtBzb9iw|?04v!zYjkktvjJAXws zCdWm9y*$6!P8M~^mNv8Jm%K*rTkt2tpHW96n@pzEs8osXl@CkGeRUnIm5yheR5$L} z1N0L8ClRizmXb+nL&3PRK^;(Wmqdndr_agA^%pNx9Z&quzuNNl>yF2=ga*5DI%_(Os( z@peD0@7GA{(T=bDPCAa|$ysxpXm?BgT$mrB=tkVcxl8w!ccW!ZvI_s#SV_n}TXn2Y zdVV1f@=@L9KWEC!fyz0cRai$RSe0WoKSi*l1C!fJ&=hi_KWu~dlBkk7I*$?+N)e~e ziP~7Q+%X za@^0)%_Elq(0}-DLjk3XmZp*ohKD`r=90+pUW)ed65d0enE~nxk~C?XbL#23$eyZR z?UBmL6?<^w<2R4tE-{2iPL3RxLlJ3#d^e}2)L=_CzG+}KUat7_n6f3 zeFpP&rKDJr5R3YctbK3XTCqO%g3nM&gxB5@C2$t+er;yXcsXpn zYYAk_W$7an-kHYHfge5ur!%bh))G3VDm6cTTpbEFCq+d2=gZM_*a)GX_=Wsi`>>fv zfS~A)DjIm3j)9p^LnnxZ5$&$-P|7sOaC6V*m{3{PvwRJ2Mj3|A+Yqh%X?uiJ@B$j{1p&ajcd9Fx+p{_|26 z+MMJf`p4?7(MXOkPBfDf4Yf40f$0}>N!0HCKi(1-=%;DOLDsCq9?AJ@aC#!xQ}!w* zAKKcQRE_N^m<^tL9Z?l;zki%nLAxUd0~;z_D_x5jX?Pba%G+fBy<;*-EkM@DD*aX_ z(tXG>Z|R-A8yzAyT~${>21C;h!b$u);CZT8Q$#oHriFu`cJhkjvsoG&LPz>NHi?F?Ms1w(^}GL`Simmm2r$xO-&)56hg? zrT0`uA+&cucsw`04-F#f&bFaHCDoI$vL<2(|6}(R7%9~ zJirNfq^yiM)69TR|2Hz!4ok%&o@&IJaM0B%bHJCi7E`)7lc*VFQ+XAP{suf7Dr8H& z5;xn+(qm0devkaYJ8SL-dh`d5m3eHfaA($-?#in~1SJ`UMLG#~&*rzw5f_3oZ>snE z9Zw2=1*)XOCabh=FgoJwZL%aT20zn=*4Nr(DKe|mK*(>vNsc&0;2u`Un;e>L=8q?=BWDoI@*r?)=y(J-ll?*w1 zpriBJf|afg>cbmSS6JygC9jpI=?*I7_hLnJ)V)SRv+^R>631ZXFUb_4n zO4F?ei9}Vtnd+TrSR-_MebKYh6TI>}pZ(d@n|{qqz`rTm#BwAFYUDdG|4zkw-ga#r zW3@pVA(dUCFG<_G#pXOr04q;R)~)R+Zfa^N^PISxGCr0#|FV?$O|{`r@2 z#}C#r_LFa8^sGxJDT;(eo~7pfu3D#a-H_x}mb_Ju4GE_TOupfq^1U4Bnm%iGU~#2& zes^clK~-eX{V^^~!}XN%_Jb9)*oPnKU?lSU_cxKZM(OYb6sp}{#iP^50%`rfE3j;| z*e(v!yulw1ljgI%k~88*;dhNFIZs;oT{Pu0WsZGWWURYS^&cSM`U|q=x|3N{8y@&CO(KnF(r~C4&Yj`(P>920ck;d&H}^vz1Y>EFs5Y)QTyQY> zhFU)%ZeD21%MnpLEg5A_Z-JZ%!aOM$a*jS*9R{pZ2$0UQErjw9bUC8_Vb zz-wHVnG8604U5%`8CzfA6SQr1@XX3{urq*_r&>(#Qo z;4ATae^%0{mlL%+&6lS|g&V_fIc+4+szpiR_1xk`Q_et)!~e?&ptM-qCgxoJHCIvk zbjkeLFJUHAq?*1h~4^LajK4}J;pp|X+VT1OcxUSG!9gDlnE>W48ou{9E zDfY5$%q4#CJI&wWPl;C(Q$OqCN*xyGU;BV2>Y$mFX$1x9UtOw3?=*khxah+`V=XM(s$B8n2-s-+C$;nM=)Gl(KOLG=IxW27_`Es|FfZlN4cPah_ z?5i7$&W%lnp6Icc+*#zXW;Tj6tsG%1J1|_TqFG8R2LnK7nhE`GX_!8 zE+Gos-PWcH_lt8JpYh;3s)uA53_$_P9kQQiatnBdJKlfJbG;UF9&N*CAGf`(#vUGn z|D+_4UHB^6Yk_)c9@zITR#N{s*Z6{?HMKlI(7Nin6W=aK&u_(*vdbr-Ej}i~0J}av z^IA$~$_qMb>!nk3BZ6DIP_K%U zrY*_an;AzgG3oIhCe&78PTFj;*ZmyxP*>F|L=L!PPRg(bms^h5ko}HI$Y_$X8#nOD z=LnJ%-6PW;B^d77iz>GRy#G7fiyCQF-m``|V=UcX|M_P+*!6bla&$1LSMRjVSS>8ED^h!y zJh$Q7 z%ODPHLxl1X18BVNWICFBB!hru@kES1=X{44`F?&uH&l-!16h1|75=Suv+8XhK~Yf3 z3seud7#+6koj60B-y{?DMjM`AZNy5D@@q<3p=MX}lZ9t%YSuF;G)!9;Ei^K!);QQ2 z9>ZF`y}z8}iKeZ@+(7OIt(biI@ujBBw!*Dq8M7VMMVr>6qW!PHlIHVG#Hcp2{NVNl z#G8`uXoJF6PcJc8SbblNe7-b2AB{Qnv@zTb@6jMt_PZYC(p$@T^dltEP;|v=QA0LW zlK%AsLW|Hwj!8G+kT8s8MeAjm@=)_4q9oV6twgmVqyo2|aD`DGoXAite=MX`DK@-u zkqB)Hv0j1i*!G27VR-z%ax&3lr)bu*OOz&9%uqG0XGB!;kZ5y^Wm_&;qQm2EIlo*& za4ONibrhQv2=*fGI}P?BMnSs5mnvWoj8cP?2a6?I`(&BZC>L>MA3g4i(-mF&LB~2j zwPcyc6njQ{Gusb4IU-*#z-mo#x7j((9OW`y3Kka^M(Jaata)4q&mVha-Qb3=STbee zjcImYcJ5MVsJ#r}$09NhJFYQg7i5N>g8VMNF6Byc5Axnpu~MKpp{|PGoQ^7bwA-!~ zV*=p;HjPv{)PH5unHy88!!sSmya-EMli#DDfxHX^pC)YjzXW8jS$1DIPYg#Nr)9G8 znEue8$F;&OnKqSf5*jA_bI0-!@rUt&TKo-}Jc-&IXQ-w${#d2Pj7Q8_Xit9!$>}e@ zjdtzV8+BU0r?#ToP*vV)Nqze9tDq(tuk%NoHe%){JF6z`fXDh2qo@4804w$iR%ILOyh%ll!VY!I@5;>~zMjC1{e^!u1V7YU(8q6;)>r z9i`gRJ2K$`x_2psOo_aa;csR9Bh#!BdVdzv9yA}zaAd{ro=aUT7W=x4M6d{;qse%4 zz5pVl<(kb^eV)YtwCcamqWkSYdXogN-K9}x7B8ytw5dI})Zr$-6@<)G7Vh@SWl_^X`9RU^I`uz=2Z5yNyg^h_kQ^{vQ{(SJ3L26FFTA z90mg9zOa~zMAlI88B-lPaVJ`3P^H+}Z^9U|l{*6B_VBT~a-_gM>K zNyJ#+CTLxmgeP$LecOqdy*D3)?1&}uUURU0CjP$z4GA!ZL_H~WFiZjmYv*~2(!W7g zM|{Kn#=4pSIRMg}LN*+0wkE@9n?560_hSPg#bR(zTLtKIPCmlUq-o3jlV%P1{Pa*C zhh^Ff%Rsl-t;vfjF?g_5PHko1sz=A(b&Hh&U$6O^PX#!0$oSnEkiX)ZEKouY^?_z% zkuBkQoG=UWMaRD}JU-=;pNavDgIm4@)%RIMOVp6+-R~=Gc&v3HVXw7&_DR*R+tzLm znhf7?R^Kh2hIy3G$}>Zo&^-9U^Y-fhy*~=x8UhYju3LZdjsWs0>RW6(T^Z_+XQ;Wr z=_#y@t3z-lk1u*cJuPS`>2l7cQ-5md9ZamfYNyOljcA;3S;7BhmmTVJzZUv$d32wN zW11B!njr*jKxG2faf2~|s^n=t*?FZy*f-{+CPlqD7y6BOpDlW=duq!;+ zHjkckmFR5B6VQy6gg$YOZW3#g73Hd=^F-hgl-Vkf z^Y_eo#0Y65evX?c$doDl@xiwSA#x4@io&qGlv|386z=pQ{)HS)IHICnn3Z|+Y2haw zo7(=niZJ8L?1@%XnNNY~5@_##dp6SOqshr<()7X#aQefo{)Th&{lmQ&seh24AvP_R zrkGYd8wY&e7wfJ>M$pq0t$7@bfH)nc9=|OXLlkqvcZQJ5GJ3Z@1y< z>WSGMRu^%uNJfu zt{+P}qMsEk<_B3Gx%h*ey`|~!cMofl%)hng!UO#JV^`~lS5ll+87(6M`~{q``0(aC z*lF~RJ?*x`5uQbdfPp>C$%sJL;gyBvF!Fg1tT%T7LZT8^1i*eVFj79aWdwUo2vH`! zQ$cRRVo}=AN*RGJv+(gAMEad0-}1w%Jy`8cZvMv}oJy2gI()wxO$O?N#=fU|0zgIH zZhd>+{@<=MT4x@xvX!|PHOi>=gS>BD$jS>4X2irE03*UWBg)+V`p&aVb$2ndLRE?a zdXbJc0m;G*`$N?r)1WbG1vrVpb0xsbqko^Qyv}qj8?fj>Q!=P!lJ`H`CLZQ~jS1Bv zP|>pM06*UD^8IB3oE(9;ln&4-vAwbByk`ISfuMmv`;l_=M4dQ!tBne9(gvm!4EOE% z9MOV$+VI+~)dQT4rwlID(R|Z}*k+GrQQazlbUw4}p|jU&6X&}15wN=<%{;Iv`t5o) z*Z+qnj`qFAS_Tz+~iNSFQHH*jxT6>wyZ zo`VqQs03S zfw2<@t{6*Le{lXJwIaoO=i79=m=+jEayE929kxHl+bXtG&8%Qd5)VRJL?&{OR7ViP zhQ|*sgyPAhP-#}D+H{)qXP=$}r#KL^27ay*&X3PuezKu|!~YgN5q=iT@MG~R3Y%gHfKT=sWLbP- zOBI-{*udUWeZI;qG!R>~xG!uYcg)VohjLDZE%@N; zW!^z&U(C5Qgp1;$x?~q!!|#E^`1AH<_x3wusJ_1g<lomnQ~KFtr&+ z>IgDdYYl>@lN+^tNgbyg(g_^xuP${@KYn18PrbTo@&xtE^|j98W1Vyja1E9<{mpRW~T zyH*Nol;s1|o~ri>04~#Sy*SHJWbY5h_r_La zAWPd+cpNV$7qfXaLCD*@1hAE}IPyuW%Zo zmhLxyzIYu3py|c7fQGC9f7T)bj8(}yAyRRn&z>ATH{BL5H9OmF7aR~~-fUO-niE2|C(7D6upO_00a-7hb1QD_K)v#Y zjamlY)Es?1%-;rX!UoGOH!7W5|EfDv$%cv^wjDYIisoM@K2N`v7Lh_=QWAYuGXCwy zYcvlM952sz_qoiLmrC@gJmtHV`VCiRE>^J#g!8I#PwOQ@vPG+y4ATUY0qE~{o zZA$9mo+|SVT~vBfLAGk##ekbR>>rc76uDdt=uPo`9%s)sY3XD<1=#7F_!sdSuI$;-GVJ-VJDe~xAy~e_{sUyDxiZ_>H)2Pas zeq5>^KN0@lT*El6>1@<+$@^^9r|Fx|J@o!Jf!HFNc(h%5nA>fT^Aok zPHX$WSM{ZtsS;@ha-Nkrbw3o$bYI;6Tf2)u5+`}yHM(tHw5twUP3(zEN`-AKT4w8{M^XCDEk@XEGxC-l`O2pv__QdN&$R% z=T5zgQ`hyxD`X?lrx5q!MX9%e05>Cr0NDJ2Vcukaksq`DIYccor^9d1d4KVSJmBTu z+tYiz6E0x9R=4fHvK{|tI#OKK3N=*D!@e<45V-`5TmY_#AEhtR5UA< zb_So5Dc3r72gkn(i@@ubNs}!IUJJW#m+3nFV=LYI6#@tWT6N$+`IxN*->hWDl05_h zr-LayV8M5F-~Q)3d}SJXlP$ScYfqB>@?>T0Q$3;TnTg5bK%Z?JL}hpRDWhueNz<{w z%XeG2(iGl--TT?VCWim~x%2YFXV*d*-d{JbId4w=E3FB(Xonw8I^PyQXJL-DUTky) z@Ki$PbEjEYc=OdcfAX~ardLt)M*on-wkHGYHon(~7p(rd@yhEFC}9^Eg4M#15mVMw+XPr2?7dVLko^2^0?8Ob3 zq=0=gy>9!?R^eT1!_0z)wPaD$x@ZlXb$GVAo#Ca}e7W0XldYtEgz(`ji3ZRS=Pbqnnk4Xee^GLU?v}a+YDEg8 zlN%m8de-UJ9(}E-Ckw}di9I}V=;!SF0JMaGXA0n>1a7VY$2RmIocWhNrCWiCb(wkd zC=N5MWwAS~1BOg~>tnRB?0yYqNfX|74xM-SO9OVVA|V2VZ0Q!4O6x?OJ3AYItt{>l zIToaTCk(1vML23qJlXu{8_?fYwuntGZIfLs&=D0BX`aQUGwQs5FpPt?>A8GQO(9%a24%&XdU=7bB4(&&67 zvR))Jx@p&s8~9>*=bHz6qE4@`{(o+5IJ3b_h?8I;ruMysthz;(qWC*4MCbnShokd} zH4*=&!wtR^I>GJ~evS8-!LWR_AQ`)juf;1*=d$9;xTq(Sf7`U${a>m}bt05EAJLqG zDf73wSM;cDUfxQf^0fuK%@QO z%<|!!Qkb88X7HZ&ofqXXrG^X?VpN#jibEgw@1g4&4vUK!9`3mr-Mlk%eh3%3?mpIk z)SM_aGVglgd3v_kb$nD@THk*n#V3hzIRq7Zmd%%rkP0y+O~*BLzNE;PwXKhsN(qET zIka~baIj)+^#J{zR0c|h?Rx`o%*2$`;HRG33mz3mfI`XxU+=?0JariQ>s#~AEE<#~ zsoova2(8G8*p8NVOor!)3zzOFo@S&jZlB|O^ zWK5049<(`*oL*hfno?4JxBjvUL5xp7<9X|WJX;<74L!-8oXtAj=~nJOD^HJ=t&S4j zFhE}gB|iKnX3vrJ;WgO|@2LInG zlE^(%LuB4#esv^%ET`D~R6LnHcS}#pmRLcIe{csn(yN?~1=Ktpu6b;dZ*~RJUynxG z1M=`W(BC_K9`-bjF8(ML{QwWunpu$8w|UPDt#$vD#~X$lQyvp^Ic8KW)o1Vh$KCk}0> zB%4wV`Wq9jcgzMtREBX^)?Y3)2&$Pw-V+b6-;{dv{tYtybdd$|FYP={SsH$!0(D2$ z?@$Md? zJ}UP@r?!}_V|-E|`LQGw>jRZ8B2oSKDHTmDDOZji(ILts&aM}$7aO$L>j~D5K6l3# zw1^HlX!*LQ6$7lBdGytZxGIB0Qwwa={74VZ^;)7#(CaD(Ty@pE`xNjI@2>X(ThY*$ z&f4%X#cKmmnUeQvp}r%wBO4Ht(cPuJ#5I-!)*esEo~pWCae>YbmE!zgTreiCm^n-8 zT!5c>-8K7u@do_#^pk)0Z#E$A+yT8`1MghiJ$CU5P;R$lhd$L>fv?1yLN0`;b8`9* z%r1`on9CLRz#80X>MGiZ4d~hx=EQ1{V0J#SZmk7zZ8*!2b9H2bJfWz~d%yte#Pm}h zUxsBbFiooy^>@Mk!#KZn_6iT*El%;`?*z<@CGXE90F+e;7j+c5C zxjV0o+gg2VM4SWOx_t}Y<6;~N(Cf36K-nx^hstvgWGK-P`7XFlQ?-l^`{zvpL z^8&Ac)dY?oC;%tXyge>=GBfjVn1O!Y6hON*&Dsi|hv))_jXAh`z;OG_J508}==hoU z*=v}|7lkK)8dB9Gz6qjKvx{T_9FX+0)o<2;%DHETm&TsSFUT7|N z9}b_TmY-EgHfs5wsfLgAmnjv>JF&&PF|yoGpD1_JpY|_VHxVhHd_SgFp$)8s-u8&S zyu~Kp$Z4lV|Ac!+MH2zk3rP`|<|{M7ZlWiAdsJ+ds^$Q-5liyP<#t$1i3j0$u9s@&m4(MNg5)3v;#!&yz|vWg>I%u{HJ21O-{pzC$+|Z$$L%h*;_#lx_#sQ93MP zF!r7M7xNa3$A{zM*9VK8m)1!fFhre2DjR&}e=Ly|d?a}&I=?sUl$9;kR2&J+Gbu0n zB0^+$`Xd{+76NUplEvFkPlEIA>5SkVivB!bZBPPi#ZgF{GJ{lzjj}EqexAcBviHut zY683WNT1wAargdSM>xD$C>7G<91w74Qmh+*5GhAX`Iqa?RxVuj1(I@GnXj5o+37XX zR>r;b8HLK4JkAd?vm?xv;1{kIN$AncRAfDjScLz7g>xfqrCxq3wJ;HmbmW|GO-a%! zXNJ6Qv#Xmf`vDf0`4lfvf#jGoEZDN$Q7A^N#3Yvs8F0^CB60=@o-_YQ3X~?FMZyPb ztL$RUfZM|H1BbOhXVeDP&ExHkcCWfIne}ksP||GiI`3JZzUo1|H4$}zb2`>PE0QXTN*%@ z?=SBMZBwEG%g+3RF~Ql)DyDodc2zd8v2TttLTn;UP1}U60DoLMXtU8nO;Y3kaslQs zI~${+E}dPuH?*THf}8C^S+|EZ{dem-FHK_bCqM#zNzQrg!AOXeiGSOHs7GRd=k~(%s{$|{UPiP{1=iT^`sP*O55fY(lVE930@2Xxm^S`TGuXXj zeu~jt{%tggsU`)F4IB;d88<|ldQw_M6!@UG?2{-t5$R_c@E6=U!MR7wM2iACx_-DndGA z?_k&(o7HY+!)k{u%;iUJ+rr8di>=o)jZjRixNyuK8sgvr_vb-ETP}Joq%fKjgygW< zx?|pl9=4fAeGjd6HiI?kb=X@U3iJ~{1NWv*Wa>A#{Ccz*DEZ`T;~mu2?u_*=UG6N1 z4t!^@-i>`ijQd})D7Clr+kgSLKdB-Nesq_0`ueIdFTRtiO#SDzK&gIja8T5t{D#z> z2BOy;a|)--e>5>+lMFY%l#)p`Rll~3CX%S!hxJyzM)gfBPbensIKY0k24$$J)eM)i zjAT}t$D-ME7rJiS8~Fl#$g$9+Jnlj)Jp0e^&hJ2pT0{y{YS%w#Mf>&hb#}j4&p26_>hVk6#cA+L06ueo?fqW3- zX-xsY(BY6eyB%K`b>=ud|I%*sMF354rmjK47M8jFgxiod=hBs#>ubs!xn5bg3lk^& zHhhzWFIx6SCd`KkxO0Q7p8=2@aBv3{OI5we zrOlq)S06^_Z6kGGiNE4e^Ra;TxVD39=<~>)I=zaL>jGDwjA|udmfSaHj@XYTo1C9c zIplANoEsyry#kfQ$hm5`yak{OXl7?pdcQ>;5adJmT724QQS(;Qg_4y|dI@NZa`9K7 zkMo!VQ-!eCy6;M!=AR0HY*E4`K`wJx(UP|lqr3la~S>|ule zhe?SEw?Rofe!jhiq8~qc*7rF-F(u#)NqZfY^hx$3TJhd_=re)8;)1xom%uk_YE>;A zT=ktf@2*gWeu77SsrR33u&&P3(`_ejp+6yiWd0eJHwaXR-f6uB#i@9N-c5G<95W>c za!LQY!W^jdB4Fw5!BR0|TAqkS3Dcv78^nQhCW};{C&Q~yIo~ySzM@1BtLl?rr$y-B zM2W5)kt8SOIkcc#2;or>c}3!PUl;Q=vAVsh$565TsCIX_^~l-IX4!g79rQyb#k=d9 z&^SA3^eyCse?O<|9`Rua`gh1PShls6H4@EuJ-ebf)e|rb(Hz9n%Jplvkw|J;r<2!$ z26R14za=PQQFkq;9$47gPL~$_s80Qwdv(?#@UHitR^9z^C|YjHQ91F*<^G?(uG_?&IY`bjX57*iibF;+0GdgmFThg|j z=60Q3!QFF^N$AeSh~#e8?)0wbQNdW|J$%#8GG|O!%pMkh4S@p(yk^&VDPS$19N;2`Fqs&L#)B6A0IMe8XjVIuy&Uk*jD!XvuVI%dfMMIACU9Io!(x)g zzP<=<U53RKV2VB$dZ1vWwBkKOuD zP5>EUDq#&*aLhZCHgmqpH?ky1b#QEVdRqM>$5zMvr}61=o4uSDz%~H>V-4ftA2Qmn z_+uqofOA{1bGvc%-{@pOYXs0=!X7bdDz-(2{skoAl#7m`OvYkuO&~RK(X=?K>Gf)A4`w0XFf6=FW>3 z%zgfw!2RuNOqE{jl$lR3R3q@WKiqb8<20yo1<+*Dp`KcNL2cT^LTi3^WUn0+c=Ck# zfClVvA}e&C+#oi&Y!$KAX>-Urv9`U#KfOxZpO1M-$*`Rb{@s}{l5Fw*9|?2RBR)xn zKc-XAz(?hV;t0CnHp%zUA_--DXhd|8gBJQUUiZmjvI;9QQZ6inbdCY6n4ATnSt46P zW3&Umlhe>&ZKM~Dl=gn<|3n=kfUlq`3vl! z>GzkDFpCNcjqm?3sq-wEiDOGXdxNA8Bw$o1fAP4ggJSm4X!n_5&R<1QUj%H3qCwkl zyyR$w{-(IT0xM&eeAO@)O4Z`&zix!!S6x>LI56*~D)iibcD4tqTn(X@wl*+QPGYcP^-r{v4s9<0TCKgg>IxT0~J3*rz7h62^N=xsGC5td0dDfCOBDY*_-u zJ{&W21zFDYhyT3qdA*(q2t7}CCeoLBI`1BCK{V=E0QLZ?**^! zY1s2${`#V&yJ!$bnEGl~ME0IP&~^I7yA<}n!OuIE4_H{t>Q zh0_-hp1@dt$^9zqV+_B3a?6Yt&K7R{g4eD!IwW$HUtzHs@J=!S{r_R)`XQEJSFu6_i6B{1r@h_Uip7*B z9)xx-dx$W5>j;6=T2>z)cKuXLwl#mPr;SDL+C&p`A~!TzL%rRVTuGZo(wx^lx>r4X z7d!(O3e-FKbBX4{f65lEG&+fA71Zs~p2Mc2xgW~87&2+VF^efeaEJI_V#?e7qmqwV znW9faCyEtLtFqtc?XG&kdJc^Zy|ZXF?7JO$bYol3*j2e@kuA4-vy3->tlg*(M4^vk z57^vIM|6di;*Zi7+_}|c;E*59oOzMfIwN6pffutK&}yxl)|E5f|1%Lw+;=ztGkYoW z#G^(_>@^)^@gm<>>}okSNbjnx`{`t#6SJl~I*!aRVRJ|Es~!oX`bs=m?L)s*pBn0R zsxFPcr4wDE?`!qj4(Z~4R)!7uSL`c3lZ~g1=yYqYP4|`8ULyFbhxa6yI@{1j^O~E? z;&UF;5?`}q-!~#bqP$YEr{te*l9w|+KD z+>aZJ{tf>u8TTTT+=q}ZSZNeTX7qy)HDvunSw5S@N-RV0MM}jt*YJfxD;qamYH^FH zAk2ig2g8<0!8Td+G@Xm)dUsg@8w=9>PSIIfnRVzuGSm07Pr)|HbOUoqp+m1tK_*Jf zAr}(!5-}>1Y5##`=s@*4UQP^to+7OXxq>DPX6`XF+#~j0&fQOYW2oq+@4Q?RyY$0_ z454%@`)3U@Nj-s@A@mp-eIJ$ZbMtJG_Q#vERnWfUrkA~4EcM%`@?sPwPW2@N-=Si} z4^56&asEBlw1z}x8?AbUdg|ttUfwq}cK6=0vnVN5POrT;W$~5;Kfx_{R!Cjm$0<*H z(Dz&Zqj0kyOX*$Wf9U&%vxU?lyT6B|Xz`nZBJ6iuoGFnr@GHJMrYgs8PB(lNA!Xf& zQS#u-oYICMhqZ>58DBWNLqMtj${gJ-V#u}nUI2{HUi0+7Nk%|yhYHC0MDdXAr+yBq z8UV+PDR)577;42}dI+D058_9!83Vx*b0A4zZ;GB=Nw_#Fhlq%@z{_c2?L#DQ#Q8U% zfA{CErT)}Jk!WX&>9q`P+t4OjJ!vw=XinX0z&h`}`?PXt>MDi#KWrbad)N39P0F}t z(pIC*5`y7M!kYozCC7I(5i?!yS6QwCB-m$idYs$yCay7wa=N<`-g~pB`TN%a$1kvs z~VFJNIR zG`84GK%5FZ_Q6*sX8VC7uh9&sTkxL-Vxr~r{MbNvGiE}@)G>pt<9;GUZJm)rX=K)B zbk!PKU44bg)w-SiA(pifF`2_cJsum2G;HPh5q)v$_k5BA?;_Vov_`bKqS7~dd6^km zL6R0h@uO1@o0v;Od==siudd3<1Wt)OB^@0Oa?oE%WUG;XX#f`RB}=`(W9-3b-D?o$*-zzA zio?}1$Ppt=7YdH{#oDoR5=dZaXez~OEBAJ7o*|Fy7}vyjo4nDj!==Wux4UVLf|Xx@ z5rI^N^^Zt_2{vhPVVP&LqQ`6zy{O8w|D>uf#2)WNabNn?lh)(O;gfZ{>zf9$-ac#g zef{T$B2F(3#pYXCDAMsq&>eZ`6NS3Gb-lR~rk&Aw+Uu${V}S&787`wB+AhjMxh?GN zl|T2P+i9_G^IVAzb+N^#)?;K1i7GELLHk42{jFF}E#kv(I!o^aUxl>fi~Q1b{^AD+ zw`++YUTY2i20?Myl=I@8cJPT%k77tqnM`(1TBVA^d3ysZV`Apya>Mz`EonESnHJ)z zFyvXN>0aW|fHAhA+byA$?$t~o=$@%JMfR(|Wd{O0nw!6j=4VE-wH2n)==$|U`D#JM zc9nlJ8qt-(^)g@5+%pQWYQHk~Or0whcPQ}YV!fMcHo9aeAI|%xCwFX4F0Q&1;`?!# zk<>3B&-TX;J+SjXqY_btDxSr>zH<9-PyWsr78+a!C4a*n0vTMoVlGQUb8*o~N5Zb=&6`}Yhwxx-Ib7b8+ zjsyJVZ z&-3s{2!o@$gZNF%EbmN7nFGTyxtVsX*hHL0;4{6P4z9MfT2ND-#k@zTc#^}Q(O356 zQi>!adn_MD5}s`!ipI!{brg>WM`7v^YS_(7VFMk3pESA0!?}a(^M_JvntQj+7V=~> zCGPMD0NV}2s7b0ECXqI+*( zl?+phf6~X0u3%43s}cDhL{*WcR*>f`FL)W%lC85R5mURp^}hdA-eu@QwVSvhRK_+# z$4-L0jmz;kvcxvU=)Bfm=k;IWgckc08sj42jIj>+(uLdtbz6P7>--0Cd^)di`_J>-L%5IdjS<5&v>l#DrH^)0b!@EMz4~@o4ui46 z_(SXLp9c$L1MmHrx5zM0uKlQ7YuMpkVFiwV^W>)Ht?$1VNe`bq3}w~4V|B6I@H()3 zRcAG+x!FwH@m1Y3&WI$sD`%sMQXO5>w4*u|<2#l9%M9-=_m`q?log~ z-eni@n#Xz00hHH;1GP`&0UO)kw`=y_2t5JVIo#1~o}_*q%Gi}glkXjyvx zQoGDbt!sI8|I}wRkg-I6+&;>C+=C})Bo71L{Uwc*fN}7 z8zJuWG55}M_P>!rFCY(R8&_LYQca-v}fwd5~)YR1I7@5wN z-GQ3|8b7@pT$K+C`kcC#}rji|OJ)*>ASSVCs z*=($J%m|1NF&Q~0LnaLNf+J{!GZW=Gl(hPWGjkt#RE4ZDLnbR%%?n>h9+=&pw?} z4m_KQRmE5hYi=T4e8}y2Ob;pBkZct(t%h!uX0id0Db;*wZf2G_$Y;F`S0yi-?7}+P zHmR_Z;Rv%g^>}}4NJdE#?U=Abcqd(E6*O~_aSe3of_JlZ61lLcOtz&H-Q5FYGx#QU zVMqe(_?ede$C-pNSfMrM>K8wKB>$#s^i}zJ@Xf76u{~jQLwkmEO^3)Z(1^hOUYX9RS;&O%!;&hPEuN%&C2~5EmrQx zVFH7`pTc_v*rvwZZ}f`A9EO%uN3T4Y>ca7Wy8mzsjuIBgUqZ0mY}gX-mg?t!z91q* z)K$1o!Q~0wJPO7tmthHN6y1caf^-f3Iy1#Jk6!UTI7_pkmpTdg#?l@}GjL(XQeslD zOzE(!cmfUh^9I|j6n6$|Z~rqy@u@H0mix2qijrMLw)G zJ*{o2+h*H&8YpSkSkqkKxh?ugN?PI?T{znLh?2A{&zK>0#`%FNC2o6=wpGs@Pl%x!(}NpsnI<=9tN^UchOon+}KvE(5>^4)RiRoxQqSXJMa zpO~3j>#_jFJlzmCqS|wX^%BSg-k)2wL4zGKC24kG{jO~Owl(Jw??A@mVnur`R=2ef86))Ej4IWnVJ?k)^sjkml;@5-+=o3L) z!eddst5L_U!#69Tukxk?XF^?Th!U8NUYtT-N162SGag&ay)x&d^kb*3J@w=>4(%wy z)8;hZ!u||1Tq>2Ly=)D5LvK*=7q@!!T6)TMxRd+JUFhv@1Y-Ht)iRk#TqWiT&!plz zI9Ms<;sB(}pbCl)CDIa%8e_)>CCEt{K`D_O6f3&e_n&1|qzh%`lp|+ezcdGh`X%s< zc$^CJriN%;^DN5o9;i617~k72E8eki6KFwQ;(X+wMk8(0ap z!Ti>y_Vt)Rs`WQU_hFr46A5$_1JNT@pCbwvYR*P7fM8KB}-n)ERsmk&IK4jM1ZF}le@#VDa zNJsPr%i-6kJ9g4vp218E%MI;JRT%SNOQHW7TooBmh|xiK@dbNtBFJ>h^_p{j+Y=(< zqZ(Yh0+e?EF$WjU+>o=6&9wA*k;lw!dM;gh6-?VZgj`EGYwgUGbU*1h_lT%;%z?H( z{0p_!RcpG!92c6^QZR->@3tb|flp_AlnesChM~{cn;PGn&SG!GA%Mv6hj@xIbbwIh zVE`~r9xmpYm`pK_$*rw6t|P^Ln4v|_vW&>OPh%tC%c9=k>7UAZO*mxeOv+sd2Rn2V&ksma3|&XTV)y(0~CT981{fIrdUIlI1*A^e;@ zeGWs-xG1F~R{r_^>qxLbKqyyO(YFT|K;^z_vuM~ijcZ!@*Dcz2WcR5dg9HBkEUtp9 zZ5i3N=&B`#mu50_WDP$ACUVEq2OB)yg3U1P{XC688(nIz3dBdD2SM8MuIZbuPp-Nw zgmXo1S1VZ;TL?nG{yKeAEb3}cQkw(PC}^tw2o}}j@E=*;e{N2(Kl5WG4R-D|*XZ0m zUFMmgG<)LGv2nQG6FJ8%>Gk<{;)}et!0<)!P?E3)a8-PSe{B%fPh)LFQ6SDU!(U!{ zfSa^gmvcUo>m_=wxc`f9?Vk~8xq)7zE6K@MTr4bCrgqlG^+;-9*u!$Q1J_V2oon1weoVOkjfE`ZRv;{Y zD}TFuI>3?`PA4Nla5m{l1g9gDGkUE7pIR5}F+dqA2Fofh!zWBk(GtdmrHlKXCVabz z@Kc(`0yfj^>Gk4^|5DKHJ~j;7^x%HWgmlK-Cd5@&LmDbw@%B){WUu{ZMpW<|v$8-s zqJUVEB!Ex$u%wQ_$~^_hYr+vj2qNUS9%(wrTa?4HM^Ne4@aBq4TdKvIofiH@lugn9 za{=naNr|I6TsUx+a$FKF`9)-_T(lbsKZb7I!}W~6>^|HuAvfV0jn97lTZHbDU|Q&R zw;QU>S96?lGYEJ(qhgriI=d79V<#BzzXlJ7ZUo`%zoAxyz1z-(C^N%!t zJ}ov9`R<_$f;F!y zHmO6wv$waXSPuSdBN|tGPZSRXY`{7AtE_6+d#JJM+By(^;S;GaWj26xt^1COgIxwS zwb^)!<0SYScnUBVDpcc@cd_12UFx&F0oMxiRT1TGC#yTSpv|5dOcWn}JBwJJ$N~I) zcG$sD3)x=iBdI)ydvKJF=*&Q&@;_Tqz>Hy51pn;$A ztaIppH%~v&D&%{bYji36G9KXPs|ygX5O_W4pC@I zyx))@tlSS*J{b5JrhKTaIc26+OjFYUw({Bhy(Urb=9K}vI%Hs8J6|0%!!7ZC@21qJ z^uRQ~`h!#0iy_NVZ|!f!V}iy~;)wpAp-W~@qh!-eKZ)b@1hLlIh`~%S8Ccr#%`yr?TEE_w)bk(_3avyi6&4Us-{;nnYaHG}rQv zJPA#_;4U|S&!)q+)$qR|matT91&*F^>kL3MyGy^79c`6XA2%_jhBuDz8$?X2?I~W` zJ2{h{CZW&^ENYvq%b5;XL6(!euGG2I{xhlqXO+jOl(YnE(@S$pcwc11gZAt5WAoT~ zyuAbWxX!(!(cG&;@a5^BTt82qZp_*p<$wZ^atM_Va+7o<;RVrpuy@lAIIkruU|E#`f3s?ZBLO)mhk6 z$p<)vLZO{z9d5LN$@c1_DUkWoyE`%f>J-B;w|4S|w)NTuOY81Q%&e^Y%GG^Dwp4T# z`Tvu8eUuD*Bvbu22Csr9X9}<2M8lI1NqZBmlSkLudw`XUy|?(J*?`jOWXy29ZAQa4 z2BDfZqF9_mp45`E)@X}))QrzWGtUEP8J7bp#RQDs_?^oernIO{BVh^WzZzH-^-lH_ zk!h{*4F_s;;4(j^x2w--E+?EarurKXTZP058(De8c?+Emhmfc(5Y3 z-b?_L@(UL=i;VHA|5efpX5|@wwNC>H!T*b&&4r7Ve_}yP&f-j$KpP*q-}cQuXk~2% z>-fP1u~u!4`AK&#L92ziBl3<`v_G)v4Rb6J?T$wdV*_ zn}?#GeU6&(!JnHlIo#%sv5Jyl=@II99c}zD+py2`nfZgiUDHGtRt}~gLw6x8)~Jrh zjX|E|@pYSIz|613WEy;?1v-RcDm_Z(?0#A@7(^OS7yi5}GiEngOZ_GnH=Be%l1j28_<@b7 zD31TS_s`gTaf8b@&y@A3k7q*4NMz2UhDdYX63WH=N!-aRO5zJS1()LtG7@b0(u?ZI zruNPt8p(+g_xkvwlQ@qmw~~=VR?Ld^AeO{0Jh|-t(Kk6gYKN)b|FzkMLR6v8)KEL` zuA2*~5PCy((m$+tQsQX0O_RLq0#`S%_%6G8Bw|t6+<~^AZW(?&rprz@~umqu6`>V!| zp*pKP4B;w_60Vh5q#$aF;^ZJQB8GHSOkYSv*-y(3-@b$gxGL?I%*{u57t=Gw+7?yM zA1u{#2tCncE#M1@nNbAI2vaTB^IQ1j8pS?y_7@j=`x3|P9zA}{)En*cy*QU zSk<+GZk-2qG$5~`uBz{`5kCZ;^_qi$U^tDa7X?PR+09?`z}CarP@B4|V7z_Vb-!ja zcZ^$hWwo*Tqpa&(niA~D1=@h*I?Hp-tOEwT{Wv1dq6DRVSEj4uy(itGP8}IzcKyYN zZ4%Xfv+5&1_ptSxGdUdm#&cw>uer&CmGfzuE6^Or;yr+F>+(^w#lY|z8OE2QWW@ySMsH+#@A!Iw>(v{2Gws!5` z`GEu2M=3b;fO!^U#PQD>_a@QmW3jKsf=`qX-_BBY%h<-6dq+<#CCTq+_nLRA2P_Y1 zGuj3VyL_Jt9X>-Vv_GmgFy`MT(7SN+aAlS}GuqwF9->o5<{#Ycx9I&P&C}Uw@24X% z)v*JIPoiJ1FyYKy&A2;u$#n}-FqVyXIm*{|rzn72Y4#zvGH;QIoTzB4T^ua&Z)q

>o=IT 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

O0H`F!*BsM3EN*pq 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≪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

jh=h7xoK0rQ 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%0Asbzn?CM?&%V~qx>Dc6S z$J~|9MbUJQSuP#Q(iQ!WnWx!E(Q!rN19M7}*0`*=C{xFcRqE*H&CKR7vjC{VFf(IhpD^-Gh8@h8wdgf73>P z(((7i?q5g|AknAUsD9(D7L}9N#M9dLyqh@=1W(=soa@ieDn0-%r$d3N_@j2MLtUZh zqy$-mW4O5vnK~er4@0?op-e^~$sx&mjiQ_(a;c>!C_F?$HJsBQl%}USvFS zj=!E)XWP=`O1*7wdTu zdji0C{>Mu7-P3^t_j5Gzi_j2Dh1#VP6Q(3~bNW5&8sn?apCI~nYV-%Wa@Fd1kE}H{ zPPb!TI;8k!LU}Nu$>0ngXs7#jd(_77JWb+Bj_x%PMY7u-Bqk61{xtF^ktcDcmJS8) zh9Pja?~r?q)VPr~^vQbe8u|fb=E=tYsUqLTL|Zhz&(>o$@<=ewI*MFd8r2#j8cMU7 z1Aff)lAZpAyVdr&kiy|Vtb^vI=XQ~*EsL|_fcY3J$-AEu*)m!pb=zAyGTGBrseNzJ z;r)ZB(=RXfGGK&nq)@XvT-Hs6i*?bD)QY5U5d+fb z%}m@#s3=b&O^6{XgiD(r(_1tI7j=G~L=i`G^O1ca>!3KKzu}Zm%L~Y=iwtVO$_sRB ze8sK${9Of~br9y8jZWk91dRQYDTrBNM0Rn6Y@&q2g9wqF8f>!_m!7E3|JU-5jPBVN$~zZnyx#Xst4|$dtF?6 zuWYiiDI@D9d&|tu%HFcMMnZN%$Sxu~E8|{LgebDI_uhM4_dUP&ecr!4kAKehp7Z^F zCK$S^=ts=mSHRPL9*s7Tl;5@oRIO^gGW;0w>kgt?1k{`%BLjh#bHaA3xOt{%00->S zOk57`5uNL0+W(K;16}0zA1qMA3oEX#W`%^auFu%eTsz3!rhj(ZDVho(tz~wm`?={U z<>gI($MAA<=cOyh54;pUA}q5=m$nsJ$1!yA;)y$-nrn^bPgFD~x%x@LZmZz6GC?o7%skp>(lUXw3?exjAd zj^35WI*`=+$c4M-1$n|lpy&rhd)~Ts3tGNC>3r{qFeF)C+3@audDhW43RiM^5xj)d zxzxsZ?_xUtI;~%~Vp81pTYP`cB()Nq1#xGG68!1j)43AeEh6kCyH-<^j6+r=o*Jge zuizR&V#dIR5O>tfKd0*#9pSfgFCN~ld>8J~p{Z4JNqZp`Fjr(AJG}E*AXm=h>&e2L z1!E*ol05SfdJ=^UGXj!|DGaIgZYgCs^5TCvM3re?tTZSjp>CtG@H`~$(#fzo(|b-@ z0om6gHnuY@JO?+6oY|?Ye~6)_7R)wD-LSZL&3FXx1WJwvx7pV4qQx6DXcvQAW$m1V z`N~;R}%fRV=kiJdq>+D7j?h>u4Ki4Q*Zh`pPH&VT#~Zy_~$ zzSi<#37no=v7|Puc^@dXDZa`)^PE;vJ?IBa89>cU6D0b@r+4j+$Y;1B1ENkR*1bQ8 z1zF$S+bwuHDTn?Tp&ML%2UDQ$#|?!;T6e4+pLqFzzf_8$pAB?zu+UXf1PS#ydptVS+gAShm{)|@&{~gq zy2E>x_wAJoiBEW$#|SvDG4uXkue!2wtd2#pKfWsA`jzNzfpR-=tnr;;5IGKdu@_#+p9CDfDY(K<%>h7GgN`Y! zwpNNijml$#G<@YiRI88>S_^u(MO1MiAeLFr)R+U%z*4WDX~LkBD~*|twhzoUU3<99 ztnbXFMdyE=pRIqnlsx`(_Ws(7lYeAO=RmaYnuq{H`Dpq;^rBqGm)yB%2&Wg)Vt|&H zqL1R>nP8($4|jTN1F22U??zmZFD#Pay3_t#&-i)Gt5vE-0rqa+@xL2fzKALgUDAbN(Q2|0wPhOhU9-%v87EGBYPjI&O!tqz?){|lF0ADYSAK4@AFM?WmscO{y$>b&{pq(APQU)Km5;Yo8U_0x+|OQsfdFMQDdUe~x9*{C9|Sckh6{D%dU+{g0=lB(yw2;$7KG+$ zQsYBE-d9KH8;>qY4)0x3Xvc@?lx&2%EE4uR5 zKhw+-Z?5Ez+`ECvhA>(rGtO7XgTjFgRL*nWQWX>RJsZ6jqGsd^stQNe%e@M>^Y_8F z%le8St?W_JEVvfDr7LCbQVM!EI48q};e)^ep}h)H1Ty~MS<=RR2sKP(D8YLWkjMef z%o6k7xcr10>=NSaDbIg({yL`?>N(+NI;%5Hyd;W8r~{bsMdNT;b}>!3Z)kHSA;sim zu+Dxg<0p4kefe{Mbp+6$3q|e}CLsVDG%W6m^NbM{T3p0I8}KJidsLL&(oU0n%t*t~ z*Nr@QvecLfhowLFVv{|Te|0X;0D~c29Fzt%!JvbQxCbn0;|w*Z6NZ`f2?;iIF&kPS z81R*~jURZ73)%7qSKX4J>Fo{_%V3@$Tj9Xb+Wd#^h?-u>1Agl(g&t-Oo2YLqxOniD z#Z3Tt&^BNKK`ip$#&#Z}DqF&vNoN9a6-%6OT}MscZB-ulkih%{pg2zqws0r};- z`&x2@mJ`qOtZ4u@n6w~yYy82qdPj7AS}D(MB^ zNzU~0pO6YZ;Js}G8C$ZDL%_e&JJlbx#bcmTr2@>5gZ{!PLo7@$2220+oD<9JHpzmqyVf#QT?;|2%dtegADF;dy@DWq|=^Wr4yD?KtcbO<7QfC19o$(LT}_}b#HI|kRT3~HATQN9U}oZ9NF~7%WuPikd}dLyp`eG4lX>Lr zZO^z&BS-q0i$d3?OL;AT=*4S+V#RmGkH4Y1ws%;&a~wDMgoJ*LZYX93Sq@#ru?H z;2HGuI|6bO6$`5IyPJTkJ%mb%mb|d5;3(Kd5ROa|u_^M)Z;3aS3!V_&Q_b&a-FwR- zHJ1x-_0O?957|FXjGoon$~5BXm=KvXkEpRI#D(PvWo>cCCKYv%PThHiW+9)H)|FY< ze}ugCUu{>HuytRZqZ zhXi_~z;aGVrucT-2V{7_cnU)7rvdP|Xnl>kco`9&l@)TJBn9edkEK$x+Fb}Q@UYTW zuj?*~C^1>}5lXPEfjVgf#}{eaz52(M4%c~EeTgOSlV9UM3-e*4q|4fs+0*e$yTm*x z)Otl!6v65S6c%KyFl_}KGhQ;Y03Hty{yuVjKiggY^CPs6c7UA#sdbuU7IdxSdaEGz zW%T|=aQ7~kkq8=++hqmA!!grd`K0@CqCNtjI1+OtONAraY$U)3KoWmSU&K&u2d%pL z&b{hgf#dUBVv|X|x+3Ea=6V$(e-mL}RvpZ*DB}DzubZ>FAe(%Gt+M=Ij>p|8>xwGm zxG_nFMoRv9h|#hDyl(y5`-giJ2rnTU+0u9Kv*pDYG5AB#fCRR&74Kt&Z1Ztf;|sms z($$YfW(6E~GaDT|>?7U#TXKShP4Au{BE#TkO#!*N?i8%=9aBC? z;%dZZ%8Ytgs>=Aw%;QNwA!cDn%uIw#)!N9v5u_lT&75d~1fEI=KUikHzP?~AxKUM$ zujLSj_-u+j!;9la_w$DmUhqrKzmhospJ{yE=frHv%%1Fb6r5t@Y+A?)foU& zoq$6nv@eSo8VIQ0hxp02?zpK!KH1vN1W7J#mU*BD8 z(RJ#Ur`+HA;dYYeO^b?QS*w=)_pUNWYNq|p1nzCzJ9d&$Q73<;P~@PxC2J`^runej zSfhYN+^*N4MjqT9aqLOb+4e8Q`nPPktCW z7`kP|pB<9%zZa5xn=g)P{`Cw$q#Jv26~qvfk0*M{lRt`mbl;WE3_zwA;TcoAqWWSr{bwpGa|x?U?W3gS(4g*{(n z9!W{g1`wJMKS#3y8E_(UOq?D86u3;00DmbS0AwB9sNM>NUyWj6gQWaEED?#>6AZrg z0n`XduPOtsc#!d)xHK?fBk%iPu#&puWXVg9la!YYk))I~0ulNH ztEF=*S+f@QX#5+t&k^LN`$$#NcN9oMVmEXj@oj6h9IxXftn_*$po3M4 z3uJk(uj)K{Ay078fm1rDrs%kju)?YAOe_`RJ`V1$v${I_lm-j_p*blStbf!lsH^gz zAi&}WZklO7P0r(nKM`aZ0W*>0Oq^2qWra#Uft%*%U}l?A@dHtPG>l9Y!U7qdl}!Ql zw|OFlp~wPIN=fn(TOr~pcs+r0!D(#m`M(V)XD>tuh-+w*zSL|Z{-8>8Hhn^pUH2Ox z{%(HG;n;l6&UMDUDW`XGthLs2^|)s@-wnozy3d&>oIn^K$Bza@LhPvoeA@lGH8enK z+2D0Kn6F#kmu!XX(3$c0CRD=XL%Pc#yU3`}B8}hQcGO6wUnsMbTpI|@+ zN}EX?4eNnBQRf1+&+p>T%!tUJ+%jhP0hxYCho?%DDRiZIq^VBduZ}>o`_(TJc{S#hHvA$I-y49Uy zZ$aWPDIUP7$q(;zO3Y_HkLpoB@9U0WC&KExvyXj9UsEr~e%N|1c{l1dCTW3nb-omy z?%iJJ_&h@J^}tc)&yUfPmcxzvCK|jt9-`lLkhqYXfSmp&-lB|Ad-yjt|NUCuX=j*z zOj=XI3fWyc^{24f8h4L~-4{_7bOklqUpRSPKUr}igzC(<1@QZv1PVfkn=Fb?o_tV$ zKq4A~24z8J6uc2vYbLR1kcPaJR!3LPQ@`ZZW#As}j>sov6E@;uX*-sejpV`2j*0fI zR=VL~6}i;H20-L4tj_V|raz|4d+Kc0WB!jH>X3e()1qJaSLNtDR_I@B)$INv>GApp zHsqCdSb)OOfQmq*?{w34KH=5gd#rotH~F|1E|@uMi3vj!X-k&Q+1hW&C}UEsvLqp7 zm=vYnfXU0kVhwHHp$*0UN ze(~k3Vc!>b)luI3)s8Lb2nhMg)wNIC*eA{*$YN}tk& zK+bbBEy5Kh$(9FVdU2KsunH{MUi7c*SlnW}(8Ph+4CKU;MMRWzMSqfv&o@WIIxXM6 z8zAF1S?mA&xdS!I_NT0IB7T3TV>|t=(c9)zb61RUjbUYD$0E#&=f_bR*G>#a#}sP$ zgG~dDTPZ&GLk4eXqRzx}KgSJm>QdWtix$X>y)Htoy79~mn{q;(Pm)`DT;T$SEbaV4 zm}2%T-h3d>93|MF8?Gu80&X0g!BDkO|JdGQ>H(M*{*T?~o<)l=L`LAP7O`nJM2chf zaw70SXc5>)Ks_A!K)^Js5du*pJSTykXtc;yj#DjsM4&TQbgic|FLbGRn7X)= z8x!jbI7I+khXJEujrC)>`losJn)Oheq!pdfn4pTRBm=IAjEU3NUl7swhDp|$j{MD4tqcAv2y>lX1^Y0?LWl|`=q^Ymn(%lQT>`3{O(09sFP>5vQa^;2atqa^pM67OfoL#Pjnz6}KSYp*i*^u+PoC4Gv43b3>L^w$lINLTV8nl`+Q zc3h`ury2m3&a1z5Mq>bIaL1UVw_y2ESHr``k*QdE*46MdNih)td7k~0D}n~G~w zfW9ZjLSu<`;5tE@T3vGX_Wl11k$?&G7FX| z?PTdVXmV^2+5U5;n?->`Yj^Fj;s~ML`R^kU;EaYj_}m|FeppiaF9MSNT`M5@4vS6y z=rD_3=Vq7%`-StZ;%3|U6O???0P&QnCra*?E4yEfQGy!OG)zw zU0_>WzSMEi@t3L2)`i;i%fQ!1d0!WAyGDmPJM-lJmgwr17f2fI40p`vGlXa$=AF(5 zE426f%SevoB);N|PX8eQ(4F-W3Z=*r>c=I6z>%haHx0}yDY%FYtvMXZ8h!%$xIDB| z49VHvWC)6Kl;UpRsr?8$NZ9WTW*BZ^;0uBBj$*%xjT?0eM^d2EUC4_IO>_ZgzY;It z?Hy9IW8`#&5ZH?+Y^7?GJ1g;IDd|DYvsqo5L;=g6M!Jz7Z*u!s5Op}@Ut>u4*leCK zd>k!;thYn1R2S31G37fiw*b67nP%D?6?2WX1#6UB2mJREmOc)zET{8g(zC&ttr zVARB!jJMz`sz^C@Zip$Ak~aZYTpJdl9C&!iP}_rKe`eW3WwS^lO2~_jTbPJo3>Yb( zCTHVXoG{H&Mm)fQGJT7BAP8@FLm;ykAL7DP*pNTgw;wlWhE(>PW6k!Eb=wL#cE9sj zkEP#U2%EnXebkW(XR+=)=DBFLzTgV%m)7W86sbJoLIUuM*=3VL;kmS)+mL;cpt{=- z-PXjJ7bkL#!pTq0Ezk4h+FU8U%5g|E2T@ZVa)4=ywzQU$;^V6_-BD>A*!>L^%pM`V z6n$Ijx-8GcZ0S@3fMbT#)12nrHRgsu0!PT-CFXP4EN>McO%Vyzq-(c?dbV3eES;7; zG-Ml{B3P4LN#3wU!6^rnJL~7j7r2g3vhOE&3@ZZ zUsgK3T`;n%Vg)O)Qz6<*Tg8Se(cI3pe8IOm>Bl(cvzpi_k)l?C-$W@&&1AAbf38W- z^AnC~>FUf6sB@2&2)=ba=E}V7I!U7_)dWl{UGUH};)6NUec@0t<%gV{q)5eohqV3I zDg)XGWH?H^HxU_00WW-g;0!}oQ7sHMHf7$Kbw7nl z&p&T+zVwIT`Mvh9v^Q6jmvh`8j8Jg>%E;nMcf~n@O?tpmC5gr$A!Q!l%Zx84u7%M* zody}HskLu%#o3UU(b0eMZ}xfkss7*?02ypLvp*w=XLtZB4KPf05c_mds*CrlRp7-rHnV5 zWdTm`J=$r$jpd_6`&MK1A3elTG+k1NrCfr;e^X+A=3ZXv-r61RK}77jyEOkb#&c`= z;Yy_p3q-`En*JkE>mFhj(N2tqZ(RxJOJ>M*e2v@Ao~}?;zixhMSSeMGF;Ab?M`_e* zbP5XKn7n8sp}D7$pIloV@*5$hpla;@>gyA}LYVv3XeoBP>r6n;fp`jy~#(xa;c&lF`{=s;7SmX@6aP_=* z4jOBiM5Q4g9XSTA{>|->X7_a+#;mhmF^UD;SyEWXgi&ET-hPRoTg7tF1%;?<%#u%T zQ(0z8k{MDWX06C=e04mLjXB1U6~6M>XJ{B6ko3H13t7q8dy4?Ox+BtGTTNdPCzB0{ zP@{-6fIwCnNfE#&+b~*g<-%mT`+;3Aw6xTV-#idlpvE82dqF#T&)X~UBkAA$4@|_J z=B=N4k%C)2(&I8+8u2_YTD;egD@{K-f1MKZjs(II_reZ{sTCZSDMSeIogxDA0SdIz z!J0CUIL<>Vnd(Ix(qd0N>QzW(46G+(I%PmZB)+5#oW}wxa+}qdw{NKs=(@K!il8vt z8`g11F!NMlHVObON?I}0l0{Bo|JnZECIEb$( z;mTZ20`g|;j}b8u8f{00XX15!2qeAiVx3L9a1hdGlal%bA?5cG_wJ3aoEAp^631~yT(CZAFl09?(ynK_fa zzxS&OurFj&N--b0A739Xo#<_b#(yY9cN5i$aC#}KEz#8aC?@j0ZWn~3r7a4DC_;1q z>55fYt><)90iQPm7=y8#SdWsX^_Sfehi%BWU9eg$_t5)RU{aI>KLP=7shGFqrNwMxS|i|#_`(esD;4`Hx_WI`Tv8qQvoCLV+~hMd9}0`b~62z1AU zm1dyhD|yggZJ*M9-liC+w|Sakn$?U49!H_4cv55K;NPTuy5>&7d!gdgdM>d$1t2Fs zd|hxMttOPNb+f+ihM}vh@Qs<54cB=x`AFcL>Sx__PMLEbR6i{7Auf+eUn|f_gpgiw|s?Y)`w> z_QoRep&}{;Arnl@3Q)4W-irzbKcwseS^pPb_@IO@GqkB_QD)eO2`pNUQ8{t_M0k@t zD<@$xdpYyi`7C2crEMcwy`295l)YQ&e4jhFeMHdaz9MhS*pd+T2h(JAB1_UBRR(+T ze#v40yPY+HQRd_Vpl*5^xFJ5P*<&6fxXJA<`pnkffCS!rfYpXla-Ed2`tvXkn&;XXivNdR(K) z^^|c(#(>B2<_w|4yuB1DxWqa2C|bCKjjWvo(b>5f$khaxAk`Y%ULydnVqx+cLSh=bp!7hIT&~ zhyE>(J)LJ^XM-B!4{OgHx_A2}9(h3Oan%x`_*(}4mt4@l^^78H`we!l&pyEQ9dkS9 zBb`6ww}riO$xKY}Ftmc;GFjkhYR|SeZe-cDDfb$_jd;SPP*k1ala26;h3ReoGq3cs z=$n2jvVIS;puX4`{B6jaxh=j@Qm=K_;`w{BqlZpWy&3eN22z&h_e!I%a8M}u1U9^`$JgcIIPef_P@8Rs=fWgB8n7& zF*I$D-0H5Ie;*_@_)PXX%wC|ARro7BBJDoS|-fM$x4W{+ha>>4(T$5Sqa zMzrm)rX?AoX;Jr)Q_-~UV_bIfMdl?}==7M~%f}khkjw|{6w7e7(wGO3NGeLzpWk#W zb`pR9He@igT)9ai=j&w6B2GuZ6C7^StnrS>CVc3X?zLqH*zLD*o3?tB;pp?te?f%H zCUeeJqq)es{rM(MI1y}T_KCqn_{ihNSP7tK<7xSd_oMlzkQgMmN1X1|ey=#PvOT=- z$Rw@IrZ4_Xijt_|UIwXQKF+1v=_yR-Z!FG=8*NYG^zdu*5P;LGr=NfCJI%yp2%LXB zPzg;KygYo+xGJgqRw55zhr`J;Kj1>fX+*0*Q&TMwbSLaA6cELp-T<`s^=t|QFbdi? zy~@n#<TFm1(YaurNP$Mk z{>KPS4VBDKrmV*0d=w$_0@qRroSevT`B;6%PV(I1{Ek^NBJYGk<(?0#3H4)@g^tcB zH`8Q-mCD4w9~Dr%Mx3vkS`OXA-jjqtK2Lt%EAQuIuAI^wbCYOPFdyyPm~ zB1oki2NSgJu>pmdEFMx2pg$jYQYpd2W?r2jHX>0;B0ttvtHJJ{%bb1XE;4=6-aZ^l zPc3=zr)+zu*&+Ey9Pd#H!l0-nO~v>6?PYBV=uRN}iEZ_# zZy&-U?!(uLZ!gk#5b|oGUFq=|-_cV@ppHe=M&0tA94;ljy%Yq2%LnLIwCapc`{^2U{K1bRXJ~z#}!tw}i1Mz`N@|6CN9A2UTH*2znQE=Kp0f zP>1RILds7gKz!X!OW6fG#d;&4NclFbzb8UGM(@`jNHTZmi%S7p>TOgaqHhxtNdOD% z&eJ^k5MiYE3cdjR&Pc+E-Ox||tA}hlr2)ACzgT@?CdeT*dbsytm)I-pdrE1_aPypYm=R!85pxxOx~wiYo6&nU^A`@2!eh>}2??@1?$1i` zBHu^8gNtitJO7Au99&rtU7V`iS9yH19&<5#NpO7s+w+NteHSZcM&q)c2u!=1!ui@6 z(cwHeu55kDlz$jhtJdGmhK~nMMZ+YS30tlkh>Fr4jc?gur0MhQnhWmv+7oxi&ow@` zXoy`*v-0S`eKGRw+$!={*tG6suiV#ce|H(0+-Dsf0&u|Xty87pWG=IMWS90+PA`a4 zBWatvupwf5G=wPia;7ci_6{e*yN#>iPLDF+)sUEC_(Aa}yO2=iO1J=yBrfE0+fooY zx+@BJu-`HXsaaD!q+-2xmc?E)+bN^_6S=BdvIO7Lz)UGdqo((8+p$}G07H%KQz=4t zs+Ya~?F8&|o_!S~fj63fE&Xl8@en^;PdtR^tY=kbaO%Z0w^5mp@AcPv2Gq6ecrY{J zlqCY2t@1*o&yR~d@wrv12Q#fI$Ty{MKf`4dbEFB#y+Lqg5u%IlrdgWXQ(c5?KpvwO zY&X(|LPcm7E{xh=>`&Z-qt&0lV`((|#<31WP=U!{s3ESknK3Kn?`Vjemk5;1tvy1{ zQS;_`KJDNBy+;=-CK1d97q91|IMWSX^JBD3C#?cAGRRNl9hj)c0GYoE}Ri#$k z<#oA(mBAu(qM)$1kB}4_Ce!wqHOpe-+|aZmiJlZBCrWS4pRS@_-l3EA z4gxn@b(+~t+bKx^n6MJiUlUiNV@ndX{&a0N(a??GU#gG! zQ(i@#6i*WM$@JWmAg`#8H{>9h7sBtBc?$$msfZ4(?;^kRFI)|bcyhVMM4!I}=a6BD z*~@>MKz1?T1$>RNV`K)G&J-M$6gbFy{BJYNoy|=dqb4*wF(}^uKKs!eIIDwqRDanw zZ2mMxL7N`l3FzHg*B(b2qqh7Tt)}K>Zn=H>LvE_*O+&WZ<-gdz6?LF$N|}ewGq;Et zmb}TJqKrM?N6O+Sf5CklC!rikD~q7cF}AVX4Ea*5?jTYTL_2MBg?Hcffz0YZlu5Yt zaL4wv-4Fz4eFpJYU?i)mUQ#%g>ScpAPM@T&auDNB?I$>A2`J2M76f8}4+RycfIWf4 za0(~@yc!MMJS5{_WB2LKF>YtzOm4S768~`rYHN0IUoIu9CZEq#SC1S>^wS!nVuSFj zvTMEW+rfZABT2csTzIhG-cZBuC?5O_&WAi{q%gH|G0^`Q=0_>#4{6} z9be~pioO8|}k%IK&jq1|EglI;g8$Pk{2k+(meNQ=L1lXLiC0fsoZJi8lp z1nnU>cx2Xj$83zfqTV4+5_poVsq^GxEKch39a0fUHoY!t;jm9hO>blXj0&d#0RBuc z(VQwfWnp>ZU~3z7&jsX|?lReQQKm40W<3xXHU3jG9-g$!%6D{TO@cdVb`zqD!*#3q zyDdq6bE?h;XY3#}@w2iuKi}F4M=7eRIaPYNb^JA+fd|jV(9dNJXo6%? zMZCK|gWHb}ct}8F&IR*jn|Epm#Hrt3{-79v_3+^cB>Ub&e2KV&-7k-YOunrA2ER70WDEf zODN|bEA|p$!&TtcJ@o8|W$s$zX5DcxJhK*fbLN?HLH40yL_@VF0-RH27A8LD?;&za z+?l?GMqWl|#Pu%({oXXL{wQ`hJMG^qPYyYC{PMNxlzPJX+iqS2RcGs;FEiLv+f$1W zt$+@L8rxwJ*}2dBBt|2eY|a5|8|>4RAr2kQx7$gHNSNS+#(vZMenUZ-l}ZtOUbSW`5BV5H{9>x%YlmiTJzaoGk^WjL~jlzNg_O_tAS^n}FOzGKm3V&v{ zS5Jbg6}i;2#h8rbz$lO9HnQPN!|g~0KFDjkL$D7%hcdH|%K*T!lmgWyO`m?_!OD&0 zWNuG26yCWzv^5rPPINLS;rZ^kG433<+J2~fklFZN7bEX-UWSMk9|>^C)@?M)rzD~C zDj)u2=-_7^oMe!2yOH$- z0(n5PlHj-(z|AIRVJmyu-ph&4!v@gvSD6|^`e+4AM-v>hZD4Gj2@`u5Lg*_9yw;K2 zW>XddrG>$8pr+qZv49imEuOs~*6oZhQRK<(uICVWv@C-+#w3|S^X9y=;9C0Tdxx-M zHdYyu`_nF;EL$xOYn4*7;3}FS?FU^@r^W>m8}eYr=d|KUr<%j{j=}A%v(fAy!~F0%%i6Tb z94Q+Dd_<|f7t^el1qZj!!^t5KNTMAjxlIxj+CyIJrKqJUyO!UDUz3loCRj}jvw5as z_L7LL-X7mn9FA0bS?kG2no5_J0Ti4|SDx|1*<8YFnEuTRd52Y4GT!;ooU-ya+~_I* z?<_Xk^Df3Ed_Tu|z2#Qqc{S1UjUA9ICxm37x0zVV-y*2(a55Keye(M4ot#o7e|gUq zjHI6PAO)1~(JGC53piGZL*W^6=z7Xe@f(^q{tnhWWCz^~cjsVg(oAgXl^Xe7qKluV zSyHTn?%w^J*K>tZ5@=s4XG7OVUBa2>(c7NJ~+B^;(Oopk&N&TRgjKe!0;b{O^4WRrog?{({MAe`yC;(2s`l z)A?NuSzk(_y`l45OgMH?G5K11$SGzKJE(TLk53L67)EuLb0$oM)%NIK5x<-#SVA@D zfW7qAVajxR3m}r;rXk+`eq=uf!qR4?uY5MHwm!67BVA!#GjYm-&$<6XF5Diozx^jwy{A7MBi&CyotCSi1qr9;%H8Y-5c~l<=(Cbb2ocC7(TFoRQGJ_>0e^(u)N4@LDN31D* zzbBB&W~1DsDZ}pd`P^wO1Fgo!uHts@O@u2Ug1lYqdBwJtq=b^A($F?fY>Yg)ePX_i z4Hx{76-f=zqS+|tvyn0>`Fb0|BSgAFAMR5g59U8&wxDD-vAtU89cJzv?`+cS_yy7B z%~w{jlvruXC53{iTe8t-Sr@}*SpSc(Es%e{HE->?wO-*M%a(Fs&fDo0^_#xJef8Ki zbl1N-f!cU^&HQAIX?$1%HMyN)aMuVM8lW}&ROIWr)%;(dA@zu1T7;f{O01l4G$=AL zYg_mj`pS7_6saLnCfv3T)QSZ^8+W(zwDtSeas6%MnahU|u359IkbNLTz^+Y<#hb`< zrMGj3B29T-Z>RC<+Iv!nHhA@;rwFacLAh}7Kj&?`Uwe_6C>C-A1W5=j7Yu_zZ+cm8 zJP#$UZrDG^slB6GpvM_+v0bEbjRU`|5Eh@5Q`tc57I9q@MDR97Q%Lm z=rad_f43qeuR=lCDwz9Ob7LMTrF9?`NNM8Yqj;k#1_@saG@(|D;&r&2cymmw|0C2N zn|01rk`wEp1u0gGy$U~X5whQsex{=@3>2pKPRfQ2(a0l5s-H}=P6i%3t00%0io>j~ zkNU>y3h;B!s!5*g#acFQyjgo%Wi(6uYDt-GS2FTNFPY4V{%~Dd$|*PvqUWsQ+Y!Sz zx`O~j6wYSwah=OroY6F9?v;7XORe8Ufgp^*&clm?GTgivbJJu~mkwGbYq^IWg}`1_ zUh2yx1(+;>ZWZk}UIv3y?dMSAR)XY@iZ>J3hJ@SvZ7kQ>f7~gO>+naHdQ(aAvZFpj zq_HQ*@$6J|t&zk5Ls53xnpe_i)moleRVpoGEg#X83E&7w@S z>yxf9PGP8^<_BxwM2NfORe^fDgs|s6m-(No#QcekCkYqrkJsis@4^>+qjw(xLw}lR zVW}Up0M!dJ>)V=xw5;&EzRV1-Wkh;JV-S$OYlF4wMI(xgJk8`&xRjRWbZWMbA@Jb8orVcXy5Lxm4-*UZ!NWj z&AAC%?oi*6J%VqnGKtu1+iewAUu$p4gULzmsD@V0-MP>=!8unKU#PxHjLZ&t_ScQU z&B+UUbwOi{Q*!)R6fwsjOv+v3W3Q18FvQoVwv-!oYoT9{Qy#SqUHlV(qbq+82Mbk; zh*Q79OEwMqUSDlvhCJZj>p0VtI_e;SliQ*xlcj_-NpYoYllaHKuz52@0i55%GZ<{_ zF=XZ5*sEWUCb1z)C>jXr;2H}fRlMPeanZQpVF6$%^H1fC=Hg8>6^d#x{D%d>)|2NO z;;-em1LEqRe@{+%ff>_uR%C~;H~tzVd6-Hv741Av`rxh&Fc~B_9*CzW^+%cc#|)>> z*jmtCqaSUzW6vR};x9W;6?2%N9HF#IIwPpWTpH)^(Ljgtlg*$9cZRinC?PhErBQ#q zLMqRE@YP-bo>_KFFAG007@zL|puufHEa1AoNWwJ#) zpKm>nvbx`2LDZ~Ta;qt+dNixG*;DuGfiE`kY)`e-nQ+5~72cfNB11bVtI+(e>Wtb{ zka$++iE`cxj~=T@4wjM=Y0mFP`XAD0tmy#bF5THyIa)z*4zDFvkl50i?Rg!WZ@zuIJ9J5pOc8 z;!HtckjuEv{v$FDNkFz7!Wv!xwhO<0BtnkESxvwWnTYRt6bDJkOD&E9`pvrFu_u12+Z}sS+XOa(doMlgJjEvYNBgB; zwwcg>_4ypFJBn#rzE6dOLasvl+*}XRz8I{=IEo!$9xDVWR7<69KhWPhK7b)J|Cu z-2tu!o=3B^Fw>q&M-rms0S*?(;#V-0!bHH?nfi)C7(M4{biBLI)&OhaytZe*)(_beKZj>!n-o}TMrIy}&)0#Rh5WzW6z8qdQEgjK) zt3I#O<|MzHZOWJyVD&F9>2i1z%Y-bYQq}lQgb>{O5czI9F3RHd9mijNb5O9!H<-w( z0D6Ud)W$QKuO3LKvc7re`zcELoI)iYE2%+Jgv*3amPh1GJ@%sz^ya!6~h`MWI|CSCIK zc@3EvW~Zu;MZHim0@Kvtqf)@hz>v4w@$k#nB0Wf9Ud?6C2E*b1%ms|D0hQ#IZL=c8EyG zI>?CZy*EjA_Bv*YP$4plQnu`sk)4o?>=BW@4~OI2pYQL!??2A}=kYl2bG@(YdcR)J z*CO`yStpjj4v#J(T}_DNo@DY~MWN7?B>Vpu6nwYvnF6zaB}l9I)GTO}SB5}<96#pa z@J}Za4RiAbX6Ox4)EJna~OlQXts%P)H4UmoWX zV}<51kM3W*`aJ8BS0$C7eo1X!Fn?!^EB@7J5z+ByC`1aH&i^M|ij<@Zj3Sfcp*N@L z$;UW+$tf^#k+i2t{W7um@;FwmzJ{ij7d^kuD=EWGy*&9RHc(Lz)j`IP5y|7TZmNTE zytl+H4Ue575bb`H1%tTnzhurxkYqxlK=BN>mNej(Mi=FFk64-C*t3#13RmrKURSuOso_3Iwmz~h zlR6QsCYQSi)Kt6l4xM5S7Csh`ubS8?d(&tgMl<7{YlU^$8&a}9{RJcoPl0}|as1>T zwNEcN3dw3D+N18!6{PS_*Tko+5O%7^*}wg5;Bcj87x7zNvES28(ROJ!zVy1z9oMUa z>tV#X^c-ybwwn5@+8zS{OIzenqwhBOKnWzu8Hs`<@3-yYsV~dl5VU~Ugjil8Hks=J z{J@kqkRlXzJV_$f$*sIX;Ff?gxv#vUN2#hMAf}k%RpbwUvff{L;BU!AOvfAyeP|(d zz7W7Tzqd)$ytSt75=u)m${9AOIT=iQFToTI>rXBVlWz|D<-dj~Ei_h;v)IEtg7jr6 zHPQzVZ^;>|w*}%=Hl+~(PBDp;E}o?+pyCsj=Tv_#FsUYH%=`LKA;F)8x`RxQC)Ic9 z1ce^1v$;w^G;r{?Ui?GkC}AXvN6MDL-i#L3OGmu`?(Xw!s6WW5b<-ii20Ej0DaZ=h z742{BO+lW)gBL3b*|=CjV2$95(K}!noi3 zt4*2N>D)VJ`1O=g9f>$t?4+e!6>DK8QFOfCwaO=VH2U3JaKsOkc3s?8hF>O4k6=|V zT^$MFKJ)}(n??XNC+yrNRGS|bCxsaTot)U}rexJnxtO{>?@I!eY=O# zJ9bgQwyEFW_PW!zc`AReJFxF$vg^t|$x7~670dU=ui~m0h|11{o-{KQXt=aM>`0|| zAO&mlR2m1E{wZ=elDEC?Ne(dtjx!1oGZtGSx=GQdALqs6ZAIe=hNJ)KdU zLC6zY7GAQmu09!uklH;pCP~`{_E!)~*Y7nQ`~TK4zkg_yZUAvUY|@v5j4QRAz`+Xb z3+F@i#D4o0yPhwDd08dqxQvnXCW72%!&G+Oy1sxn4U!Buo(a0p2~G=@6{;aVYmtQucd5wS{BmHLmx`E`tHlR~TvbLN|Ebqo}a5($!K94yq~epT196$ z9{CJHs$L7a14K5AIQlf0)8dDRJ)r zd)xj!jPI^p9%qllu6ZzrP6B~vCx3(spyhkJl;3lX@9#SAHG9$fPy8u!4UEJuyhbY~mgY9uREnNDHW9%>!mCYt zb(!moaNe=tO~MP+>e(Ig&C1fgEr|^k;_VaNXL-Mb6iyAlFCHN`MqmXD8$arPcW<}G ztZhM{{a0kr94vg4E{Y5~K5p+FyBBoj1wz@DB4NHZU63z_oGa1XS^}7lq;Sk$(@Aa+ z(`&O-)F3u`2vNl+{Oo1)KE%pTIca(L^8+&ulk;IsQAlg%Mhb5SI=D%w2gY@`usCbcy`)`U!2T!J;~rvMSbb&)7joB zddky#iQJ!)AOVu-leodM8B~ty7_@_XYkCJ>N$wVjf}}J5bZ?BLD+#GjIx?#9`f3%x z3PmiWr!vw4uG!2b!VB;xoF))(wUGfF|7$og-#^J7 z**#^BG2HW+U-{`+D(l~O?4E7&v%1|_X^*k0d7dR-{Qxg-W`~`2e{nqhv+WpLdGgaE zAj)N3H(aO0-LlG|lCn)*`DTV(y*Xec%GRprx<2vc3yQYyPF20-ciTd?Sw_$DgOIS8 zFAdIjs2+MnZr9f-d>3J|+bW`(^K1|uu$V!36mBb#k>4wrjUYo97HRD%+RtY0$bWtl zn_<6jqcHKLYO3b%d|*{rxDw{=!ov|-w0N-)6Z!>fbu?&k+NN6Zirm=i`bB@8W|GcN zy5rR|NMS6jh?^;iYB7X4MSBd*)XxQUXrdFKq_Ln^WX=>AVv3+jgqut4Q^oJkzd=%~bO;dKmnSQvLa4X#ZXhpJynG z9J=(zLy8l`lJO0Cl$*q~gn`3@6rQ=S@+wMTw%nv~7bLkX4-m?%q^8D9MZhXQh@w6i zDPaUq9W+9uRlTYJhKX9Hzglfzk1)LrY*rUQpyu;2C-h^1=d6*qqqD9yMbMO_(6S%; zK;r(TqTIrR!2O;zQt1+#Ri432iSEc1Sb>L-kXd!|BkG5(a@#eFE2Fc$_qdFwBE|zu zlXEC?vzrM|A4qNzH?|V(4Xyni-s2vXoyslFt;oK(_lq&tJT2#yRe=jc zE~!-2+4;zJ<;UVS${I#l#W6tzjm#-l^SvSwpJ)i@;3Mez(3PUL_E3BDLU97&hE~sp z*h}!v_YiFBtPy4zO7>*(4`^YL-0>bgs{B0coT9&OQQCJrSR>+9gY1z$ZeiT& z8J<72VHCbu5c6l}qzrp1=vc3_R6$ieuP!j@niv}p~{LG97_JVPaXUi#< z*vw0ngdrlh*`FJEz%n>WT^F@0J6pOG&)YY=f5ezYZF&ILAm_fPMuAY*A-yk7-Q7Kb zH?E-gTec+D{UQ)aY1gMSyS6>^^|pk2c+Dr#`o)&IKoEf+bdqm=FmZUX_a9hV%NxgoS?E^Teo{!-UtINg%8EhDwFJ7P$i3C27 ztQK-(9*7gGcf6G>VD8G<#l$Ai5p}ErY?K}34bEL=9Wt_jqc#Z5V&)P_NL#YGklrl< z56mkNROxz|4Y_C*n}^rmD#qhGU9Lk`&B2b zPTA+%-(=(9r0s2&ZowOA)`ax4lms4z?3^Tyj|*!y zK`~^SP92@`4<_y!_z^6I#rDKMZ$-Mi&TLrwceiHxPgHF`zswuR$02qljH6oXsWb;I z8_#TalndJipv&P2?&+_mV6DPUxj>5P#L1$r^cjp#@ehDt_O1xTEm{ylrwE3Vgv5*g zw0QuzfBdq|lcg^gWou!zr(a%|_}4W2`KO^c*5l^2Gv#jf=X0(YNyfgm^$E^Gs=+U> zO|5eMMHdzBC=_op<&I~|tP+XjIh}ri+0}V_y#Kdgo3lUrdC0}6f4*_*iP^bXt6kXorT9X4BPi69a3u0hf&x|&pcYv;LoWg9s05%X^h>gN zcZ%&$hXdXidF!ln+KyUBA|b>>gL#h*ptR_;on-PJ5#%|qoL4lTYUZjrzK zWb)f;JrE_ewh;UDBNe^(sn%wRL{I`*G@Tf#Zat+|hCPq_R0#+j(0`8BahOlZN=o9= z-|s@CZoPL6a(nFb7k=&a^5ZPtH?|<;hIf>Hzsrvnc=pxQp+izc`mZ;JiHXaa8wClB zV66N&A_>MxBeuRITo6DJKO9cV#ehV8B!h3X9~`Y*%B7uEzKYpQJYUbhG;ht?bs!eI zK)eoAzLffrDdx!cR8coFUdo|k6=R-HtIu_HWK&LfyzpZ(!z(>fNRU(Zi?ghH&y4M9 zpb`dwA^-f5==K6co9MCaM)|c~-*?X=mr48ACFhtu#dvoB;)?Nz+fxWbMSqXQX=D%O zc*^wVVk`u+c@6=(ag|fQ!z`d3H&3Eo5qgw+A6XHyzN6kvM#7m^CNE{y>H`6*GUDs& zPZnO@ZO5g%r!uZ6PK!>49YB#Q?Zq58z-kZ^P=6Jwh*(}s3x>9ec1M~_Pr3f0B@FEJ zZ~EW6419)f?^j*m5;u@jk5@fviv@n$w@8s;n_qCijI@Xk8MAu zbI<`6j07_Jsc^hQmkUJtL+z=?>pR33OSQLt$2%yN`#w&YQnVJquK!!l}r>^~UfjG5va}gt@70=aI4! zlADk?p!p4w(~t)kOMdzD8k&F8mET)N7^NiKNB<`8#_3fP|Jg=qcqX@<~ho-M^oAu;M2{bK}+$4Xp_+ zB~s;H2lW8a=)7Ww`@yeSZ;Yx8P>%xqPKR!3TK#DDB=K+~J()%dA1vm#ogyxG86(S+ zpb{_5t_JJ_j1mY6eqpM4C`jDd2=&J-F_{~30FEy#o}SwOF?0pP<`!#xAMq!z2ZO|A zApsGReul_v)ElZP4XE6DHj2xIE?*jI6AL*V0A zFZcX<2K++vp70WC>I0_am_MN<(p>Sem%$t%_|LVaLpI%MY5gSCJnF!|t_~ep#ys~~ zD5Z(JN-1-k2=^#S0y{tvDNifyDrM%n`hHoc((ZQ|>BId6M?O$d>iWPly6|6bcO+TvnBt0CO)Hl?N!U)5sU;hvK~HA!W45xDn@Mo=hw55f7XA0F7MlO zdLf2k6dD=FXWv5t1_^g9GhpNllF9)Mk`cNjhj?JKe<&Wysiun#|z8Quw-a)-T- zJ!X@zSkDviCZ((`PTIQrDYSNEQen%}m#ZMDu5cR&XIIMu`cZ)DYLYYPwExF=IIKEQ zEbu&qN&54+XcUq*H5F;)8Iz3c^k#7ewN3F!4(y0UvQm2O_wv zPZ_UkXx4+iC^mb(re+DzHa8MO6EsKi-T$uz$lO4zrP&k)csQ+Qef|G=`rem@4A(g22Vm62c&;sc`IQRvl}c z#xa}&#*zo+M=ILvgJ6RnNj=bZ7g0<)vr?oVD7D%^q%xL%DkECUa!NGYLx^pTu|5{@ zF;24H$siJaKuq!JDVM3|r3_6eX7$R|r|;p&qGjR&JWRSe{0}6xa?9YmK6)NiJ{kGq zvx&Yp=2hDK#sUB2ubddpCTKWBymD&lkZtz_xo zz>?hkn#3F#2#*Rzz=I+HaqHRL><-kM{W)^lkD&IXoF8rB{Q%;lZT3r&=K36KeoyY1 ztR>Pe{i?Pii`MR%*_&`AFdAV{{vY0B5eH&W*KG;hZ0P-nt|utAe#f`$E>iZvTKbZo_}_$dFPxaQjKTWs_3Z>*W5V zZuKx==kI8<-i(Y+`sG3>%76CfPQ!_*qcu@XCufjsxh+$=W=(Bw4}*PI*fonse`A^xGce)v>pl z17|hVG*Z&DSP~ch(%|E%1rD@PUg}7IopgUgWn{T8a;bQ~TV=#pNWnz-78xm$!Rl#( zq$*EY(B5TvP-SFUEKDqszk?;JR(vv@TILxbWGRqq1oq8S68N7|Y=8Y?!~Y^Umycbw)yf9$pSVu7cI=x#7>y+k%*J6Uj0 zXU2jcg0d57M*Mz76QJ2na_8PNZG6kEvEfB|aJMRozxjo7C$b}ZxDePhr_;e=GyMasRNFywPJ#35b3R1dY--JK=647 zKG40pKkIe%3|Sw%sz5xH`kOF7?ZS^ntOe>VMB`%IV+;~cJ8+9vclL&Y^+(xRh!%B- z;NwX^JiZ-4)jyld@n%(brpkSNT**>J`Q%lg$ET!d$5=AbG(UBG-9Hk^?pOn2H>O|xV^6J4yTqJ7DuoQeQCYY&VSrta&f55xUQchU{fQv^K~(pvQ&IM!a8=ksN{8_q z5-O7D{2NuBcKh;OtbG6p&4)!!?}8Yoyv&L(fdjFO>wBJ{NU={&+bio#_XE-AdbCoo zONi+IHRuGuR4l z;0xnK{V)9Wog|2VM3DB@ECvlD6;Do-rs+@fes67kpH}fKJHA#M=_CKe z`9lo@^!Iypj+64;bd{L-#aJ7l!xn^z8FI~wk`~mn5Jh9 z;zdQiFF>`FzM_qQtm1s>)*gzg+SMfdAa|aX?AudIdBfRe96sgz*a@mFf+^Lk{zxGl z2ATY3cbmAe6Wi%CC|u#MxsoslUin|hB_vM50a53Gx&y>K_Lqp{-7qReXkk*8UO~y{O zyRFs%w9W`k`h&i2IAuYoZAWfsJJ${f=YrTHKr;&((?|?*r9N(sGkKA+EYJ`p)Ue<4 zulO5TZC;;GjjVWTJ7^nE5-RuCefZ;{0S_?Uxb@7)H|dd2$vULjL;1#ue!zn`ga{L$ z*_6Nfl9uh2K*xaDrV2D}Jcrw(ll~3i(300nmzf9oLD4eywVM&c0qpH)(B}F;g)Nl? z1lYN3hA>l*?xKKXnU8eo?c-0P*>?)JOf>E*-?9RJ`_Tt82Xf&ivAr!Ud zJ_`M#g5g#RoxClcx66p?&Gtx>N4r$B+J}Wgh2y$Y zoXtzcrH-Y0G=*ik~1#<|8o^3RTDcndsa)g!67wqFyVs%YiYB7^5EF~y~WzW_CZ@>xSo5Twh~5@!pxB9G!GD0FhXKp zf=)i`vu7!IZaKp5i|Xt4_Yv+CD`0ZF`&|bF&20WzLSPHyK~yO}Wc47P`B{`CUu?g$ z_;Y@=(|!7fHLx_knQ(h=^Q~9rEhq79FK?UWLm-bC*`m5wXhuU36!iG3Ril(y1sx7Dqtt9k)K-!<12Fn4WQbxF&JN;QdBPu`*4}e_gD^6 z1{y|w#|`m9QUwyC72RY9A7c%OZm87Kzzp*2ej`XzkxId-DR(nz{`}7=rGlmrZ$}=u zSXH4h)m8U|&3Hn4543_#UPKxz0XhyRR6-e5AgfTPNm0$a+R*# z5Fu!q`ETF`esawg5_Qv~fSvYtQw8@F-sInUEZc<_T|GvlYtSc< z&pgF54W?v2SPaYb!zudU`42$^53Brl z{Y=cD>iK^w02zbr+BPM_@}Ip({rG#d#4U5>VRZZzR&aPHcXGw9HntR398L%31ix-^Rn9~9;dMR zfae}xU zlP|RB?bVPANh+@3cOMOE8NdmL#UTzyUL?suYQ@~?=YAG9?C$=({L$Iw;MuVL$N2mv z`JTLT51Xz)<-D{mQ@wsml zPcbQ3Y)C-a1V{cc$5p;CjUL&dHM^9l1oTZmdi&)S|6=}E?I0(xpN?w8E+&#!aB%=z z&8Q_(wOC8r&Qff|HAFQ?W&H*LwE!}Iw?VytrZjqmCvcOA(&}&E8~)euCB~~IPMC&x zQfSJz+x8z}m)jsCon(n>N_QpGVb~k>eC8fps;21D&nPw6cUCgdLisXHUgXXKv(z6; zT|`3Yi{^Ex*xn>ZVZJt2k9RSzApBS)rK8S8AK<(D;Iq07 z>dQwWp7<31ETb~Q@B~insca>ipTM)BJ2kRP7}(_#40km5Qj;a8GS`(K_7aT+=fB?lKz{N+3+8!pe9F9tibmBKV%8>uaFPZIyP-m z9eL57LX(yMaL@Gk$#SQR_P#7d$v>^KR#B^Id-HXdVW0G`A@+qF?Y2Zs-tr?R1vihC z9}@mA;-(Y!Ovwrv%QcTkdw$|gf+w?Odx*puZMYfC|J}z6f41p5RZ6jvWAcDizbLW< zZuePK*{!~H=y8$VU{F&v`nmT()N5pdJFC^a`T+1xP+qn|zu|R19)) z(!4SMk*i6E>~!pqG@mVuzd;URXn2gErMJBAbM!LNC+%F;KSk{BPf?f~wZBQw5Pi?O zEtvwMlMrT)tz;2Ot>-!^R?IlxkhMP`%SaF@(Ui>Ckrw*salNwJR@rU%{kyseP)rgT zJHP@b{rs?lPt`6mXV}e(#oAr}r2n}P>mE0a)wIlw57Gq)lGJ*WZ;;=J0cU7bjZ`P? zNT{4|e;&YA-J9TO<@2Mo`@B*}`R;XJcyi@R-IgtVB?iQ;I=skY5>xB;SG9s&SJ%^t zd$%lyhg1-HmJax3@6f!6WHE3#Mx&5^|KQ-kCi|PI+-p ze0?Q2m=Kq}jUfc*cliNhGeWxlv%_LTOl+0`wTl;UsU7{oiQWxWthfc+F+8;75_%yAoZ<#9|4G&= zoo>GfHR)RLof2BWW93!fd4+|vo3t?d(+Qo<6l(zLlLL?Ke}3N%0j!h(mL@?`J4ZG` z2^tto{BM(4LIasFeklLV77>`i`Pox_Y9Q_ zSflPRlL6(>$$}0&(Ob0+<}fH{XKR;jd$R0QyA>MK4?>SKD4^c=R!DVLc>fsiUcoSN zrkCG!3Hk5EgS#HkVE6uEK;Jh_De+R8$E(b2E;Expe5+-$DfkY7_?_z- zD#p$%C4SBF9IhT-0z}>1+nl)>(@AL@F6oX~AMKr2nf+1`QCsdmHH2nr32>M|3cAV_ zKhYJIy2)s?70C_ZeW3J4A%`SRa1*cDf+}ItJ9xYQ_vFT8B#racb4uyaSOm#~*8)c) ztRVVKj*s^$+r{TXbdrz_memSEaT$RT`#)WkfK#uFT?>*);~ySG2lSNB{*kNyw`4pV ze9My8Tv5Hh()d*U?sfm!vUd%YeHv1A>lAX_I)mfkkFv<_9gm<>ejWbsz|HpwVvAtqZZI)6t@eD<`=vw0*0))B^O&RC}2vB4)iCTT!N(=k;go+je?x0OP2Td+d0LUBwM*@{ZkbW@4ZSS0Oe`f@bf<^-n zGoM@<(8n3v$-<***FjD7%kXS}S(TZ;5e6|)OF;L#K~r+7d|gl)Up{F+;=lbi*y-83 zjBK7%#_y(sFd7)IEKuc9@3CPR#~k|h8aaFerwGxe@Ty5z+~TYUT1IXxxryE6OS@dv zW)67F^un^Z1H#?nc>()30Ko=99-oA)4L$!sfmFwi2|j{jlq1x$bE;0-rm@E0Ai&R| z8=G0J?m$a9TKMA}*DWxuEdVwl!{FPS3JyW@=K!=*h4yoC-EX9*l)gD8#b8ra1|hE` zD294r-L-t@pW;o7PZY4j6a(+#S1F#3r>1Efs1)&N8kKcFTlxL+y(qbc9UPH_qD1oO zETIDIriT^NTGzPtRZ`)pDE@wxL!_XNgE?oFvueh@1NYmjb2TlcCLAW^o5rQ3p?iM2 zdPS0<);TTM-HM8m3h?tEhnS3fBmwL7=Z619~~^l zCfMT32@GBfk>xA`_k!>0E5E%Co#(>r8($Ij8AM;4*vgoV-II<>&?_f^%5^u7>d7GL z6&%&^pkfa_&qEvDk3ZTz+#RRSe!%*d>mu{&116odV;lT&V?XRAok|h?%Pto++PeDG zPBTi&+7y^-Z)4xWRGMptdyV*qDE#bDp1XW|mWpf7P(McvLC} zv+C=htaIe*O`?CCAo<{=Vv-tMTQH?@q1^$Ru1D}v*?UNsEul~XKsR1R(a{)WB0)nE zjl<~}d430i_U=kM6ZIdS$;h{GBq?Hwh5%{XYJX$74H^y)U9C#+AG0a9^^g~i@!S#V zT2?@bSSYVqyQRE$aq^De?MpLQwRPFqdFSRQXYUt3$j#`FCQL@R-jxU?Z$9d2zMMVR zVhlQEspAPQaC(mqN$!^MFJ}Wn$Ffd{v)Iq;W(JcO_7z0E@SMg!EL-K4Vc3F^{eX)!kOfMa$M!U6O9ZeU0h!V`~=Osd&!rbFGpWXA)npV#YnIsQH;WI zao?ZUBn8%qYME9n14$ptkQ3HF8HtU5Il&-b%)T+rPDTEyUIm>8Y);T5V?CK*fuFLQoX=o z>3s5CpA?Bj$GmlVcxL{x<>IAr&N*{qRV*wm+8GR{j^-UXsl;ei;BQ5f?WZ?f30|cv zUeF61EFz2srrtN&O$A%@vsBO?mf*OQeVK- z{^KT^j1amIZ`4VJ1{9k#u%ds?Lmxpc1~qXFX*0GIRi=wv^?t>My;5^p@8XFwWg2|0 zWpvh-uT8WLuubVEu2+wsMJ_w78>*_WBs2xI4_r%5^!Dy5ucd5oqAQkf>BBCbDY^cr zJ`GtL!3@iT=CFkHGb#bE0c~QMk=KTDTyeBIIbz!UMPl~P1JAsJ3ZoU{&qR5O1ntRw z8FxoQnZSkl9xgHw-#(zd;A|j28J~BacfjEY`~EjpwYvPo>Q}PHiu%pMh4<|wbY};- zRh90a#Vgm<$Y=G>r`c;5Dqh}nPjQ=Te@+!wMQk_w*CYuNneh57w46Ar?mStz=lSq< z54~9ZwYwH+oVg9>klFaLt)0PNqBF*=*LOvZLp-MM??K43Cxu}1WUxPzi${D_>nTiK zx4l9mroU2QN@4SSTk^piNIU-*+YJagDC9Vw1k$LS1KOt!RM42nonHj5h_ld01__?z z;J-v#1DUrRZ%Sl1gJ%xg_$_q5hfyI4{wNO+1LMQRb|uKw0T{WfUpJa)@Lyhaq zt_r3|wAJOU_7`A!fbrZP4+un4ZTO2yhq$ZL&3@;X^V2~)S%f3SD=$5>Qy%G_1VQch zPqYyr5gmT1bLHXWf*|6D3SbgmDx(WPQA};M9r_*adaR)_16QzFK#*|@WpKEh#^@4b+)}~w9up05QcCrcbgC6JCF|@E~HaTgiZF*e&9u-qhKkQL!t6kr!x`B zy@zcw8o{|MUJ^AX;m7X%2K{U9zYN=`Y!+q32iu=pmu`Hg-z+d!AIBNX@kL(>P}@1| zH`&9!^ix``C!WNA#jS+`G#8?`@U(n9vHxP}VCYyXIlNLZ zuD=BL(H1t0b+|3k#{E7zIO&E8*OQe<3_liG^uzs@wR}nJyExnQ`>dALrqu0XqPsa9 zMW!!Y!~gqDE2SK`KTk2J*sPO1)Zjp9?DHFCq-rVdip> z=T($~fS=vVPzcbe(F1_!$ecE$~63{LQt=&?0Oy&%|ym0A|b>1|!tJ`4^ z!yTVnk9Au&g|@IgE^BhW2<;{x^$dIxX@-Ev>H?w51<7|DEl@46Mrk+MqF`OXgX09A zU=&DVmq2VR4}lV4%?ke8n@=&erqp*r7!zs>&Iun@5h|8of|Xa*+xzxC`NzBE7RlbUr=uhia%r%(9o%l%VWMKj z>UwOqbLnL*NjJ^)#^%>Fk%m~`bQsp(fJZ&jvI>3la|`?6?de@xQIT&Pv*tryyIN*f ztMw^)v#`{{z(P3?PP$mm-=TsA#=YJsVM>xoy51-p>@D#toSVM&|GFRD;I{@h6DIXm z6V?R_=vP}gNL4&+Xejed(igEjV;#%~C?v_h^T8$rcyg=K2;&Rw0IE}K{_O%yH^eqI zojR9a6lcEUU|)V=Kw?;NlVWSXxuWALbfQf#j8)cIgn*PIVNEmL94Emdu3n*)WIRS|!(n)AIZ+)Um zM^V*RkN47JWMt!NB1juDy584sqD;T?zbjbEGhcY>$ABq|gUyk{@Xu`jzZL-UH|Jnm zH-g@4SsOzA+}oN?Bj~}v7pdN?k+by0%e@lcdK!ZEa_lpnk8hC&?u$5W40E@yqB0q$ zH(9LVr{jG3<0`Cba9i=@F>}JvOoaILB!1H@+^72cq6-skZL%LQn-X!)@cigTU{10# z2CDUv9r~c1Pis_Pw%{azakSIPYNYM}MLKGP5n(RKqK$z-c5Z0O1LKj;tVyFX+^TW! zF(z7u@~=x$Vmbc8y>G5ecLFTTB`$n;mo@^uH`UONOs}feX9Xr^)HSM69|55BBwufr z*{e;STPcB{W1OY?sCw)7a{Rh1btg0hsc$h)8`Wl_ssr>0D zDl3K4tH=1(mVS<)Wnp^u1p7TV9`#SZHq4w;Dug3vj|-oM!61}tvdai>c2cmO+}6c# zo-gG0cE4k_dgb$d6!UM1_F#a(vjhJ8u2{3C5~dIRr{P*ItA3p_e)EsS zOpzGKUKMvyzK#| zrR~YFN|aK1FHDclhYU>$g=T(LCezDSaB71dvp;(7^XjV^%6!Ww+opk3-icpiYqq>V|MfnA*iN*D!BZiMM|ui>xx|YlF?xXN%)K>&>ZByz&9*=*?12A zH}>ozug$yrI(f&kMu`4Xh*)-Q=%`UM@p|~N(Zaqq~@vwtph%|5s8OyPTU0falt7e+GW z)SQPh`(p|n$@tjTqy{ZnC%Bbvs8d~t_1EOt1{vo6yb0vW^@p#{3ohp70U$2Y4b?&Q z^~&&UVC0fSfNtw&QiC704)C=y55x(CiqU*MUi}rMS#7?&-qq+j4Y9kKlocfdlbA}y zSW#?!1&Eu#cZ0;b*N^xs@#&Ak0zNGRU&0Hl+}R)Bp}rT_(zzg8@kGVIERrOFrhM?7 z&7wI~`Ta1jIVKErlX_Gd`aD=f{)?Q!zQ+3Q2~y6Q(m0RusP=derM9^Th@kVctD z?nCS^x|a`2_JsD`*A*?+ZLjl`+&$_}2V>q~Ag9gPTg8fy4YRb$H}BC%6~AlzgY2nU z`hE#UWs&;GTf1EpcbSNU_&T!PnT0^xz7q^^ZZ4mTqK6Fuh!6=diVNGyes{`AM6{`C zFmUw>Qg@915g^1P%57JE+5z3|eN%tU&P=qQDjcC!q`+#IIiRgqj3-K-l2Avj06p-VHrrATBbzM=7Io2{#FJ>8xc?hE^6$o zNOoYGgg}ILM~dsxA9vJy@q5IyE&i_vg3Tm1(E=q2Obs_rZSNFYy%yAC`zt81eWLow zJBlP~zb(AS(XqR-{JK*7PZqCLk0r&E5OUw&e-uVuCMAuDwX08ks55_`J0~w~kWx9F zV#0FuUjDZ0xSmN^4!2mvCFzIJOT15L+llBYc^ElG5VK^7Da&@#HID-#W=Y(_H{e;l&Ac0#ME)g(d~NgU^_>&?_RE0pOl{l;M>`( zh57MKmm_58O{gA+U3_Rz(Mz$PUa!W7E$UqGT+~OtlqR0_|j;#A0=(< z%^)-TtpGG5)2Px@#hnCN_E~^N%#^p1hWxNK1WM;@ksbAqxA&=n7{o3IlJl*fc8TJz zD?0Ch-}ck*yGXJwVUQ&K0rUEXb(;741SV*NEP+zobtAw7Kyrgu zoZ!78lnn&&@bP(h_B0DK|DvBW!N^zmh-h`Rm1xefetFQD!@)f7c@4 z#@Va7qKF~G(ay=32m9H-;p2BLUlBV!72x>b$=dl=@|?Hl*Z>Eq_mf@FU`pytpxQm- zAT7v`weqlJfS-M(!UtcDQ{juz4<=r@t^V0X8J0;;^a*vkjF`QPic(Ib`(aQm5rc ze}#D+RZK_mFd@zE`gRZA%{Ps{2UO)<4*2l1J$G)DZSFnh zY%8kyZQ8To2NZm{V#F)Z%u{z{IGQ|%*Us}aTay6=r;5A>ux=qILod_vU3Ni(=gIn{ z71!waJ7>!48g8^sw*rrS5K9xksdBruc9x>QWk09hv>CSgW&n(N(^xDb?y9uP508mH zY2d%!1rKH%w$129P}5TD2-?4oH7vOok+#2jEN;_#0YX}g=CAnI$TSkTFap4 ziX`~&L>OLq`MwS;r)Yo}t^Iiw;p23CA}&5Sb6EB%Zobm8R+x3hgP(Y7dZ!a^8_$3H z_*)SX`R%rhe|v!=U6K}zB}ze}lwz5;O~#gdo>bJvxC61Lu{)DKCQ)CB2deW6HQ|rX z{|`@Z9oFRI{r}$^14am=QyASX2vQ>i1f?6K6_f_)8Y$8sp|m0)4blzLAfeJ-f^>t_ ze)sGBxxT;a+8_IO*Y2I2^E{8|^8mY|SsQ(NlVVAZrq~?SlEe;l8l#R65NQ$f&Z2s- zlUF-mRAToOC!alzfBq|CihBp=Z{vU@kJ)&=>wDrO*cO%Pj=mZ(E<`>bw)-8j=)IwK zSucZ`O5DOzwHzJ_u}!WzjLF|4s?^m0da3qhqSJPw{2}PB54JN#7TE+Nyp#LaykT}U zH2LuHN!4|ZFxyo;B)+C4@Whj56a6~z`9B3tw_gM;$|y22qRN?y<|xL#N4)&HPX#_$ zKfha(k`%HY?kDSL-t2o@{!ZZcr-@eXLr;SclEBwx1^76eT*wp(LYj%ZH7riaO{Lrl z?nJ}QXG3_)eR)K%eh--+hVLLusKk9F;)#^G;ZRfwyV8B!XfPO5T-kDB?E^X)U*@KG zm&gTYjh-@74ZD9(dVf=75IpKC>}GbTftkUeWx;MSbm_7cLsF>nfxU}d2OCPt9z{xo zjx=w^+=F}vVE8Jq8Q)l_Jh?`WqFy7xZ*q;1S{mzvQl*+Y;-`MG*^S;LkRM+Teymz9 z4;!4`$E)S%54?|6k@3ZXUCarCjnX6z`8>sQT!1&(y?bTA!;!mIwfqJ>LGvQ8)c{o= zqbng?O8&l>1Y#_JHWs%nPdQ~F0=PYBv}Vr$)&~NE(uj<&IW!VbQ^S_eK-iPx7(A3= z{q0)#A;%KwYCG9utu5CsMG~?FOL;(i_3YOs-4SCrnw-TdM&-%;opq$uZN=Q(K$&#* z<>=rKmSu24p;EP7e{c{`*B3h-d>J|1JCvw(jtXAT3wy&qE23>8{ex$(Cv$4a=BKgR z+bPS5&nr7y96Ww|DjbV+R<-4320cyuJMutrgIz3p2`<`s;jP_=JnS`v9`5nb46Lz- zrMMjBTvOaf=a1-$9ELO#R{F1kPMLRWSGI!9^NKDXD33<+vo#E;C`D(eJ?%el!G}RM;!*k&?YIz`t6;{SQU4=nizop&+{NtBb6|pOImUJ<#{FZ}Ax(^*|M~te zZ?Av83O8#zhRT^ogu^vsrm)`lNL^+j5HozlR0}Ac3F=|+1@UR5KcYv7Vg`tj=zRc z@%^Nnpk5%H7mANE6^ZBk{P`H6`1RT3pNpcWd$2sz3+a~&QV%q8&YAYl{fIr@1eK9Z z2`J=b1`CeuZFs&f*H}V|HM|E}(T%RQuVvqwUyfr)y?#Z1h8=}m zH&I<_zZ@Q-vCXbR$>YgL2jEw9bNt8>6|s&mymNoaOUTPR4{TN=-P!K*p^M$XXS4P@ znUHF!XcS<;r_^Vl2*xiNC{tW$iKFNqRsi335j{T-KRbN1S1>Hw+53upf_o9bdX_m^ zA1><6wvi-n#j~Qklx6SEr|d;Iz3zpqD-z_U+mU{-`dwOxY`oWs5Txs*wibhd^j^dE zcMvP6%G<^XH2{f+3j}jm`cdWDol#l<^xGLEcu#+B${Z)AkS)vbijQJDZ+>(5^iJgE zum$fJA}frH{m}}4q}mytS8lQT?bYqU)I&cq^3VlHGD71}}{4apvlix~xof@o+M2l@DDy z{x(c1F1xDg&QX?<}vPPrlgqSM7hkdAy|2=;n1$*bdUwedTe6w}EaNBrP zpwrQ6wbyA?@_q)pVA};B0Z3P*;}$sHEgDxatrK-S{k_-@WDTsM#Mx|lfCzc`*7Mh_ z!`$h^Q=x^SffuiDX)YA$Mr2~trVPp6KlVIVG)U1ug_sv#u2tAMd+3C~{#?T*KVdw99(>6EbCs}fvDDiQ25`yvou_972Mk*zN9P#t~Ew#Fbb zl5b(=#)89Zano`7q{Y{_IRuH~)gtgbHP1IJ|&ShU>xTE27> zIEHN!M|iQ1*SPZf#|3HWfVKN#Irq**I)%`7k^SOhH3sT&R~(f`PHV5%P|QeI02-=V z5g4?=V~ap5l5ml#yLN@)RXJh7nOpoXo^T$SBbn}^?l6tk{vBp)}sR$3H*35HK3(IF- zW?V~i^70$nGgGDg(u3<2jI}uhC$rkn+~~h*7XeuCY_+!M*?`zB2^@J&LktD5351XF zp)IF)H>>ftq-G7*=rAngS__<`qcKxRVDz4hOqMS7coU$laP{5xiAYDlbqrG132G=%Ovi2Pm@vZj^X@XC^%ChojorY=Kn zLutTJLLbqy%_xjwZ8evVe$8lQ@h^Sd{c|Kaw7{iH5$_%Mk-7MalP_LH7%`YHCd8c2 zx*nvHB!~v<6{qNGyQ35)^|FEy|Ef`K`b2;l)Q-pJiBY@KN8EFY=RA2MHB*Sd+GN~5Rme8g!TjLDgVyz^1oaR~?)gXg)N8J_(P~-!AA$lE#_a9tz1yguUf` zc23Y$VejYD6W-T{D=fu08Lt#sI5>tPnYj`3nU>0cG7$Z|diSc(DL!TVu-B4cWX|2V zv<_?63o8)(6RgahBO`Xh#f83ECfaRg(hpKZf9q}+854&JFN7#QaYkc6R$SyV}JE!rF%wT0bX#d;cET-8|{_7CW0j0R}8wDvyHZF`ax6Yy#*%4hxyh0UtbFi{e$oN>P zQ^IyWjYectJPqu{2Kv2mlzMkgh5T3drB_FYUH^{qcE3`4$xCCXxM?RWDSTI#nUxeYe_ zCDrtyRiEQq@c9Q+KGLex$Jsu4E@%??*fi#wvFlf# zs;`M+Bxrn?Vyuu>y)W3}Z6EUkkcorp5Ki6tKmu$={}jO_qq8HerRFL8YSVUw#e{#P5ipsF-7@ z{BU6>T{a`ijH1KIor6NJ=P=+10Yu`W|JXtVu5GW}s3;R~EZGGQ4lNwD#J(1#j895H zQfj`4Da$Dn&DMGXw8}*P{$_OU_l%3a1Z=4A%V;GI}f z#_@l}Df)%i>n81-F`K-v>)PV=fmfefF6sh(SzJ9|y;(lTZ+ceYo_Nu4KI!Wx+j+C+ zQyeb^xe2(3J|Rk|v>K}XwD+>PY}Z4xG;Zxqar(=ttZ2Ab8P9Z3zrH1--XawLe0Dr4 zNaJWJ<9)SSbK%Y0PKE2*5?M}r(6Zm`5Xn|06P+-PKwCoM@009j+wAeFZ_&VAORoiV zDip}39t#2TeW5B3jpP8O&iB8r4+&)N%-2E>{=vk5-;V7}qAx%PjWDGY)>_Qemi6WD zUl&*t&>>!sxq6wo8RCcYXB(^*=r^6Fx;;rj;XE9G1U4@zXOXb;@GHZAuol}SY*0!* zL(P#jud}^6^(^&kRk&c9i9fy{t6|1O@cDFe%w+V;Tg@K#lm$6qv|a&$_y>BLp!|mY z@OD=T+1r&!i6DwK2Tb)~&;=lzCIk1Yo zJm@6q#Hk*H$KgP!+4LP7Am0EEdn59Egf&Hf@~o^HI~hp3lR1`F?NCil->M$N-+&o2 z2)AFud)49S!OStK^j%*B>snyQZVf#N3I=<*?o86w$SW>qiqT+Qf6tMQke7#(X-9eo zTm{En3gp+2Vz7?@6%~2>2By0?V~lb+Mq6+8HsJqMvpmA{Xr`>M%{JB&!ejeJo56v% z?gPixoA}@Bp%(O`6*>B@b@ol;pZg1rvZ0i_br<#(^#rCn9xk0@zvIk*#i=))mGf2n zs+ckO&NuE4?;WUlPPEWyz@$ltuW};{Ev?a0w#VdIXx?s`@>^)O`gY@ z8iXZnv+a$5gv$a`Zq2LRfC`eu3vQQ-2Nhs>8>(K8Kf1oey=afC7`~X0CwNMZz+!ql z<0p+RuNuZd0RWR4Hv993Q1o;QDQ{ex-13x2aW_%+$0+a5$t$|ei#mofYjd~%j|ISk z+`m*PDsWYR71^Lmgc<7=0nu|t$U@`!d>XUK-Hd-!i2ROo>s!J%On~L-NVpcprqnNi zHlD@$-Ya#If~Wtu=Tyth;z{{G56bHNrA(^oEezuvO)C@x{T+0z6DNNP{xB+?UHn921-Wu`fvPRaD`xnR|CV&H)K>2M=`lMacNXgy)LJV;|2%XJ#TNnvEs`rY`ZU+4C&8tYp4rFe7`oAKT@t{$ELEJcU?;clPi?Thdv(R+2rv~Ok-?Jk3+J4v7-lDD}q$20G%qM2w zZ#lKSB#mvMdXrOH46oh}1{K|F39g`OY-VO^D@u$)%GSmREZ&boqaFrmW^~6#Y&akV zM}r7v)ID6zU@b#lCv4Qfc3{cgbgYI_1J|AnMGV2gC&qH!zoo=4Q;Kiu4Sq3qvOn`$ z0O+ajQOsm6nd;1MZqbi@7;d%6W!cp+L$?b3^3{G&|H>q}M4A5f+>vblg4>j0S`dI( z$mAQToU4xZvV4kw0Yw>Q?-8XBciN%ws|<9dFP4i~-_WeNT`w)CvRf?YO_wO*T}e-l zYZOEyQZPL7ft~;wId|ilOZy?ziB6%4cIuJ%WYEgdv}5(8eyT>7Q|M-Wx4jwCv7-Hm zz!FmNIe)T7SM}Q;=PtJ-n!OlhRAD3)1jm6UBTHiNJS$FaJNtEx&9uxK>Ghn*0TLH0 zj(VrvmuySa*L>#qzK}6rv@d&Nm`tQs2;yU1t44ZBtXZuwCdK6ej43=O!4XsBTWv#h zd6i-N6wP^(+8&h<4=-TMoygNIen=PU?DO~~4)wh2=78J5l+9eujG=KvNgt8PcYi6l8 zI8&oFrkybb%%1zu0V56na4o=K>pf#1+}V2{drYx*Jt-f}+>cU|7OmbTUX){EDh*4x zVvsyQ*Z!cX62^EvG>)G+(}wfH62@ad%fyr;JByk*^SxI;izlmO-u=A4ExL52fG6=T z*#+tng-1qeCB+0LJ&~G;2Ou;C--Cb!s1LZ3o@0tkfqTMRVB*3~d+l9(@*PRgk7xhD z#kLc(uqJnTtvXiPS*EnYkV>i9)=ATsslTqh`kjn_V`uz*FM@(vr3=rdu>|}B;wNAG zv7$jjPOu?3hVs`)fIju>n8z=4ULAxSq|7DqHz`LRwzv8y&hJ_!mr% zQ^|lQ^VNENBU)zklEdEl_fepa_-Wc0&%-pEQ_9*Y@zcK9wAL2fzC39&zLUn%b@ZHk zKyca+=l-nb`6f$_vMrzJjQ7T_UD3iWQ6)!ppV1kH+7CKy%Y!djo1qoXi(u-d2qlU> z4UDZNw$!_%W*xMkB#^H1rmVTf)f5If+CyA?c*?v$7+RbyQ~Q>nBbjtRNw|^>N?=&o zd#1Bc-ckEe=g!3VRJHCPeorDyhAS0S>~FMGYBv?q^ywD2}KyBw|D)1WC1}ml;1ns7_;s5 z;MLjDauj8+3Ab2tSl0We0a#J)$bLv%BJ#fG1=S*0n790Ayh7H&fQ~Q(nT*@TN2MEO z^7NgcD7->%`6x2=51x^j^_*#Y--sYO&uZKMj0a9`WPr)gQTCPBSOhy1D{Me7m*C(E6Ik-5{sL6H?^123*z+)f2!f zvmwezGyu58@ub_z0a|S^*nc3H^5y(a5FR%6W_XoFZ@`FmugJ)Mt|briQ`-ifgC43J zf5Ilp(Q49OQfhZf4RoWR8)Z^w7lk34Qm?u=V9Nuw<(=Of+L*v+Eiu(9ES>O78{lQae{!n|Y7m8ow3nMJ$4vz66Eqm`l{q*;6$xeIE z;@m!vf`yKhn_SBk`?##Bdu`MG@b1zK1z~=w*g!z0{(x!xzBU}5c-U5$qsU1Cluu%) z-y`nw@e)b3W9}d2jNtx$8CrI>V-M& zh?^%UTc0xwj_VW0C)yu3{4VM^BiKtc=_RoMPTR_eQ_wOAQ=9ElP@{%D=wTuA0eBQh z0m>f=-~;gPO*ude2|!oO|NS=aA#Oc7Sym-%042q1W@JZzQ87nH3|C8-K-*zJq~Hpm zh7Eby+bEb#p;X?DCa_&M)r<-Oj*UM2pbQqJM{!LCZ)=xkR8?`ZB|cnBsio9esT(uM zUu8N8{ZRqQ*Avo$wx2f{+;YaSbMxGJ-VO%7nBD(wb^h)5}vZ)7Bk?!97p*C$s0nhwzZaLtDfPR zeZAkWivN|?9g;?Ctn+lS7BgT{$S3YSn|i3x3K^yh1`rQ^FvGQREt2mwB}~@F&1Vfj#2n0gV@{$m^J~D$|rZ)@6u(U-14mfRyVussg??6^3;g zwlQTtv#KQ)?*VNiFohd0DzgsUqox>9CY4!gp&nfYpj!ut{qD99vC`U8!>&Acw_t?< z`IZb*wwixGc}7?)X6fWA#_NaaD>LrwTy5_a8271dnEi~E(&!Lv`W@*X=)AoIIrG{H zz(cDT5`24fWf%M+F8s$6fx8BSn~U=1h*wLSey0hHvx!R8_5Fb=ZOT(u-qC%@RYR-Z z*u^d<=a{&69-X=~Sdh9oSUD2Sfd%@OR*RcV@t^=aj1hC!9wHF9x}*dFZ}ovXq7;uU z?+}gqN1>At(|w^8uE3jt3zmr+P*NR^5f&L_Ms;M;h*VVFLZK>rldcJK<|iabxCHVW zr&D4Aiym`51MHB04ebr}c3Bo$0rsxOh{?`V1kTzQm@I6e-x|#3?gON>m5qzhjKBQz zJl~7js<$hO1}+~A%Jey)S!suP~DxZe6y?`B-xFB%f~eP zo2xM>>Feh3V&LF(j~-gBb+a)G2p7B(27MbOT4-&A!-`%b$L;a=7-Uelc;vUgHSxeY8kS{8B67%&{&1C;OvZ3Tx8pqy1#hCqJUS z?~7~?07_A-SBy{q|8o1`NF*{-e3hJxl>I3vKg%cF=a)G=JrV9I*$EHtZOpxg6FZ7B8%qF7Vvj4`)+aar0um{&vp#$I))vRW;Y!F&N#(mtLf;&-zsz zQ){4EzAfa(+^(3wJG>P6L0S3o1Pxr>`XVeq>^MH)hU!QlXnW}~UYPsKVMD>q5L^FV z9}>VqNs0dgIlsbTcKIM1onVxV$&b_v8j$DpHm5$(7Ig0%h7zZJzQL4m0CdN)I73z- z(dH3OgMQ9-@6q zj3O`Jc6&ysnKYW)jISwpH&;T-F9lQ+pbB*jpa6#hw(kJT)4!roeB)6i(H1-=6 z{y-`{!cZCtgBPubUX?0f0zIsusa zEb)P1kBkA@&x+eGK6`wrJ$Uc^K8*fwlF>#HAbb3Ub?!fV2iLsiXEcf|dp-azkeb%b z5^m*j8*g=#!+Q50u1?;XCBT0#;(v^A<3Qd9ElWg7qE*0?Lf;t6U!efYpP&p+-8B0* zKgN%w)q~$|h2}}M6oz8Tsy88mF3gO6fE~OeGWd{I<4yasu6%XZzYxEne$ZS2A36EV zllO?8Nf+xPO4(#xfP0_lFHPpGhRb`|d;j)f5LIjlJ_4SD*&1Eu2wJXy26u;;%KY; z`cH~KVuAfG3Vie(#L`!fw1sR3Pb9k@{CSgWl3Kp#Q>D`O0!fM8L zhd@94TCSnVflx>kmrNK>r@r!9wU=|*q6SeMa*W6E%&fcGG)20k+gdUvY87saV@XFIyXq43!Ms8XO~|zzKuPT1;9wZqK#VH zy+O9Q#v2JBdhTyWA>MfdS1N9w{Pz75{3%=Y-=%l6(*&CJVjKFb^>MXjg{sACVh|Nm z391HU4$aad0Vr!Y(gE|Pi=0C-9d!hfx&Gc)rW!X?ECtD+M>{MGrPYPi^xqn)We1ae zhAFuD&ul1>tQO5B>#*x1=QnvL&4Avds6fVV3nno&_$Mr$%-{d(@aU_or=@RA?aDa5 zb;VP<>S?{PLj5qN^xs(rt8(ENnki~-Piz;y!FE$>Z4>=e-rE1ExL0`u#&I*Pm8lY8wSr)rV>CP*+#g; zBVl5N(39)h9aL0*H*m>y|2OI}IGhp>+#(j9Wfk8{W$bvqJY-<&IU}A^m%M#KTl|0w zv-lJNMQARZ2biT0Rz9y!p7`nfWd}p?U`tczEyKMjR!g?%n8z{!bapwfj3hkU>K=dL zaSrArM*3y5sQn{`lHV!=FAGG_aS<;`sy5+NT8X@ z+>9l-Hq+Jc5Q%a^f_$}|I64gSV1ZZ9IlKe@pK&u=TX_A;;&_A>7Hn6FNtu(j8PG)`$*KR7qzv)Z5tza11 z&x}Cn_X|m^3_d8I-M$+RJ-%Yz=5h~HuP4x8=J*C}IFX~A{hi8fN6 zjz#d!TrQ^t6FF(1;Y%5ah`%FcQ_*7bcMmY@siCt>{nP&D2*FsSCyO1 z`ddlnyAy%TEbIT+nECb3F>k25gnmgA|1_+MI;QB2Q694UFh_TlJf@l7;O@I;*GBIn zxeq4Y3X$4pvmj6(UTid9q@Qf;C)3E&@_Kpwz@Ae$%F5IzKLW?2=+FC?qSdx#ek!(7 z$M0%OTGgyl0t5P+710J$PAPQ69bTpkpfh4g8Hv(>)+RxVrR@lzn%Iy*9N6VFE`~nv ziU31X;62p^8Ib^c#NGh21Rf3)&KiM+Z0JQ28ldTb_L`z!=p%sRk30O}=(_Uq>p>SJ zT|w=>w@UeoZ(FvJ2EZo=vt}Ywj}?F}d^7kUK~HId3|*_;!#U%JPmI$S>qfO_n?j94 zyf(BIWnAD~GJeLMBX&d6@qHg=(FhSc6Ok7sAck&9u&YPE z=ucL(l8^np1hxxvlzfT9&iuZP3hbWJEeXti^OD4JAT@F&U_IV^nl(q6!=SPqNGCYv ztAVDSvH5$cRN7CgtN^SBybR-9OKOjRB=`4|AXjnjp6GGQDiUgDNQy7pn>Q;n%j1)& z3o(u1kv)ED2@S_1gICD>lY6k*t}6D?NUWt; z*w5bAS8Z7xSh!x)8JEDagwA`z^5y04LqGkG(le!s0$=L?`5-M=_so7%jHm3)@&0n; z={O+uDg5tZ0-yXglZ72+$c)64~s%Dc=vS~e z^Gl%g&4}Nh#;@4T$xS>RDoTTG7jVru^jJINbLNwbXG>1aDy0+hhXIURk=RpVJ5^8| z0S^bEQTf8YBF^mJxmmxc__xNlNxNWp_xe+oKDnO}uCAIRP00z-cuQANE`nb`TX_eU zBIsp%?+vV%I zE_0`;_=kydOHH+XP-~6R=uiC5xbkm2enEHe{Km=maxcDZw`=>0N0|E;WL#ss-ru|m z0Vc_Eoy@@dQm>JyU}30k4_$}-0R|d)^%jGZoxK<;lIG!scI%Z8Xc!e45e4tL^=@k9 zbmA5^Pf`$w{ORfk!v;YjX#*d}1q<@}%Gm9w84CfDbKnWrQd>Zf)9s=1r2#)VWq(IF zwu9;KisVlZP481V44?0>nhc%3@LNai;9UefXnan)qVs?$yM}9S&HGG?h zfSiZZh!Lr0k&959#%(kZk4PbVVd;Nj@~voxJkh1=od*0bkr2D46w@b;I|3%v%plcB zE>NY`iu|v)i@em>umi`v|MnU_HAS@-J}ps<@QZ;~ z-`jVQJdZ+yuYySv1ggxr z_MNIru8YzcZ25f92$y~*%=$Z&cJ3~i%~NByWTA)F+%jC?YH5*rog<%nVW;`w%-+C= zbliDTJ&B6($l_QhhMqGN!S^mK(r|^Lo=Dq)o<|zjf}~rMY)j*@}Uy1+lwbFuWaFHnb;H18rfvJPn`ZJ6?}ZRiOl`=KeK$ z+3s{NuD#lx12q{t@t!_Q=0$n1N;*N zgwY$Sm{;MzyD6ECHKj^pw|B3EZ^xO!KK9%U()0{DEbVpn;G*&fI_6jUH~rYLSK9ge%OlYOFiwxA~0j-Ab3uUh4} znO9mRR7aZ*9vwk!#P4Zq zlArBGr2&IW7dRMN#I2~4qR!FoqZV}bjx0(l4o=AY&;!@UmK1>Dm8yP?8-O81j!htq zKrmc~HPf!ETWb|VLL$f%A=yH^m~dU$9mMqf+0AAdX7LO|dHugHd5(Q*7h37HU>(!B z_}O~HuC>ngGjr!tOSREbN1P{k*R;@c2z3qtyjPAG@os0b>U{wNiRDWvRw(; zjpoO>KI^M`Rr>N!D(W&^5beLdy%HAbOb;yO|Hr_)fZdr1p~-(hsq?Zx(BFgQ0Iv$G z5p0m_1MBp)5ejV!?zKm-b6T!h7aROu?RD`^Vv-ttpzF+L5H(91ehQeO1Ox^{-SQ=M zqCUUO;y1#AOzI2YEM;AvCD3(nHDqb~{_r~}`!L;h-no%K_|&y8ATSGdQJ?mwXkW6= zVLHS(QM$vSp0Gx>eG?mr^~OPQZWj8nc7%`oJ6DfQou3ILlMh~r3p3&EdWjE4Ewzu?~mn@bld1KGN zhG%h53OMm>qV>EcQE|SWjI3pxP2K6i zUVZUJQWY`Q3OJNvkV>4SJ4@L{tu%W)co6g3ZD}YPk4#=g)qU>-SM|=_QYH8V~-kkL0*DECp2mw zT}MTP_l2l>`+oKv1!ydO^OQV;eIQ6}w0H?uMp^QjcFdDuMHIXJ+pCWx=&;%&E>@u% z%JRlNJqguA7+mbjEE(!goj&iqN<6y_uC^~_wTdT@A~S%n z<^yI~Ml?fn01#c$_u3scbuu#`g1N0gw5fE|z1qXDdB6CJ9lZQY?ZZ<__D6N;`KMT?F3IFK ziQ$ips5pB|{O{Tkj#8OW7&Yz4YYkV94IG&G?ulql`sNm!5mp&@5gBKjj}R93a4TWKP-rsvPa17b0F{z% zWt6?J4kE5-ImN+erC&Cp+`;%5CGfxn*BIPi`m+WS=6D|q18aC4h#`!rU|s}-9FG9x z20r#Y^i@a8+xeAH2|`UsQTyVMQg3IV7F(_PEUMj>d1+#{B2#5{$FPs5#BS%pfzF5mzmv5j6L9Givu{rgK{YznTojg4D)WV-^VnO+2S1&66B%JqUj)iN)$Qpt z*-te@_wZ5EWSnUQKwj9no;N6bbydUTIpLEZ%(sv@xL}gY_fFb6@Kmx2-nBe3$e3lOslM^@_EBjCbQ(MW8 z1sBmh_6(Gj@q*8zVS65Ewf+e$mKh@ddpj6~?Ivw{HLcXgd+bZK$A$=i2+W#K^q|t? zNm^1>l>_-ABxnKNOXY!ga5iL^b&dX4trY|UNkyp0ZRR&)L)OJbsRH>YDHEmR$Y|{Qb+D&uDnGs z5}|78^$J86?~0H1qMnTSF#?|}5sDX@8~MMN=-8g8WZy?Ir#E@72ly4dN@;cd`FlM2 zk-N{uaiTX8Y4@?*X(oOX7mdb-I=JC`M9Zwbc2;f~_v~4?>P&8ADx~`|#k$}m6+D|$ z7KEGpK{$Oa7#n?>{pvExBiw zJ(6?N83RXxs2eFM`#TIV9urPC(Bgf8!!Fhx1hQrFPygGsr4zlwoO1bk-Sr_b3-kZm zs7a}IVF#=xg_M(3na@eFRauZoqR+9AA#I+JfxE;^XUBRZmNGK!PZa^d5U!d~&SLEskrMj#qcJgekI}{C zPWFY@_<~gPEx_I51TQE|6Ppo*3*BONBZeUafHRm1-ye{m1}w;*9zDL<1&u^9SBsnOpWV?uYCHNUR6M>BM%RTL|5$#s1BP`Srp+3T?fLp%qwuk9 z9{UvRtx~>{v-T!0yH}IS)eHv9m43G0(-m3n$Oa-aJ_rVhESpl3kQA!kczq6gkR`~( z6pqIv(^;~zHgOC_S|)K`>trU#lkdo$zr%bXlCC5;MWZhebOZl~_Z-`CNxEkzl1S{0 zHUFz3;4e9;@!j*UZx^7J^{hDg%l~e4vr255rQ)#8^RtjdOZ~0qrxm;X8Yb39b(1@U zkK{#7d%rLFRj2L4EgO5umN`dRlDBAQnim6?nk(`=eSnWjryP3<`bc?=rDROdr*%mz zXrwzA3-Wh^4(NpfS*I`FT^d10&TD>480_KvT(s+mNVc8^+Fin0sW%2YKl7;VkX8n> zt~XF(d}Jby;{WjN5RyqyDQKX9{wWS|+Z%q2MVhWdS`o3);jzMh1L>c?XKp_i+pFkw zP+>go*m9MSzGnBCI?_YbU_(AY;C14HEl1w_&|~3Dy#?-P%GL|mnjUFXE2sy$Xsp@u zSj)oF=oKLJ*x0W?4?|WW6s&LtFABPvduZ*)pfphMljjD4*pYe7AS&?l#!-|nQn=hx z($00Ch+Zq9s6iu=w6y9pi}X1G9WC55J3j(T6HW>hY#LcYfCc<=-FKvbE5H3Amf2ha zJfxA4em?pyM~}={hvVYOPOepsV(MvQtfQTYH0f9`BElQ~DoeK~E}Vo@S1_`}^f0z! zo`sZ28x|fGMpp1%5c5_rSbX)iaP%|F|AjSmC9`*LUonQp4el5^f*@Ctv+M==HQmwD z|J0qX1UMS|Hsr@xJCJW;oi>#F!a!%2m?sB ziqZ^?fKt*8f*{?}LntYsq*5Z?AgRPCDIncBh;(-|?|gsfoa=i3gSloud#|Jq#fEyqP63ma;7LEbTahzM_)?B za&f;`5Y4AHhZ%VH9sFQ3QxV-SQnj zNgx9s_^_wHdg5#v@^->=i(ZTBmEl>EpSsNKmDtZ?DLHTm7#_*9ifqJ2k0Dt8Vqfj; zWIK1iKSC3b9ya^5ihN>t|7fD&(L}(T`wlOE*L*W`xM@*+{ZW-YS(Qh(Vm|NjbosXO z^n&B<%1fK~PSc;Rm?+n8Kz3EbP?fB+h2egcZR0(uwFCq#^gb%+a$`4!Kww}wPJrGm zB9XD#r*W*~J)Xsk^|rRuO#X3i-F2dNl*VFg$=Klt6p#h z4)(k|pjEM=y`Q-0Z&-%iypwlByF4W02_OsbMB$H-#Xs7?36YnqX37^1uqWA0x@Jki zPmfZAyF;{(u_W-JvPZ|6uNtg?A-`9ItuxjnzwqK7QoMPid3ry!d~J>ri_AKnc^b@db7RJWaGJw z;%&+N_ltZ2TtIDJUr(K_gxAtMrX8H!J}Pd%U;UnAp!E6|syH$c9onJO;Vm7hBVFtc zg(d{jLWgd@7#+wEfiGcv>PZL~plSDoF#Tz0ZVseM2q**}mXPfmO7F$+t*=dN@5E4-=&<)B9a!=Im+QI_ z3xN85tXZEn=+8!P5A_?}M?{pBaw!)1S#M0u<@L$e3399 z@rcUG8cU-`BWywEX8wfeAzM5mHq=#vpMP1`r8i_Xz1DKpj9!O1V(~N4aNQ^+u%l52jHs! z?h6oKyxHitE08;xd;0o0A+)FI*_XPWC6dw=-S>00PhX28WU`~>HUQUz`d+&aKjl-q z(`wKj^o09Ns3YzZ=7&M2i&mR18uUGuo@@e-tn!rYW7#q3I2XV}Jj!#IoU-dHYJ{sN zTbE@S7`ss$JRh$P3JPXyyP%Sb;!6Ykz`cT!IgR6%?Qm)KmyzP(@k+Z-rY|cz2}m-(Q1E z{)0g+CKU3*AUW+@-1In%3W4J^5UYp3pa~?PFSTS*6MbsxsOX8$uZ>Q=;elQ#t0k8@ zxi9FVnvD%rGiz+p9_lTuHcp7YB928IP0AJfaOg(YIfUCPF+Vga&Aq=h7?iE; zJH+I00IhD}%os{R_Zp-v)%~(N!$|d;t}1e#Acxv&Y{Q|KAZ1JFY!*Vep02z20wdm_ zC2|r7dYYSi4f)9_5h0+1g0)GmK`4nw#CUqK*W_fQlI`u*p4|J3D9|G^`lY2C*x?|C z_ZQ=R1@=x;19>+UiQmATt{+@cLgRwpjy zdGDE(_lJ%HE%~q@N-Y4D&Je=zkky3R#w-osas#mf-q9Dogj;wQXk0B0ek|aFxDNeV zwfDZ*5TH{;R*bDqXr+|;_98o3%R?I`tvKac?nxZ&E}Y{$-OGK8BPd)lKW*a&lKp{J zU9B`29Eh9*Bemy5cm$lm;_!P(4_rb=rtLY%404BgZCVTlBpmT9=ej3ySE~)!D->Dh3<;j>bf&@fbH+ zlcYc1j!w37-x4sSFeU_GS?(*5AA|M9%oaq!Bj*o_Z+fD%O>^f88=sOfU!REl%v%WS zvFyP6di3Vs?guqORH}fB92lFZ7J?0kI}K(Km`4ZCTnYy8*{gle_;hLRV`Hl4B7r;U z=VE-}b!~x!12t}QDOsaLP`j1Ltl#T*r4{O9S<82f-+i74`mwu7`YnPVPqd3j@I-*` z#`X?+B9LqS%)P?1XfCZsjoYdC)mZ6hIlfw;aXOyStZ`)g?)hsG+c=rr?S|e4cn%v( zgZ$|Txo!OA;#J@a`k05THN>clhEAq8C1hfS0gQ(+FTp)$%Lp`X0n1*{N%j)K;x(Ns zMVe?Qw2}31&?likXN~T*VAK+BooT?X6NxMLf8qb3^?)-F!Z6C{;SC6cfF5!JF&ZEJ zrjvAqw8fw61SD2sU{okgW3xKEMu*G=xpWDu2&$$3Z6G4gPd z?(K{gh=&>b3|}`Bdiw*j>;BWGISlgiieh<1F@UyGAOyEAQ;3B7d{GqbA3yw_-VSK` z0=@l)t<7EhdYj$)zhq6B)rQ60HTJjat5K=Lo@(-BsjrHIDm4wkQyRiuQN*eG$VWVO~wc^CEl5gS(7@R_gkdHFRI599)j9&!bS18U^5}LNV z-L5W(33cZkl2bx~K zjwEMyZ^pJiMCx*Y2@zA2dMDou{um=Q*DlnC&&RtOgRS$8B48vhrYPxQb5BWOc+d`( zFcM!Xat7JN<6p?kaX>8PXY={rkF6l(??wVKXFP7a!t=^bikUZX;b;pBGfBxlcNi6wWXvX+xXS~JB$^Y{6``?IM z(zDeW!;KBatD`HBBGQzGTe$`x)F2+pv^hcl`k8tb5SXjB2&p zt#L9j1i!-TkLgYomZ38(N5e8JF0GGA{<@1|{TSg!s9mFGn?hq3~ zFj@D4np?5kv$%|8M-YLgrg#X07e?|%Y?CE`Aw(a!Lyr=2O;aAggt$^-Rg$m#vk8Gm zWi?uchEGA@^(u%S$8~G%Tthg1#rqS*Q)*AAljM4x`P-6_mIDv@(=sgUWAPU0@_@?; zC1Stz#jfw>b70##{4`y|K77sSR%Ie_I*>wU^z-MLPhO}4IG<#-BZcUDSV?>Z)y zwN1#y*91u@jX8L4wrUfTvcG+o17(khEuVU@pwNh%W$h@K&?M*9Z6ka|wkdtz&1N4S@be!XKVwGlt3gwTXh`7`xIUy_4d-NUpCdM@8kOwXO(a z8%ZEa-^2O>eSCZ&Y>PCTZJfV^$bK(kYwn#_>YZ{w(bu_|)o9mpJ^$|kUP-X4()Cn` zm#^ptUD>3Bsv4nj<`MqU0UB{!t9KL@?>_dw`g$?Pa6@TUCa^JjOF^}OIvfEzV{AZj%N6caO$x_W|b|*K8ILEF@(?BaRnmluH zLci?Rci0^te4hVt!OF#9QHukx{F3`x56KKg2$|@0G=V{_2bX(G@e3C|`}^!1ND%06 z-~idahx$@9fFT%c4O`O{_$c;(1HVGpDvPaxDa{tM0L6!P$YqZQ+n$u%*8?)t5Wvb5 z$a05FGilpBk20#3=)|wSi>Tbr;W1jPB#nYN$m1(hd4^M*)_d>Xy7L8tBH2H86*h?_ zs47X#=q!u2OZLL;hyTDWwz8<+&T;Dl;7_W-V)@gP(H@!;IBgZ=4*Pv_Tw!>!(#wy! z)7^ENFD5Z*H`E0B`{reV%8{7LTU9-jUNmwF#MWU!p z%K7bmQ&V=H5)!QSd5@6EnS2q*ND7Wh|Je7M?@Ob+pVsXtksSj!$V#c%=gsw-E)1i9 z*#6>M7#k^fRrcMbcNE#o!7W7BPe|G+&*PoRuQ^#ag8Y#o+s5B_U)=sLMdo7p1iBMQ z>=nN<|1sszg~5qjM1AT+OKou*IQsm3nIe2)1Z%v)oLexB3HJB%S!0Cat2nFk;-i#8 zW2yDZox|t0r8Yx%8FRiNH@3g&Aub$~C2CMop>x~wc7f8rKBZJX^F^ONJ2PlQ=Cyvt zBL$Z1^t?P=Uy-29<*H+(?@4YxO#{9+lM!)kfF=>B0P*lLB?maBukr>SyuT3Kf!X^Y zfx-h>b%YZNn+QK#zEO5r!jnl-4vvczf;{xF1q)drY3LK!QZcA-c6pp()3lo&)V&_C zLKg~!mW{l8eToWH|E1u4>Ff{}JTp^4(3{dx*8?F(lmNi5bcY>Qq*tFo@i~yNYUXy1=`W zvEj^!aygeh;^JkRA32jPV5ku7<#n%)#{8hINeJzYj1rM?*S=S`@BQlf7 z5_C)H4a#m+kC&gzm&bjl?|aXMo~<&YOghrBjS0uwFAX0}`#Sg&SLQsZ;fu4nw3{mUgp*K0Q@H+r1_KxTpZ zT;uAt0gO$SmP*F{caQC(Gha%T6D(#iS=-0fOo^7n8{9mte6}rT<Kjmh8U9Z^YkrRd56YGSI{vXd<%2G1{S5+_()26^(%8j!$E zaR|f`R+|nSK7~cDWTOD{%k8s+?!6lq`U4VHh(4VV3m$&+z*!^feK^AGC(wGcJ^RLu zafCh`58C*=x&!|WBkL+$xI0p8_GW4egDOe`s8iAa5NbIO(};)(#t-nO;R>vyb-pk@ z>0&OLtn36g(sWLINN!gVQK_bpbUvCJG5dO+`o)@jV*y^txqDZUIhX&Ivd@A_myXEy z_P58abY)*sEa+132drmCvEV0{V`!6fc^m=o67f}JiE=X*V71M1NA3^}8P-mzRc2{= z$t-J5YRw7i(PBIQ2|9EB=3#n|`^QhRNM6WTc|dJ#KF14p0%;Q6pZ)J}eG}tDFI7R$ zeB9T6F?eB;R$Vl0def?oDc4eyfH#!9*CRkTIthaHU6X~9>^G7yef^OqJR(8TPW*-@ zTg`$vAC23-n>{E1K=7ZSseXzDi|%J{5{<7pJOL%@T*%-u=d5Vz-ZqCV$Gb#!4`CRJ z_NwyHO-G88T;=+WMZNlj<3ceL=Q?FR7&*nfi;63J^Wl3yc>VvnbZSQ6Lu~@XIB!nz>OVUyfoy=)c`GHla zlOPSUJR85z)fX=y0)vMLDb7V4-`rtfaR-4`xoPhRlfd9Li!KUakmzSmHN!m=n*RD1 zbcjj93WQGyLzf@)kewP}Prb1et6K8VZ*^my31W4zVcvX>>MwZD@aavClsJ%&+TyDK zBLPtPl35th41dmRHNERzA@Rt%JDoLcL?og`GNkzby!B;4$v=QUPn%Mb-jCqlO>Yax zuUja$>12#m3*LOC5irr`r6gigVcl^l|< z4tS``LU-Ql4i-Bc6O$Lmx0Dvvr)^MXVClx&5RimH;wLTfWA|1f)Ih@MXIsfpo@zx5JXBnwe>igCo(T_HY{sygl8A%_^Qj&BUl-+q^w zoE@4es6VPY)=1xaNDq(b)ZX(9Nr|*+6A_GJ&De`?w4=5SY0|rRhD?6Gzdy%L8b$H* zQ4h!h1f{hCBx_<&TqhnT<(4o5Z7`UfAMuC5U}$injO>F3sYQR(h`z8F!q9d zKbzU(7;9M+aEqroPjo%0deu;SQG5K~Q8Op@ocE!5My^w8W=!dq=po3MtgSpLvOYO0 z2iP_PD*jGF!{+wRESUZj^-c3-zp0a)i5q=xlok(HTAp#Cz=;wY8I`A2M5FBDT$1*p z^2=v){7U(d{wKeKUrSE@1nHB+bnOudY4rwj=Nf{bA2o8TvqhfZP>#MBU}Oz?S0F5bhhTz0Yi{`AH~& zu3kkeuSMN#_Em7<*^7#C{!YrqLcL6C*}ntJo6gyP0yewGB$Ww!kAZi8@zF5-2y&T> zrIz+}!iD$r_bfdjjHC#6`t?x3SeImU@W8O#%^5`CHuPtFqeZxnygRwj*OIzh<4tVJ z2{sS=-??j;!eV%9^au3){bEaF=)@=?Ih`D-Vrm-dcehH%_xMxLS&YMU#$Lu8pqLaN=L76HqL&jNe{D%RH%89bCE*0}#>g>ZNLqePRe zqIl^Ax}k7~mOOA0g+M5s(HjPUL6{f3)tW@9`hqrewfPP3v?6q0YdKj;6h8$Bxkw`l z&dal-todb6bD8p>vipG+Q+#ho%^&Hwx(7vV4Mj_DX%zd+@e1sa3?dG>VvKj+W*ZfQ z3D7nPIne|;NazEPr;5HV^Mea=9E*_Z_0%i-GVJqvoX)_&SY2w0*2_9+ojWqhfevhN z9o%wocMc$s!RE)OSEc?AS;~bKGOIox?=i@~h*QCgy^hpQ7}BOuyQ{sa=?0N|xqm+u zjd0z3LN4kp_B^MZ0DJOD^tp3KCn^0#s3oKDcV&80B8v0v`AMWOf#x6FR>TPEeHRr~ zEGCffx-dD=zJc_bpsW~c^nCGuSsEK%x#Xtjf${fP8x~{xXV-dP#=Bk#+trhd=*96| z`OD{z%QPh2w0!4_>Py)ZV0ynEW$5XVWA)0jn&$D9WN)+K6VBYnyeg2BAX9r@znVFW zG2fuK>}y(QUV9b^e)yM8{3)JS4Lh0ErB1o_bMEcs+5Jp*h}y&toh)qupQ2oQD`T&7 z(*b@sK!8N8F$FSV&#!{t4$ifN@Y3s~d03!%D~`f>HE>UPNBqzJ^Ubf7%Ukhg$eyyV zJ+Wef3-Xa^33EWj#lmCw+hPv%>+G#)FI#X>+sSO zZX&<>>NfCQ7&45}Zs}IdcP%L^tg3M$<;pbfCZ04geVR`@FG0Rjp7n;f^7dwpm6k8u zfx{=`^n@fov%;%K2PDmftDtyLT6P)UYuzWctD;<*=FFvMKB(A<>(LCo+M0|$UDX@f z927eTp!oypX-LU0Y!Av5cd227r*XBc7;e1EF}V3W%K0T4f?xn;fD1&k3l&&;94Pf* zliCe^gLXzMnprZL29>m%gClXmyjLCRDSH?~`one=L`OCSBjg|90)&Z)?oI42MI_@K z1{3k>fM=lxxEqoYamMsjCj@&P8G@g1myF=nK?|$#e-4>WAld!-)=qs{&Y&{GApfvR z3)JERo;qDiceNO)XvO5lPPvy(sQ_qwTxQZ#;QMf+BWqz zO3h}UUnGB!f}@S%vmV1R;_3iB9FyL+w!gf1IrwH}m`>PD9RVoM9nnnMcZobt`y1=f zOb`(%cv*@0qnw@iCJ@%&7>`V&Uy}aIqmJTc6|(-D*UZMTl})88Q@D=FFwO3b?8}Id z#u=vO0HAMNtOjPnJ1g?9$80UGq#S|7zkLEz=8CzSH)2Y}Wt&%x@iSzvlS- zL8&CTiQGV34TuBj^7PP7v@vESGy{l#$60{t%izHD&dWUp;euBNMYq&cCyUj66|xk; zH$SCtEojkr3drtyXrHJB8rTzX-G6iP0gS~7D1~Lmg+NFaB@8p477DR?+VGv`T;sAx z@kWOK=D&8dpn?J&)d66#Ff?i@O5Wm)9e!gsGtxFBlH7^66JEYg?(YUQ z9LKf1)p1B3d9B7vFRx}K02qD>Y<>FBmy&BYN#iSS#KI5D1gko~5+t6CcmN8I6GQ-P ze}`Atucyy$iOqs4@REdA`Z64@>MUju{iVZodDbOv+!w(uv+f!?wbo%c{5CD2d{ z?^p)~GRickNPH+J;~r9IV4Z%OF&D!4N-bAg0Hbvn-RB_iEMbwcspk8F72aT>N{YkD zWWeOkl&3<{gYAT*_3?y!n$@nx!ly?FS;62vV;gXlqp2f^E*Y>|Cl}Pp%EM1uv;`etcl!8H|WIpY@Z&I?u*a-Fl z3U04n9xw++U(3AQl1V`aJJ#Mqx8Cr3P1#AQSNE47Pb)~ym93fwdfQD$;HhQ->X z-1dg;C*ZzKdGCXhybxr?)axk{gC8-Pj_E5bfU&J7DsNJkJP*xJdcop?^Ws#Q4|O(2dWd2!*;MqndDz z6T~o0hi8PJT$9WLvk7_`K@xYI-0AjrFX2D z?wtAK`#Te*2(D^wNa?cwvNyr~W?bgobv&W6tp*KV2DLsB$9-|ij6PO@J~01yVwDOw+a9^a!vf9>`>``VpK9p8&C2p z{-1khg#i}y?uXC$MTAc1!_4+b3LkmZ6=j8|OWml+8woFr%$_h=dl&@EADF#{+oFoN z>OINm#c~&B9BM_L$9?Qe!5Vaf*BM7^Uou;U8*jC>gXN|!>q+WQud=32qD@olNSfW} zD8kg!?;jPuciX?uGeZljSusK|L3JS~vVR{|Ee*O&>5sEAg1nZEC@A0Fg6j6xpUqcY zmYp`#?`0M?RE=GEF~kH+Z;kPOHttxUD`_~OK&&R28~IY=;@WN6hm-- zM42pVcyUQ1=%{kPQ-7f|Jgw}O(e?d9CJp`mj5I)4x{IWXclqv{v)z-<(Eg~2yVfs+ zL8AYY&bOO-$Q0vkDz_F&Jn2rCPmUaJdbd_?!Lf@1- zM3l!v9rT%yI5{f`kJ|2XO8<^qSb7zhC+_*cs!48D>gA!YlPeN^-RcF(5t+n^HL6kL zXZ7kV+8n%>oqq4jl1h$4FGVd)RGP-0=3$K|pjw++i^}}sjvAvh$W3i`(W0+h*o$uf z>Fb7=$J+QQBp_t|`Nzx5OR0i365No{7r~uX0|B15>todbB^T$P;uMnpCWoUR8DUIf z;7GA}O92}Yd{aguVE-c_G{Wt;@f<{_>Uo>E>*_`)DmwSxyc?i8ccDi1S3>-!gX zg*k!5Z@*g4Z|UF&`y1awitMEoLFl`)zU#04dJj8I9~t&ok65b;wUk#YJTsZ^*(v{B zcp??kX&%#^z|DJU+euWl%sJj}_|$&tI|pgeZgu&KK-u`wrZQ48J8Q_g$GQXWPVC8A z$@{Q$cZDdICVZ0BA2rtyLKp%MT!zlQSNgNp1{orVcE?6rhVV!=S@fnD+GqbRubX;p z9z{5`VAcdCd7)vN0= z8ETb**Zy*mBr@;-DE|Fy+;Iwt6Fh-;Eip7dZ%YaJh9kw^FnCm{(KY?sWnSc0b_R#ICNuSV{keHPHfuPbS z%m42k^5KjSV|9)UlQY`Q)@m?ZKm zH2^)&2UEiVO&m76E%)WD31l(rZ&J-cBWkhN_9Ro!gf#N?oOsvCC0_wHGMwt`OpXbV zt94y`5?0j#2GA8ub-579hnVEim-ri2_$$9GCO;8IdSvx|Tl#i#Z-cULoksS2Lt}3# zvmF*6!|bN))0hy^4O6CjBO0)JD4>dwVNLMi9$FMFKnDl$gZ!48mrY#k^<_4CJ(KFAuKv^XxZFDND+1*hJc9tZeNYKbE>dvJ+2SSn zo3)e0?tTg#i@6BGNqV=;SCVmOjosl4XBM`S@`6hZSyx2wNWkG~NE9IYN#LM%YBI8|{Xk$yU z(6xkz-{zIf+Dezc2oSOs5o*3KvH3{CfigVLA3{4_K?e(-rekn0y&}iZ@5JoaSQUi;&cY z{%g5A^SvlbyZP;3MsA<2XVmsmNWE>=k=@Z;>pN35KLeO3UPDo95 z`{J-q1~5=Y+b9J`$Q?rS0%Jk6&`1L$YVl$sd%4B@%DSJC0^PXAyDJt3EJbsxzhPvy zCVRNM4{kl1n~bifO8fZ`4q=;nSdi|a=tDsPP{u=OhvR&qp{XjHqQwDyI_dMWP(mW4 z>@dlWZKAgJ;&<9YBN%4okpanH4y>v3Tl!OF4_J`59(%LhmQ~#@qc^S4Fj5YlK31Dq@#V?`~^z0P< zNKkAeqr~MO_~bHo^iJP(rMPugWS(wLL)iYkdB64go{qE=s@+({JokJyNfa&qkz)Lv zC6J%ZU1O9(#xS!n&-jl(ZpY;TP^RLmb?u+B(L!d&?`+(AwIDY-QSJ2&Cxpc(Klt>w zI{HJt^iFT!)+hff=!RC(Q=s&!%YQpeCuk+_RDRyfpdN{`8CFgH+lB}3ks$@&uls=y zG6(?>5qrT$=F30KZ#mN_C|HHw5>7HQW26_@_G77;5`>4ooiBMTa!pdD3A ze7ZTW?2lv=a)Vm^fpX#Kzo7ha;I65&lgbYN?A;kd>Xo<`_r|oUEY`36bCdPOvRK_NBKDi7-dzXd`%eRrxbBNlS{OcOZ-$4&QE_yWmu=Hl1+& z>I$M3A$IaZ*H(?psI8=?zPUxK{QLI%T9tSyGdAPH76r}+*{0J?!@7Xn3fQ%m`(6_s zwCG0HVG~zHYZekw)1sMR0E)5osxCNgFZd>#O-Tt@?$w{`W;kPMf~ezmz%hkEFdGy> zYZnc|93Q2uo>PeaiFJUb6;RU)LA(07C$QmZTLa~hDB{?4hM!;J2onP1#CUrc=~;jQ z3DH4r)(fjC=&0O@HxCDj%jxf|;Dmn+sdvb;{2>|};Z=<9Shs$B zg~lkw4p{C(-RYX61q*M-L$G#eAu@VRc3{ik9=&co~vz-vQ8O zISIC{$mj3-e9JHc3g;012-HnQI)LkA_AKH|vzk!AUf@AC2FHi#!!nXqqAJs@Is((& zn6(v9-3P*}FwwRBFuH}GnZ{`Vqu9N>;DVksbedc*Bf3A0f{p_aPJ6(RP6bO)xGsEo zyxQ>pSO9vhzj)^_o%n86M_jaWDd^N{KJw;u9^TR9EspptuDUU4f#SM-Yv=V6oOF3d zyFxwWPVzwWmn1#z8xpiu@9gjDCkLM{ifI^!Yzc?5PakzUg?eAAoE9rS^CF!VL8r)i z-vubsoCCL4e3G!x=Bl-M%25~oxjFpX(h1OAO!sXQo2Wo$sUcgqBeF-el?>k?(>VwF z`rplOI4kM%Y?eLOnHuCYKPO+;NA1Vvrcn1Pq`l7J4c$a%PT3(2=#-MgHK4=p3jh5; z?e9a7ON@~;V_bY{okn!6yx@X=OO@d1mTqK>rMr?{ybS)|O+JVY8z>v$Wol^i^eUC0j0rlK1U$xGuH3z@Q5m9+?*_ezIRsBvUnAMjFV5 zwvnS$nY%a3PqM;Z_&GI);rodrpwkCH(HM%%O+>1o@f!GNdT8$$336o_z9mMY zFW=)eFSEapeF>*9=|4SX3~$HfU>b=?MWF{x%=OL!d6ne`Wtx{7w$~oqVL>L2J3Q8@D6H!W*<~H==`nC%kWXJ zGifQo1ioqYQ6qip%9I`)NenaK9*yd|tez#H$=UtNtw!_y>sw8gcGQVt!{I|Cd{p_M z_LA4VndpjXpxk52NrL!qvLtkA<2QI&a5Aq_-nmi9>7VS-Yz7Z`ohg>WMj)BIx)O3+@gFx;pet*sO^j?k-E& zX1OuTbTHbY=okGDOeOa|o{GqZ0Eupj3Km@_(pFZ#X%o_bzOBzm@qft@8h-oZ^6^J% zdZ2z*NUad)m~qWr0{$&u`jg$QfeGZezG9uQ9}LoJqN(i~&UDix=X~tfJXE6QrOe;^ za%SYym`h?87_B{(pT=h6MHfX(l~Y?f&Q!fVxMg3+%_Efq#q*RGm$ubM@#I)Y@2Lt; zVqh$V746<^ci#UkI}f8p;9R({9W-yZTauDc)w@YNY{;Z~#W&-=zu&S75ViFra32Wg z>CgnfJxt-($yGkY7P8-am5$E~iTp(Tw1TarEK7UwgNG}bYsmh$;54BGHLcr?(4XXu zL-&+4=-0tQOCG5{lOfnSK<1ohpII!JYu|k5Dt`R*|L`Ny6uFZe16*_yR`Vm~wa3SI z3U56P|JY6V*$n0keat!lz2EUa96H)uyhCkpF|jhpPm7AvcJlf|N_d%8VrX(>^t_D! z?#%I*JN3DK=QieEZhj}FgZW|s6NkfB6|>wUMpfmqr`B?rdoAwn&hNJYVg&9EPZpRDz`aH2E73TX0^H&wmsC^RY2oP_%e@)%}|Ceq+)G3Lxh>`nz}= zOeF~lQ`5QBC;}LQ?A5!F(CX@-PrJn@CBkguIz73%CvUF zj-UqTg_=4qD~D<2uuYcM{X^5#$)I~RM6{Ye1ztbm{lZ?Zm~b=C`;D_){Inw!gtpp9 zy^$JIen~7lYNSA%p|wl>_GB+1Oe~8F&E1_x0CwCNP2tUCts=W^K;b{;vS5a~;^6H6 zONNo}6D;16@4pF$C-P${aEE)Rk{6Dd9c&Aui-3arf!%arP+BF0zED9=HBl)2YO8rv z?9aWuznFA9O?SKzWK*iYo>i-&919)At267r#WJY`-464K9g6q?ag6wRIMVArDh$GQ zJGbO#6T45}(*73qPcZ6oPYR#)jmiF<)=&Ic9Q&D&9GDHp`pQ@!1^3PG9I?J0dhw~K zGp?@i@V@{YK>6~Cz_9!~PKT4d%#O|22a|?hykXLpavAhg|KxccRhy8rjzkncl42*fBC9rj9x8!K>FJJG3{&nYaon zJFlCi8<%NF6PPc6S3pN^QDRvLZpXef@>Cej1v*r1v<|Sc`s*j>$;&; zWq{Mp3>d^%gaXLepir2rg{IyO4gq~X7(^F*yn_uNmI>nq>%*uleu>7~Fha05vP>}^y>I}f(*6s|*5wH950B#*<-}y??2g(pAI42H3lNo4} zR+MiD3jneph@EeDX--hmDII(H6Y2UcHSOU&-KKJtbAJ1fz0ZphPQxCGTCF=GCqo`5 zQrg-X`rGgLK19i4QE)RMuL04uB{8i%>?)^zf)9O0HxBXKb!AcQy>AV)W*;5!3XaNNfvtOK~E_7uFtzmz? zHAAL_IHVnjD$Na2RUAY|y;67U>OYJ0==d?%`d(kqXJtORKaEoYh;kjJ!Gs z1_5UXIXw6?CCo--7@e91xT*+)Y9){=NiSK=-f4@FiRF-ysBF2J4aR4Y==48-Iiu~^ zHLQA&`n~K!V8Y2ukds)&Cy2i~-YA*9iwGSPqkrcOVl`;goLlN!~j^&V|Fz{8mrPv%_?*a{4dkm^{o zTSXlT3Im6)G>@?>!hf#H+H&|p>CugjCYe7>Af;ODJMibj5zO~HmchLO0FL#Pg50@> zzb14^xBr3k`pL@~Y#JC#>WtzH^A@2xvVsHr zLb=QzN}La74V=xYiVOJ~&Qr=Dg&89W#z+*d0{R|63{6M~A%9;-1QK~YqHW|)91+la>k?)mol~L}9a$~0Af}<7P z)SnVjz0u5v^$Xm<$hK*m#M(x;!&y)d%zF0MQItNYafFj8M1c>X30mieHY;jTl}c#h z5lcR*w&89huzVT_8+&oeuV6Mnqbp%#y&H1S++Vucn4*Rf5X6MUSq_PJ@~ENeh8#Fh z(YNGREe8zj&oHr+)SAy(M&IHI6HUn({(M3p=H*h#;qVxFJ8JRH77%T>;M#>TXeE5- zA5b?iAW$e_iUJ8~p2YSE%X;y+)nFhZeYVM7Ff}nzfgK-}F)aAd6$}=I-|p`138O6; zB?UZx+B9P1aMQ~c`*Z-r>|QE5I1UzehJygYD{1=P6bWd+aXwcF$U0ob3dxwQvg5rO$(q_2X1_!P}hq| z@c#q<%mQvp&%f>6&}8`^^Ze>>?2hFn2oS|hK&-4AwybQB5`wuhB zJ@+}+=lWdXEPG28_-|AM6yk_jN5TmtL@fYLt3e@5!gTQ;H9RED0hS-*-V<-p5Jhdj z%$FDP!ff3D?H$^>Z%g(+V82#9?P0IWpVuFwdgFUY4K+;F&jC(NEM)r{>6d7J5hDX3bCt@;fq{`gURHJw69RUr?GA9D%9 z;b9HQ6yC0eQrW~fJDR7GlV%nk))&De?2!eq_YuX;&NfE*+eJfFc-vb*6d*z(|9aJa z!`!rKSe@a70BZSrFuna&MLj!$X?aBubV@xCvVsv(lSn`E%Ic*Nh(iWp*lsMv(!4=- zo03_>F;V1Pz#Dtsqbw5#wVj4{P~SO_$#(1SWi35x{a)D|RzzV8Ie08$G`^Z3U|{ZC zZ8b&gryiUex@vsUuxZ`gI5M?(`{?`aPmh1Q^9$!1677R2Zw)?19`njVsoqeR2oqF~ ze5akJb6`rI6f)dBG+H>9*S>J(d)Eq18An zS!^X`mO*MxxVkz(`=Cj5=$Y-zzA`S)mpO79>Aykz98K6(zZx+NXK1C2Pg4yuKG(?< zk6-pU<~kb8;2C9?Q`G?BuiZ-hr`Dj*(hsnHAwo6Z3H>5`2i`^}4K*X0W>mtdqtyZb z2#mY!Xu=bcX@5QLP*Qw1WYV~+nXQViJX-4%vrKa$akkFj!1l#_lC6{~q}nq=Sx96W zw6Bpf`9XF1?VAdWXd;&HMb?RvOtBf*u`w7(NzDsSN_r7BeX8BD*4v zc@T^qYga^-u-*B2P=i@x|DBOFKd!S#{+-^vxXkpsT2!>385TZp$o&!Nu0K2Vwg%Of z&vX$ZRjkIiAn=cbA*S+m_vO&so@M|@j_KKKgTcw4gI?us(dZ`S$WsJnJV8c!l2?A$ zzSYk?x8o(VQuF<6G!r(|ws;})1i?S`${wj%wogUFl6txQ0?>kq`?pBO{_|CQ6nHJtp zUbZIjWcwFvJT}4==)K^)ebuCB(~ja!49wuqCZ!)?)862(e{nh%UaaZVp`SMBnpa$KcE!=!Ty|xM+vFF}P4kb_xZ&r`7vySzoRa96X!;WA}+(CD_ zFcW;$IqJ|XKT0e_FA~yRLr=cmXtKc>V|&C{g3-T0Dl7BL`4Se2?yq!Gvlcq2G8mHXEQWuW4n(~vqZluBQf?c+|lMM zE!BS)ZT&mZDI-^}0E^a<4py}L%)xO~{QGQy^RChZq~7ea6z=|#i;~C0To}uV1uP&_ zw1|G33gDO3V?nav2pj|G=^K*egA+2;`aPPuqUB0Ykc-4(6>;nH>;8SWbtb5zmI23L zmrK!>>+KCU1}L=acViaorbI6^97#z!Mb9_#17tTJB$(M%ISQvHh$eJwaBR|O&qE|t zoxZCxVH~N3A|^E;xG~V2Rwrz}PPok9fg|&skE#;p84A%g58`lq(ebXv)IwXcR-`|t z!K&b+Rv&iOKPlBgzmFpm+tKl&IDbU_Sc&lc7>+yCxIiFnig|J}yyVhH-H*m*4|y6pZMq&aqb>lmq12BdcW>p14prBR~- z%4*IMulcKfE+@w~ReP5F)32Z8;~#avHST#3;FoZzkcug@P`mf5Oc5TB=iRsWHjINX zTU0pJcj^tGW=-(Zx+&UEKKkSaYcJHe|L1T0;t00tZVKQA|DY_b#bD?=-vz|ut+|>J zK`<40&Q!xi`iHS93dxN`t`smypxM0C8Yzpze9MosOo2lpLeAWj%>N%%-aCKBpp&$q zh$-_MPMZJh48$!qai_#rik~7RmQTIRe@Vrf@izk>(76yl3bxFi(L@T3ZZ9I>wIo9#%pSSXu&8Du$~V z^fYq=9%n#|+lQX|&~tMni7Q6sF(6(8tS&;4nIT%wZsi_(JX|-0Ztm2nC=yx-zU`6s zcqjbg&t)H@LZPW9pR(4E7#Rl~QmFQU<_9)aWbz|kZ-j4X(PIsBJ=7B}B|;3iR_u(` zyx}6Rp-XQkTUiL%ITPIFDy_ZQ>SJfO-+WWzg)?*3_nTY%Wu=xU{#?P>;CSN&JCsop zdyPhuhA=HJ;IL}%$AWxh!M)V~+-xsdK4lhmiMEwEEwXl&NtSJ-RnZ7MW1-q>e}b$h z_$A{KsA=Soh2fBFYK3;d+x$d|Jd@8XVMoZtkCoC1$7Mea)Qs92ux8_M>#1s@{I3`rUI8rFziD( zem??ko|0R>(#j-2yv)p$#w7>Ddtf8SfD!00Ju*goyfuP@0dk5_D2cljFBG10%b5lQ zeC94o;^V~Wz|7n|5h;g@;HKM*cS=ab6A(PWum<0=3GI5z6f|+9pRL#)f;s>jKV1&- zUKrd$3z!yKH+T1c%dPcXu@`4#&N1dvSoYmEgN*)DudH~hR{bq-dHCC~9CYWl#bozx zPI_1Di31u0q*EEyvcBr#fJ4FhVV%n?O=f#n~4JV&?JR>MC_P{^@;@B|Pls*oQBy+2C5v!OA zg{DBJ1P6qBVsp}Q4H#?$O&ySJEV9N zjq)#483sC^x1?7#?K+R+|CowZ#m!4$>2Ap<>9!CcDUgg0-~Sn9=L3%eo>R}Aap9PY zi_6Vj4E3G@R!H5+@a8y_3grk)57(V!9)mol_gi=sjXg`*tsA z59;^$M&@t9xpDo@NyGeEEB3$e?fK{i1TNCIE=5j_3K5qW|KU6Bn;-wcfDx`lIk|5pYAi+s2T71w16>Q|4u+^9;I_C)0+IaziMZL%3?VGV z@Zf2!`E&0gu9U(Lt2~}h4RFQ28$aShKyl)#-X9uf8Z6oo8v-MQ?oZV&lM+7}^FSd; z9$hoz3$@X7XytDL?+BLcYN+9S?k#)mY%coy&+aB2yBPN-6LK4p5d_fH_&SAT!_I>z zYf?&A@B5VE#6O+Ds4=g>^ND10YuVH>`>Mh@qDR;$zdLU%iP%x8`O$DGF$)*!#&{6b zwzPLTobBl?w_DPHYS%;>03(!w!iDn6+3DU{8$Dk4^WgCRMp7YGgx{z!H}6ULFu_u_ zDB~aB*JZ0lnvVJSx=#O8o$Uj*UHxurxA#4bs2wM`2*UNzXC|H2iyOOiAEdA1RRoU3ZI5yS$w@H%{`f3i(7BGY!KV-L} zC7qoa-N!Fm{`^fZ`&5RppVG6Rr=d^bDWV%}rDsMsd;lylU@nSRqt16(SGy&0a01dP zWMgmQSHK+$!f7HA3=EGtTrHlOD?U+V6=oa6#{pNDB+Ou<2Uw6Iv-$dSkdiM$n>X?-cW z>?UfP8(n6pV}kK*U*V-)3Z>w>gF&HxO<^Q8^O6k3wI!DF2V5NnwxZhSH6RcDgVHO2 zp_>1pV2>~^1vlF;k*Fd0CsVQ)C_L5v_AVgq$;)>y^z?adIxwUsx8lYUmqfn-&@ES% zCZa1Hk^g`k8g6a0z&n5i^QhSZ38xnKHn|gfDikaA@F?q2LF_0C)qwnR?md-@DtIK3 zbpIc`I?mdkpW4(%kYe>>^ry*iw=w6BOg)n+G)d=cHRt);g|zVrlV*v;=X|f0OI;PB z>Sv#2TmN35uzb7ibR+wCe)Lrp0r%VYGR3(KlV4|-vg?M^E6dFT4x1q1+a+&bh51tD zw$hc_erBz%TT4$@*tcz||Fmb-o;g?~ekl$csbUh;-TlPqc-{8YK zQ}VhG{8JKH$(%_GtZ1WPQQL05rwl9F*uB+mx3&gA>)%LJZ*e)TS~ju? zWx_GmhXj){jRK}KPTn)UG;=R<8alIt(4Jhp0E$?4UYUX|xwG1rDjsPJ%beEt4g2t{ zA~Y><^yr#E{PoAI{C(*97`KRc?0`!LrWehD^lyo)ff{ejih{swa4nvHf7%#jO?fpe zVGib79ji)C7@_^VmjCD7*KE=4Of2Eq^F5JWJt@oYrlIaO->nIk&WXWrDVY{kbPizK z+O5JcCQlNjx+Lh|-X~}9=0*6c5o5jOluaX?{ga)ZQm1M9_KP4uMkvTMYGgi3Hz)7r z>ux--739qLH!*_E?F>tQ*dD>HVdjVXL5r^Q!IK%;Lnf=8nD5KFkgAIbub2i}BgHncU1e~z@Lbl?f8kV)Uii5;>`(Pq zWGJx#1cky_;jrvRqQ?D)aKhz`d{zu~l0**k*)MZzg|R6(%*>JP(YPdZ0pnKQk==_q znL}ew%74fUhUg~lgm(ei+yi0zN^3f$1pRL!Wk~%MhdwToT}Ly>{tiYff%zz;A?+6I z#3vXy)ZSai!mTMiw5;MOAaf@nm@ROIhCF(Seo0MFco+8LJg5nkKnki!3jF2Q;MHsY zPYdvj78m%NC?FF*6WsdTx#FS727WW}dGyPKkd;c1j)d@FJ*OhZw~7lVhVkOLT?f`O zhk4;u;Sr6mAu+OzN{)CK2#>GH`0!z)R*rAOv&YX1yI2{B^DCTl#55l^)Gj@;I+s^X z^ETpDaC)d6z+~72xWqtQ#_lw)X`cCYMM@Krz>R15aF%PTou2|d0GNGx`~=*L-#eq| zS}r%3LQJtB!sN@cW}s>~4~d}mg~)mDq|w9^)TD<18u3ECUF?m*Z%M#4T%6hMr(JrW z(zwS2^uUJ`KyC7|yCxd-c(pWpVD?zJ6c!fv>UDGW+DLpE^H_J8$EFbkLBdm?Kk!FN z)XhGi)B%L*3mW)e(kjF0HKKXu2R66|lqM$UXfU{hR#b4n{m z!sDv{s$uw%rSRlW#F&L5Sfc|e&}f}7tB&b6Q6~)4o_WI#aWrFW#6YT+m7RFI7W%Va zh#i5C2>_8@Vt?HuclU9HK_@YYfMy|FO3tc_ji8g``ucXtg_;S}p@HN5a4gSFdx{|- zv0X@bXIF%ty;;(^F@B|^vGHGaJx_I11|Q5ycQuKKjt=-nW!JFBqJ8lu1NZG5f3+x= z(ORwgk!RqN@FNF>&dg(l-Koi7w(6goF&%9Nixxob!@tc{&%1_v9?Xn>f5ZMWcfa#g z>#v>jC|DR#qe`h-A!(9Jxw>*25n*Z)ZRQ3~P4-8CMT^kTud8q3(`c&IQPH6*=YhHew zNKOt8!3Ho=Bt$~*%(P@N5iGq;v~h{x&1G?)A=&<4+Woci`tmZ+vF$bDreAe>*KQY!zkCr?5-soZmky z##1>G59@v~`WW&)&$h3=7ynBx&tVqT=(a}1KR5C+Oyzfp(&5HKamXJ;OlPg}7rGk@ z?->5$5$Pi(vB=;A6XP5OkpvjB=dDAzBzt9|Lm_ zsht0glSw+S5Qz0S7U*fm6F=Y&mA{56VnWqN${}M+U+4GfNyNMHm=lo*pL)TaOE_G8)i? zNHEuclDzySGHI)yf%%OlM&MSy*KyP-k2@XL=b%(tZIGN9yd-rGrzD57(+HmlAG%ii z`eKFsB#1b7f1XK&qxF%Bqx4&+4eNw;y|!dPX>3vHDgXP0L44m-8@}W>%*HjCB$fDp zZTBT;H+`|F{euC8gu*qA^ZLU|xd-gj@EA)?p)lAsY=#i75^GQm9aIaLgukqNDW;FX zD98uNw!tdoySlkqz4;c*cK>e~kC#()VR)vW3AosaHm!nIuOR!zpFWx)!-6(}iK{o4 zxE>GQD5KCJy}4>cch3`{_+xA>znp=O6j#GO-iJarEX@D`EuV55I>uicbqPgJh)?a@ z{&&T!`i!9N8Ee-9-uY&I$%)yloJ^JU-;;Nf3={QbKTDr}@207|Wqez@s59SSAM|th zf>V#{Dc~bvHncFl;7;qlvs=2-qhvVxJFeX5-3`@?>d|V={Mz!4YIa8gL}9%FishY3 zE5~PrfDk$XS_9`To`O58jT~mU0Dm?ngpaxM|HXc_qi&d1nOg6+;Ov8gja#@=5~~j? z!25)a6(z(h2#WQVx=#bSE6h}}abUfm?U5H^xk_fQ6#~Hc)B#IUkg?{5n@5n-=;#M` zVObysn;rSiFk#{!JP&_AT>dF*=ltt&J8w7BXC0#KA#n@NnZ;V|)DLB@byb2zrN=T) zkUi&$MqnWF+uAPuf?R*rNHuM90-_EXW zQ=&tbty71!~J#Qo=zGCelF2_!;sZX2V>iQgvw)4Kn+yiYMRnxlQ9 zolwKe3DVXDS*;fm0VJB&SL>D_wr|CT=4l$V9g0A7dX66Rxne^7@7>$O!1Fp>UMSRo z^>o2VNSNnCtRr{P_tcBsp97Im(wd-H?*s~!CW`vH*3W>b6ok~u>xL?YfvS$gj${NF z|3oFKlQf5v9NuwOkPMtInK2G23?)9Ppi&toM6EJo>1yk&Txo3|*~Ap5;#8q8-V6 z&6J083HrYpnxHB+H@xT2Wu7FB`(7U@Hs8cUqK~c`lR<8eURBwYDj|KF8O9)r*Gt!I5hu4^}Jpi}UO*I{$MMujpIBK~oMdiI4u=U7HR zCQZJ&7Z3W%6aN_7{r20@-t=QV!mw7+I7Bwu-R3a(ZH~O}Ia`n)piE3*U&TeA)q{R1 zOM0S>0N$X<(jBsJnX`x>|2dGIx}~1jMU=VFl~fX#c?Iw>Bg5cPsp89i0xh3YdULwY z2*}yg@o@y7Tm0NwB_aWHm_FUiTSLL2nJ`_tfZC9P_hIgh(ZeZ8OK5n~C6D@hNyF^F z3-6sBqCMXFSD)RfvN#6}>*xv!%fP3Hs79vshvvS8Jc*fuu6juE^9NF|GXe$Q@5$rF zpl?Ym3fr-5c3T|D8vs8dsVvV;8oP$xeJr3ZCOk6r0%=zqXY8+YG~m7mk$ar!lvPmk zw_lzw4+2P$&@__C-nsO|m7&37cpi(7MGz9^4s&BO%kkPp2BGEroKcQ_PWjk@h9_+g zt0S8g`4g3kx;HfKgpWwST1U-NQJy;B*_5nOqoc(>e%? z2?B^tmf7Pj+YFvX=zXeKooE%KW+(FzK%8bA(u;@n-99|d3JVi`^a)!ReKCM~vlBFY zD;I?h4i4_68^mFvq2#OCPoI@GW9-DL*-)P$%7>52cyr#3fmVSK{;un8yQ4-aEv{Ez44 z5vgpv5a{L+05;QL`rqwid%yWMHil4Ea|`A0=yDG?bW%k%goca$*!!rwY{Kir0VDuT z0sBhOu9l_9%mA@A!;5kqp}QUYN{OFMPV61LyMLw40-wB9{Ut-{|!Lk*Xi(ADDb-dyTXgw>4({FAOFYT6j#b~+Pk8uj0obN)T?PxOh9 z-uiX1X3l8P!K{KVX~7IkD`CG5l3ZNh5GEf`a&c^FE)&M$IcACqc4zb|?d{7N6AnCp zIg~G3E^90WhpFCeT}Gfe_q_6~cI_GsR`%-i(PW*|Of)p!R&e?1`OkapxM#;$z5Vi> z&^GS5BTGuGvQs|^V7*n=6IiLSKR#3LYtC8J7}v*W_kMEBy2e52e zEjaxPlp!WZ0TSk+(O3PS57!Eq?u^s-WlER35IVS(oMgbquT=i;4+B~xO(DOnDM+o< zBM}nJVH9~uSd+?zJQh!3R$mkiR1ss0UtvdIDY)C6W@mz=6q2KNQlO%( zqCzd__V@DB=~>qVb6+m4eyaaY^Tyk2Ht%O9JCjX@v&A%)9;fJ{nkd4IC0jy&a|6Pr zm5lp$5v}8#r3+|)-aA#P+9=PQZzCSeu7NXD4VFJh@TSc&7FMrCWAsl5l~vGVBPlA- z2^19|p*n>KHikG%hrtbltVuBhJATwWIlRv3p9>tAE&qMV_7c&0Z_TIKd5N(pv^2%66SWT19G%bh48!{vl4 z-D0HxcQSqX_wuVtM%7h02g?kVW7PP^KJ`CcLB{AR{@Vf$)QN}9{eSO<)^#whV1I_i zMXzU0Z?@V=hO2>37qfX}?z_v+2VZQjw{%ZaB)bSll6S-&RObM3uPxCR=Q{4Ck4P4S zGNuI?Mw%MS(+D8q-w-n8Ba1;60@u!e>S;9SyELP^o?wZnG&4<%po#q@j_35D>0A&* zIAu=iD$@OO5xL}l#&jGNgv*Pd6=CFKp+&p)L?L{xTjGF2>06>UkDtvOemO0YNYzwo z)fFI7`c{N=GMzrI6A}*oszBZ);0S-cqLhG)?E)}5whxqx_RX=*DGCh{2bQ;@OP!>n477U0FP+m1_Fv5>V64$HbEM{V^6tH zFNI>(=RO~!O(OG;Hb1;`TwnNzQ>L(>b+1hEls@9!?cfuhOMcn*RsuNWrVRQzV^p%smCj;shtCB-35`VjPT*$WBFjE1K}eL5AU9%WyiHZ z#NjIN67YNtvBL!HEMDx|o9YT}e%10I3H|oRp@J=XC)NnFa)6LTOg?}+uei~LvxSAla#bjOrJqusr~U3J zyHWpZvbj8`t3rkjbE<;s%u7XPHCggT8Rzfig)uq4bCyyk{zfV61`xrFr~F_kS$`b` zaPs5o2aBxv2loivpcL`AR|hfHYw6~GmoJAF*l9ZcE>SmPd{Y0a7ah@uOd&T{A_F2} z-WNk9S9`H&ax?!;I>V=iZ@NQhjx934NgtoJdq;S_T1vc~R7oy4#bDA?JV;@`0v@4DC|<%ZABg&6 zL9P$6R6RekKWGNV=bl$|CXs3j{+1sGn z{iUNbVgdr`i@4VcqD0*1Rj-pw({L)XbvLxEhsno3)$g}2x;j#4)r8T#l(sYcIvnb= zbpu$uRgmPl`|cg;^Q!o<$h`RVZUv6cON_^bMn5}j*zuoo9(jHaxJ~7@Lh~2r z1t%_fd;sQNFXg?~Xv%UYe_+pYNlQNF;g0Yw@PZw-MIen+&Aoy^r4vD{IzK zW)1UaU5dlQE=}>sf_&|HoSOdO@I0W&-*c5V}|Nn#F<8>2JwK+jg^V(v9X~9pmP9P+yXf6C$z*+3RJ(y&vC9CGIxCaXfy1EuERTx^$ z%0nl(rvAW9kn4~P8$WwOFar)49tS-U==aiSjsX5q;l}ECr!oq}2YE-bsWDHZU=W-Gai@YwPr)d1HtV+1X<7 z)SF0P5}Y)$5>OKnXn)kgB^i2RH>dT?+z6gtJ7V~rPyAZSWHSf4^?PevqY1luVA=UF zL8}(t93c8O&PEBsh2t1CrSZUqE0uArXITOO|aIWqJs zP($RvfofK~kt2f;fo~R}!XJd3&TqMa`UTvT4Q^DHOF=XP@`q|yR!isos{ z{F>sUofM5<1_UN}7iHW%gJ_lasQ|-1m&PL^d1g_~L-c>s?v91F9;|=CYL0%JeQ72i zDoX$2n@zU0Lwl$RCn>iOT`*9(f3f$`V%nriKoue+c!as4Lk3rMVWPy1lZVW|B14MD zX8B(+9o_H-Atv$y+zAXPDT#l5a2Ra-p$Hwc?EZ{$xI(o~)mI^K5>*gBmWOf)fDk_m z4s*}@Kk`szvJv*a_aq_Cwcw>ov5&5fsZcsrec_j{B(_0d{T>X_l*sh(|lGZePf8>#W2M3`#R|tvDFQqYwAs$$NBxF-1quy zf~1h1U$hvJAILhbCLWv))SJ@F!W@A4y{ZXz2kddB7|0H(h&)UvO1{c~ZTKCiG;Eys4Xm9j0I=c|F?IrJImF#9Jb`v+wCtOzz3%E$({rGYDXbu4c z0VYYW%4&{V>9q9ah*ALR%UfGmSiZek`tXKL(nbhlr{X?~d1zb<+Ec}8S=dLW!zMp1 z7-D%pXqAaO`6Iv}q0@%TQ2l7JlZMz%!+WMBUIs03oTb@=TK|zpo~@47AxT^+tyNd99q|cihw%)p z(s5LCbOCAg{Rx#kzz0DJwcEYZ7p*xN^i3uRPt3+i*DdGINRl4R4H)!cMgi)f3cx+}Psm{<+^n-YrPmqSXBF@KoC zomK|u;{ZATJ@b_^XB3Q=RUc;3i#hO38Q~y}45_qwRU!1Ut7aVkD)+YHPz3=`O=;wT z$akpBF;D#8iE%=VFu)MAxa0F9%i+>3 z!{ToW*y_3yRF^subgXacSzQ007NGyKvdk#&+hGGNO$9NXK4>Ga#!jx7ceeK#;fe_E zRxD!7y0Z!u;q*Bw3llR)wGev2MdSD3Q;1xv0T-%<2ZRHR30ce4XG^aPZg4UL6fYP+ zEY5pu{w6C#_U(V>n1~OxUv1hC9z2PB@2zT0Jxvf)kDrgxBpM?ZvxufL%Ea*`I`#80 zdDdfb*#Vh*2*|L-Gjy)rtz(QN%CZz3$p|tks6q`*)rJL(vo=CnBSL=aQ7$O+JFRkD zRHQeZjo{hE9r{_frDuosxVAd%tqIfSS}VlP23?`B8SOwE$1LMDb|l9Nxys^%aBqpE zu)}xN$Z?N)+b`SK?w#)l_1SX+V(`nSFx_zs1=&7g4){jzi7xlIBA8^+GKNGuefQ3| z*x?^PAvhFGwm6_{s~WhKZsPEFR0@d%-FLYHd*%3QqD7FKRw$i!o2W|>r0FBnMidc2 zCvl`qhB>PaP3*Uo&(6`9o=C90q&6!dIV=s^n`jqhsVX)z3Cne>q&@F596H2=Xn%rs z1+)w7bj}8b4RByb#ec4~y0^dmDG|l%1L7 z{4)jnVf(ZHM-FolQDQh+__|{!VPw-nnVX;Oba4LVs%1Zk74hv1Ho=p%sK86sWk&Dfa}ufW|RH zEC^*$Lvo$ZEAGc_z}qwS#SPkpNxUr?4@l5k2zwppZUxDiW`JCaI@F{hQ6C#lg!{V~ zp>GAxb%!bz-yrNS89sZobXm-$fv^%(PtCk0LvHF}?7w!F-Bk?c`{!H?MO@fCRk;|B zhq$$Dz=CHJT)FQFjvm;MrLt_v8I$Dd-*m>V?AN9sbu zU2u30U*fz_HI8`MFRNzTjNyIXUtA81y@itZcOFlZp2ovH$&}WJ;fm}|Hk@wbeItUMDa1+vW3=xJG6)9K5`KSmjM-J3~$QSR2@I=Y= zWQ>=;#J&JfCj~Q7Y}-4>8P(Wao?HfumS}TRJ1MQXRJTX^{qsc26Dz7A<*U>9#W-D| zgtOsK4+E38=zVsF18{vEheuQASRtOS#Ntot1!g8$pZJD02QFE=`u1bWa`k%u7Cu+3 z=Y1v$e>G}pu`JZ{LK(O>dkATxA!ANkn_TXkLSx^JdZ9rkMd@CYztIf3NdqQYLhM_6 zr z`4uM>w-VoieM&`$x4313lf-nZ86GM^u0qCgZ~fX5#uIL9-ew8-frJ@1mh@6_nf zcw^~@Mnkalq5Wc)B{Avw^6;(klg+2idA8q^c56f~?eV4v4<)S$uyYpi@0wQ13WuEj zH2AIjvSIn5KPB;rPm282u#o)C|`n2iuE18Hg?5Tx9} zf1DAQgtUr^136c5-*B}uho)y?r$woPk1k4g6QS`BdzH-PQ;k1xu*2khP%C^LeX<|wFiQd5*>u-HwA?*7JdLTG4yBM{>+eFyL^Rvqh!QWi zm}E!H6+@oiPrm4baNWwkp=sFVsy7I&Z(}-c6IFko1Q6!7l+t9I?ngWPBZEEB^4a`4 zSUGoFJl{wP!RkW_@J00#E}zIYM=p5ck0yGgEMfBY7-8&VJxb8y>cR!pJinR~{9zGi z!uSZDsfi!vi##;bJeZ56Q&^|fzvxo(&@MZzGM3d;LxzSXD;%e-PBZQ4SHs4{hi)hB zAzeWYrWeFkOBdcf-co{sM;|*_YicsY-_#X7ATv7N<~NS!uyISOounzVJ$Y`+#Nb6# z=5jKZK82zAa2hLlF0YJ-ZCuilw3(AA<1z3hy>Fv9U7`3Q9Ru#@X$7rRA%>mhg~vM6 z-XspUzbE6~Dw!8IiV33I+~mxG-AIaEyPda4*h^PV&P$u~nz+S=b(CUY#4-g^s{-p@ z0c5~eSokN{)nZ9dM9|p~8X}Aptb*WJeri%>Lxym(f(sML`Jw_$Bn$$}f^qibQw@I6 zv$7&2$n&v(o6Kq#vCS0xO`jA0|PM|GVm^lh&9RB^C@ZWq-pZ%;Q6Q!c z0^ICyN+RTr*;g2|YUsCZ7o0@GhT0cW71nueQKr7lm&C>UylU9huxQ)8sxqKamy)?R zx|0&aC@V+!#B18a!lCToO9|PBbwXd`Lw{|r@&RqS-^OL(fDoAa*u#rb5Fp=fZ~>VA zSwD}0Sg`&JZ457kk}T!)agYJOFX>ggaOs08965_vojS)8aUnJ@rBX=QR&G*w7XPN< zG9uSCREFeCJJQZd2o7GgD4Wlap(U^n15{CBKH^*b#gNIXFISrAgwR0E8#$~0jyMnP z$-XsrXUZrR8bIv-;74|;n?5i3IlPjdv&aT}q!(!(>E3&(E-2wOwrI|X;3#r?t&x}v zeX`;E0+htM<9S8FMN1zYT98lCnTblil$xw)#=ng6W-uG^-S(XurOu_) zVg8zmuZJbg!k?hyYEBva%l6y-y6|Jp$)Wm0sMQzEU(J{ZB_c#H7l{nnvi%Z=DqIdANE8veU(2S+ zFrXZy7N7KVJDe|nlo$jt3CpwQV*qKbcj=D7@}38kyKGrH9}(mA3rY#U@$8nPAg@Er z{eLdQFz`dh-~Z~9R9AMr8%hTpj$3ZIuTR`p*?#=35 z)o$dZ@Bg!-#H_uB6WSs4pNA1?FGA^le)FHw(8-p|Tv|6ep=>l=M5GmBA=*|RrTlxt zy#}F|%F@#o1SbD2X9$~{^oDx?#sfrR9R>Dmt-|(n`x+Lxx=zmeS_dhNC&{E04SPxs z{|=RR^I6*FrmPd!MJeUyQNX^;>pQc=8RMU%%axzsR~^Kv4Yt#47JdOAbV9W`0=BJU z+EC>_Ig8KqSDWmJKRl2<7ld=mQSa4fVw3=l7d9!lMCN0$0;x8kwQnF&CIHH=u9}FC z`q#xoX-Fr*jSUG-ycYP$CLf-yY%6mvieB_$hZnz=3_W@9&z~?a7?EN(zVP=EJ$!AR z`IjQ__BMgzWIr)bw-3q@NxJyJ(K^87lZI)(pk>2qv3S5Iz;WP8#ituHA#E`g?kOjS zSD%A*1oU+wUO}{e19{f-M!b-#KwpK&FEz?;oQUv0a9F7pbK~&=B9Oqo_ViThS(EXK zhh!@T?LXM>#bjwwoE#@o;Fyy@hVB5tfhpxqdkt0K3C|+>efm|!lM6A4cBeoUn}myr z=l@+JQ#`b@Er?C<)N_c;PLXvcowq!Ikk^pF^C9TuWTbyXi+>I`F^3Ne}(<-0C zwgVW*)D}mf?D_>1)DNa&!vo=^@+93f zqgvrf#oeJF5ETn}cH1ANDJ0|s&|6~8LDY9K6BIu*OMJG*H5z_NSV$vMs{k5b0uvsc>-g2$k)O!EAW3J#_I+rri;he@j*6f7ugvdCxOvd4msmb^v1mu zXw;LQ#5_mdpdQuIQrGWGVciiiu!(oIjt)A=ioK%Ee(lJPpKI=X!x6XD!020G^0n7u zib(s8MNKsC*Hh_znZqvvssA5OXB`&R7jOMD!_YlQBi$X+9nz)J-60LqH6RK|HzK8i zfTT!D4P6R~bT=X(B@8|9@ps>QdH9P#fa6d5JFvfrf?nks; zAZm0?!iH;eQkDb*Kq<#P{>rKaWqC6di)Q#!Xr;o+4q@J|TL+isv!i0-j0q?6#H;2% z9nWi*I?TVkLV)6_mT(U^4KjWe+O9j~Bxvy{1+ZvQ+E5ja!3>8QQ?<5Zt}J7Yu?$>j z=&ovBM@j_Pv#oDbonU^Vu!`mL0*{SDfnPadd8+I!4(DLuYuK@mduood!(3C%pf33| zBKgNp|3gw1>4yNr$_NIK8H1>TLVR6tns&w(0HlOyjG-SEcO;!rbRDVst{iWe&Gq0=zKZGjxI_YvG}~d9pXn8vps=i#z4Vn%JbI!&l0Oy*3ffq!dLi^@y)SaTsk!NK z_>kb;KUtKf0c**qc}AouWxqQ+sa5CMSS4UK;uX(g2xVCL%x8WWdToBzQh^7QYoz+^ zpymfEW52saQbUnNA1jWdz76?c4cylo8IkiOA)Q(KdN~FANP?hvp!JT8y5n>#oXbJ7 z8il>}YzzG~dY9mLton7+VpbM85H+6jF+n>sQ*h}f7}RqLDZ(CU0>L+=xT$@;=`g`d zXLWF-TkH@&GG0N4%7mVgSE=NFva!O*uCx0mQDVd?^&qrAA!l#c2Y zwgz>CoA=1VtK*b|#u1MR3Df{yltoTXd&`0o+L6k^s^ebfv^^H&>kIB9avv|G+2@KcJW0Uc{piCVLN}`1l_*AHb>=XARIMRBOXCx zRG?KVCZx9ZH2uv6?#wWNTTQ_TV-pMLj+=rsjitBwofC1q&-r+;C1IX|`u@_Bhh1I| zkeLWID)Wnc%A7Zfv;1reuqrM69VjZ<&05Kb7h>DpgSXbGqwDxSZHmt`@w$63>cjg+EW?sS)H^MIT!TB)be%MPEXoP1mV7>Gu^QwEW=zpHVu)N8MIupm(3ae@PR zFo}-nqKF&tTD1Z%JN6zWP*Om!WdMFAb-YCzQZN;>qpK;@(KguBG3yJhV$B3pa=Ex5 z6wKf%ktn9Um{G#56V(RofZ<;COg|_I$qBXUwvV&E56KY~u_6>W_!L<7&aDEAs)vm6 z;EC(6jE>f|+D8NIe1mQs+Z!JpQ8b*r2@)wHd*-`q->Mnx9Ty1&WZX%oWJq_n;c{qu50o7NX9-iwk$kuECwIh`|4@UUFLj2O znphGuv48uGC@w6#@-C}#$>yNrT0_kH%vYrb$=1&`EKMi#XwF5ASG1H3b8YS zmaLqpLP?naXpgVeqF1|CD^V**P4GYeHP=<+6{7A1M%c!0>7c#WIa&qrI6|=)eb_N< z?rXrKR6qz0ZL$E`yn*@l20EfT|3JR#rD@NpS#t3#1+As#H#~gIpN3f>Mz^jAr|C@yk73wgV@XEnE-|QL}c299{(eE zfGP*;nCc?p1jSKe8{Z{Q7wuHP%{uTvPY@V7sgK0sAH{Na0$T=8$;W7dHf+N+F9Vgp z@h$i95aL>PHaF*ZbUAV-O!HzV_GgBa zGfl>~Sq|Iuoj)HGKY=GPB6?L^z~Tce^<dU=w>%-N0kIa9L8C=^h&F9XfWF ze=ju+bW)eZu{C%u{ZB-|V#MMD*qsrNDA*q1EloU+_~b)F;J>;tRky)<8phSouD=j0 zTXx8%8m7nOJxtQA1a_2g;Pba_E%sUeNj>P1X?SQ^^D#6pf$BJ4ihS)fPXj33QQ^Se zq-FGf9}u~*XhKQ-6Pca%u@v~8CC@>fwxed~c^G#2!gv|I{1`EHJ5o<;{qaGoNJT-N zG3Ag9L6f34!7&>7GZ9pMo=G4j*?>?g6stl=X}c&FBrNSmvpOR3W^HBdjX--&`*{_N zQ3EQYu7x=fU8}v)gN?GnOdWJ$R)J2tDVZ?WD6>QC@UR)*!qj)ly@8C(dG^ufpHzCq zh=xh?PcONK|Io_f=e$n5h#0r0Jrw1t+ruL!xXO~0-Cr;fA>t+KF^i|(}C1ZHykcc`4Fdxwu3K@xwA}8ZC6eH&rxO|E;+- z$QiV8`HivNNvGct>-~GDJxTh;kS^bJA4rf9+o-b7Nl7~|&g^^Ngwb|h+*A;PRog^r z-lLkNMQ~Thjkr8~f2nrw;NFz;BVEzX&*9vRQ|pj9es{W=kHyvVqT*lY!iXEFi=^J( zH~v7?-1>N^9`zf`+&6eX$u<1oWJEhwW&CI4FviYCi-l$Kv+))1(zb;|d}>BR_)T6U ziK0cZrdbvP-C#(=_M(%v9af?5Vm2&Q3-a%vn#c?hV^c_h365=yub@#ux1}i=LV8ST zfEymRNvb+RG6N9MT|o|_y08*5Sp|ZxqD0|PHn>ePm(c2S@EoL~uYJFX9a29+tLhM+ z#Lop;^%dU%sEF{s-!JAmFTp(V-}D=DTsKKHoCNqj6brFmsK#Z*Xj26}$&}$WBJ6?7 z_A8a`RsS)k+R3^h?fEQ_gl5rT>l)C({PAUndq@B*k*6zW^v-N-EbF9 zi;VuGyMeYLtN=5KjJTs+j@@U(DXvh^eH+$3GB(bwdLG*?9F93Ya4k5Eq|PZt^9O^b zf-w5pHRr<1Yk%fdQS~EKYWM&nCKmYRy)5WHr-c1y&yG?!^Gatv%5vwa-rH)Xcto~Y zv0)(Jt=Ij)x7@`KZSIM-nq+A)5YnZ+5`}lZ^-F}_Uz6io?>`C3vviqhHv0 z8XWkjB#dFwBDT44@>#TXGiz?UJtNotgb!J?tiiWBYTj66Cm>GyBHc@ zdYB;)G`>^R*0tz;GFqWbm4|X)$ar9x^1WPv(cMj43rF(XH)b_(YIK)*3P-HS6W}Vi zqybp+A=lO$-;B`@*s%Iux#6`~<@p98jq4GX&owaak;IHxKMzzY&^1*CwVv?ut zmr1(g+er+4Y+0~gsdY24;eWNo@kRtv@Q0>FoS%{pI2?8bN_6+yMZaN}uqZuDKoiNx zd;||Py=E!Hm*#AD8K2PAeUY5bME_xhyD|5U3dkjSdcSBI(8v53#m3>NdGJH2*UF9h zM`@zpK-j3*s_{hyNozi*mWwW!YYe;GYWA5%LkI z)~f|GclDeQC%jycDj+af&-a7MC=^o%-e_I?xLx6~RATM}#vaD6#yJ2j_y82d{h=R9NE!d8<{6)$2xz4W4$Lbne`Orv&YBetU96UQlYa zF>&fsgjOcRP@CB+F-gvfYL4HNK5VuM$Hk&@aUm^VFLgj#6iMhlnV{$Q&dQ1)ihb)6 z`%xa(w#}Hqv?}a!C^`~@;iUFCVo#4x3C7O)x=Vzp>_4v!8{aqZC@hSR6o>nG#e=q<|mp4k3h3mc|UHU))H%(U3nx= zLZzP=5--pbkquXrpN3xz}wVGaW($AzBS5 zG5WejljanJri=L7vv4Hn=#L;*j|c2kOroffbS(6+o-dx72qh?Mfj9x_^kR|9^zIM2 z8V0cnTWZyKRY;SICvo8eP0H9qe-@f`k7_N5+xHm&`x8Ijls8TBAl)OvrKh0I zLn4uzc{hh`wh#L7!j2KoZbp6Bd^; zYMe^_YrI}m!DGnIUh+#8SR%zZDaUdy-B_dnv8g3W9i`S-WW6bVANEU0SbYE6@J!%U z!ib?k(qlWd_H^HU2TtworI&>?R6)LvLUG7KSQXsn1lb|agpuF_<@g@$3kN{#?dS}R z3$9wE3al<#0|A=cpl7WDCHdyP%^&f1T{;(}-N0{L6MF@mm^lFdVN4w#DHS341Y@)@ zRGk%IqCRv?WtqncSQ~TRl^?xoJyUY!d4dxYvcJ%SIni0&wLUuZ$CBsx<2WCSmHR2- zVVi~AQB(PlwWPw{-quFG9LM{w_I;OAeDP5$!$Kt+6SY0KD!1B;C-M!9vBOUb9Vb`? zkM|84Tm9)kKuJrGtXu6fpNIp2HtWqcC6<^L`WBX-*b7SZH{|UaT`v&ENA6)n1KTTw zI;Q9XzubJic=T5}Q2o3u{n%KF<|FuTW>e^ixRgkk<^~2z41v6-GOi5%0x%@rW!KW3 zStUR>Z>lDImu3%?-Eb$FjeOio>=cqRph1dl!~??AW&w((CU zS!GkJ23IV&R`sO92AoM@bo!kC-3^o%XQ0fQ@EfwWmNJM6M0nG3j{P`H-uMDY$wkpa zkkFZyx^TY;EpkNnUq_m*gZC=`pIQJ+0`p>moV4vaZys^w`aj}F?|gM4Gi+Kb9h)r_ zLBX>SRn7crv}bCt=j}w&{4-%N!5IyG0AattlNb+bpvyrVvw z&_C9ZQQ^H|TEOu6ENpagQyMrU)mJk$`H-LM`4r}f*886#Pl<0I{dk!msdV>d({oUb<(jrgd&{h{nrm#n0drRfmn=1CtSq>peYslh5x+ACq=_P_@fc| z(cSYu%S?pWA7o}|*a#dHe>|g6bCs0Ok-3ZcdkevR$3fy=0&liW%Vw*ut0NK&_!)C^ zlfy$tz^;32zFZ%*2@gT$dof ziRZObz$Pfgl+aJX`Zjy~aE{$S$)`$_mowm0efm{^dL^LG4*&XR@%CX%soBdRy3kq< zIlk!P=wwF;@$GYqeP3&mNHaplwLYKfde%19(ayS8Q;xI9=5?y$qXGxQzm$_xgW2wB zIX0{iFyDYftPEA(=z0j9y)nJp%K(gUQka<&PO7^DyRIlNj-}C1Qn^?RUS>LlGysUW z=}~Lh3#0yQ%*>lPgy>h{)k;UzW=;WdChGx*&(xutq^V*XKQJ-K2-Y)wq3&-1Q8=!? zfT&CVS#oUDxsb2>bcYr7JPi|Ik!pZ(k!u}gUQ358yksoBx|)9lc-{Z}#=gM-lUg^E zBPHR*kOX_$Xlagz`4BAV!ER{Xl;abOSsA7@TNnNhhtZ7)CY#GiJ0_dx{$Z+o_mP{! zulGUiW|ef2{LjWhym~4Kg2WcxfRP<$F@ebc69%rz(_e`Q5q;K@Bii=^VAMZ}ZP9|- zvRIO3zQef0QaPv>?@&un{HhOKf2DhbM z2P<(f45fv0f!>+STc)$`O^t3sip+rk2e3$rJwYNbJbyV*IhQF>8!Ym)h?Oej>lh(& z%EK|fCcAm{`6tU9dzJgzUz&N(R?nD64|Zj+4X0u$?QK>cgD21L16}M8UJJ)WIvz+m zPuBm3Lk@Bx6N}nW0=8dqd2nX>H2;a9T1*fM5^z~!Tjbf`j8GFwB@={%Gn}zRC5nfQ z4Pc6{_bL2BF@j~70s{nn(qC&+@h+aazqem1$WwwI?QkZiZ>$>WN~iVqDrF89*qNFp5K+Z6D|v1V&3e}_}w{4&knC;%3j`W+N`bR zLt0TbL*Nmo$}ct^EiufF{$sWr$#uITzCgtS>d{!N%#G>+^JJ4)CQygYi(9Jc$2bZX zhK6yX(yz^5Mh1rM&pFEotxj!(ylictEX6RHk2MOXusv<_mHetWH4`6C7`l&t`8MnM z)A?RJfKO>g(9=!1(9VPBVw2e~JOjG=!9@t>nc+q^B6~rohbWnWhF?b*yYNukXs!RK zOiVu5v2Wxk)PUX|UFKrg*>KS5A2UdKDEjkfurQ&2#r-CSUsyOl)#2UrAjAa43>|N2 zxD5jqqXw{>l!-eU6pR$Z1(%H^kQF&&b-{9T*@mVWThyq;&_}kv1Y^37L5!zm6(mIB za@egyo-1%Q$$yQ(76R`kHpLyK`J~#Dt^xFYyTBHMGx{&g-Fs&GX1kn(Vv+RGWtZ$g z5TgKT3)52L1BsuP6{2omEDHw*d=eCLVt~mzVUAU@jdPA1IPZv>PZaG7tfAA=&ZpSJ5=;WFrmxvoMKI(_8|{QHqV>IAw6{ao!%_xHF7H zOkLlb-VfUD3@p7Ee=!K($$g=_WNab$S)t|Tw~_GO0qJ0=L3J^}dPu~u@T&jqXs<2! zp_&hxWJMUN#v9?Nvmv@;?nxg0tAF5UbQod+UOu)0{cRN>F0<|3H_jEMbGW1i7C6G~CF9`vGTWp-?>--~ zbRl5{Y!h~}=@;YfT1I)WUHtUsCnvn8H=b5_+~cUwNEpACFh=Oo1#!_8&1hg5B>xak zWs0q;uN38~57Fl6FmE8svYW6FkarU>!e>P0ctv~j#PAUyGAL+BzGiBs@Enbf8wJ@5 zQpf(>jp6Zo5np;M0RcyO;`4ZPV(cl=GFwVlApLYp0+_e@gr4|!Go`(pV9!vM?{hvU8In44m+24?OO{q&kD#DiCaL2)NakBc zlL$LU&u=l_0~}A@55GBpK7@S#c2oN{x6o+<%l+}$M<=s&PIe`!$>rhG&Bo9H;haHh z=+pfi>%>AR@?N)Xo*1(KN<8xDnOb~%D9CE_7W6EQbwNNb@gVo75xjzg;)v=B1~UmFxtiQ zbVj0;+ohJc?C0{bfKeqA3|rio7dRqzV}v9Vh8tk$ah5=Zkv2~31qPMFYgvGrNa5R~ z0JkUhQB)ga%ca`bKqKXfLBX^47=~L%t?3I{OO*x?1N*?fB4o$)564NM=dW{!QHPx%!M1s-rLWhLM4=H`pO$CnqUF!?%2n_?oH=#P+cw?^v&E{Bo95!IUnQZ?2hG8RqkOsxn;z9vgAp8o!fX?_+?3MX z9pLo?4b~X0jSf-d30LWbK9HeRu>hieRdLgMAb~Izz$DGy4?GzVUi&spj~E%J+3OAb zemu?hyvi9*bicJXLDaqpRpd`@*1F2AKK(*=Jx7B2Rfxj3YCF#{oxbLdyo27MdzE54~5@{vM@eoK{KHWoxk@eIO`RyKDRgFt*K_&V)JIK3RF6UnLQ?Ts=TN8 zojHGGrzO&Q-Ns~EAcATtQDynwIf{zs6+C_xw7P*@RH zKUUWNO!1f~=DK#bWAO^H!Vwa5j5hH+%c19%?dM-DyOA9KY*`zk7-VfU4Cj;XIVIN? z%!Z*}Q4?Gk!g zV^Km9++|4IF74jZhpCG>FDN^PHmGTb2Kn_k+-HxBj(y&UTwmq`$BIvNV2{Tc`9}o`33baT5|huIH$D{2i`dSI`$BR+ zd3M{?-yvAG$RDc z>`u@cx=5ge<(0zA(|krhgl38S7|P`w#mTx?f>rkAR@^-pV^b_^&MNx1%W=`dBIbc1UwF_Gb22GZjVaVMY~gUu zZnCr{;bu_rCPCzG9q%0I*nwpeiM_S5%FU(N!Ny2>)NUz=BKRH2joy`IUX0TcJM$&_ z)J;>q+h>nQe>|uDZ=*s68;I$dcVOH24V}v*U)x4dGJjk06^L53J&&yTD#tOJg9mwP zk&44qa}k!T6#@MWH6Bg+9%-qU%jMeI;yR(afwJH-Dfqu|J-7!VN><3vXT6 zt!owfuQ3t zaX2oC^H;Y%|9mpsI^NuzaZ*mZ2ZiYs{&)pi#l#Nqy88sNwaQIoyPcV z-GekB-93?IX0MF7=Wt8 zSVwgB^x8x}f1@k$nkPR|M+MN0BlGS7@kA5+L%Mu$d4}%_{D-fLa&EOy{$O4hu1qQj zet9#^V(imy)M<87zN^14|6x%qeU7L8^sn7?dqXynGOlxoVdtbJj{ zFYzel!z2F5!?rb!nNQwC4Icb$j?o>9%VTj*4d@eP=*?OacFK}88G@6a3np_oJU5^z z7BkM&fS#wnk)!p+5&LC6q8m=9Jc23}`eqgCN^9<47?!1?z#|#c9yqx|YH4tq8`kMA zFoc3kaRUnxAK~)qrbIggEi#oG;K;)UVkAiNjZI;=4L)`%F4PT<*t$rEwd55J>O~xZ zP|0U6Mg)k3$R&u-+YI=dh~TE4ciinCu2qK$WMBD7AmKQtpy|r=AQKS!a8X(Ui^Ea* zh_6+Y4@n%#Pzr8U;7Q&LNBrtih!c8MuW}yjQ#${|nwZG=-a zF^$1Osus?~i>41_gmrHs<8F!KNjQ}qV5y*=mtK&X^Ji4(Zd$`jzWclCJr#ZG=pYSM zN?Z!8!c>N5nm4H7apru844v-4DjvP@wB3pE@O$Ft{bBh37Y@=IzSG@q(7G8^1TOgl zCn9%my{K=cv=u!6g|(=DAvcd5Wxb5=EvQIoiyT_z|8<{nG*28x`xeryn4NK94~@fF zlYeMJaCJ!jDs_ckLd-@m-g+O-chH^8%v1I;c#SkYh}F(^Gg)&PZw>BwHNsBBXqd8} z=*ou?Q~@n^h`cvUk2jl9nBr9d@H4|onjgxkaGZZ)sxc|m%zM{yMe~SmbY@jpoMZ8B zl=ZOek$a=gH_;Ic!{71H&$2FeKQdtnN-yWWD)^Hb{{&a%)0Vb}BCb+~siC`+62)`yrk90j+vgTs@FV=D! z%C{nNl?^I_D~J!b;bR4aaZ?@9;LlmeZRMkOsbBMgrc%V0*|~cj)OR~#2&h`&+PLj? zl;2VfTV!bwYO?iosu*$pN`-Xhu|n0`jYw>vY#d0!6R7$SuHlwWDuzrf&E^-kWdU?O1lua)lv>AShH4@IB)+?7sqhcCnRs>@R*}<=g_Ea_D;^O^D)8!6q3CTKhCPu zG`zogD*fw^6$N8Kce@_VIjh{BiGoqG$k_0V<=I34EFrY&9!W69AX-}TN2;S4pwcL% zBmr&caHX{fY}~b}7_45Gm3;2NUF`YG;&qPuljZa6W+hGT-@p%9Rz)UUg5_o55lL+Y zPWq73ge6MR$O?ZG#1oDoeS+2k+iXY-G>sG`WgV4*~75-hX%C=U17;}q8lGavP0JMe4)JBP)1TP z$_i694NuL1fXzK=uV&=UlpMC@3wXaQcl0s5K8a%%D&P&@qpy&(6}*)EyN+z6S*5eC z)^a5zd~vTX^svkU%DheWc+^}uypS&<$R3Ts_gJ!u;Ojsn zlQqu3iI5#LWL3ON^N=H>1kK^*Q%_&Ei^s9CCb%{)6YKmdYxA<%9K`8mb$@2|0KQ;t z8+e9lPT5)ExXaKfQ}+JdHsE;;{^dvn5HIxdiap;7t8Nn@!2_(|ovz8hO1|`%&WjWW zrdox`MR~9UY-@(ow+M|*ehnke->uUn-N*{);eFJgJz2RD{E>`G=>L<8Nan-b#Q1et^)f>pX`s_M3nCHIuCRZv@U_OM_NL#coW+F`t z^Q!-Q*0{$L*yL&|5Fvm@gqtXkGZ%A^N5c@`HX3!3vtr}t=W9OyU=dcT>C6eE<8XN< zRsQad&f_tj36U)#Dn%>YM{B6GSM+ zB(7iXu3^lRP<+h z_o-;(j+K?xO02Hb+t-}@8O?zX^e5qLGZN0JsQD*%xi%9yJ?wfW#d*&v(w+AgbwDH2 znw6go;!su33UO$cuiL_wP=l%*NkRN|5f8*%;KKgMWMW*jYxPcISJw%t1o zU|V8QX+Ds9oF7RYd84EQtpcV+sc+m^tD@@q;<9l1SQQ9bc{E*7=kX9i8#~*|_rfyG z65%~fc(T_=e$uK$Y>d!26HB)+2aZ0rq)|KK2SY`bE!zW!#HR zXUH@#U~(~xq|HgcET}=c-pHY>2VFZdHtzWHb!?%_I!7EuGU(-AbKUD^y_=l8T7la` z>hsNMT$U>4*N%D}ZC-T%SKT-&@&hN?bl{v-?^6Q~=<0BpQ&exoMR zM#bo?tr>Vcw4G#vklA?8YTndntAy)KQ1mT-vYb4QzVS8+x=!*vd5Zl&gn3^f#g~JU z`A@>m<5ExaC^pzC^N`OIBTcdVLcjO{!b>kT*fae^*X-TX#)hbsbPM$+)e)r@-HA{E zYqluH#sX|b&bF5DJNG6q?A8m1l0=a-VRt5wB%sX~5bO$%LN9&IT z>=R(Ko1dn=43@MVz@~AVq*|t7i)$abC6UG zohlJzhE_EBRfKJI3Ny-bROrq!#siWu-n(WdstTd45O>?`JlL;vh!E{|z__HEPAM2} z;a;;dkHAE3I=j%7?vS3`480@yPS2d*^C56gDX0!EW08(t0Fh!>Q+X4UAf(x1xA`sD zD8mKl=Gr7_twE-`I#69~w|XeMFGaEca!7*S1g<}!3Z{ltZ-LyHC1bkn`8NL5l2|Lc z4~7%5Hnh4geLv68mB&6SE@W)TT&j+bAz)c`qxMN;#Z0D~{6#{wE?SpiTE1>sgVH_b zqat;dT7wX}w`o_r-;z}sgr7`1Xs;LqBd$%=Z|gRe89k$S?9Dz1pqiKWMl_&rw}xDU z??g{Ad^~QCLj2!uqvQ)%7+R{8A>1|H9>eq(A(s5i>5OWBo1cN}lhC81fiUZ|&_a)= z0XLlwq57J9#%VwWZ31Y`cOTx?2JO5-vx4=#*`i>;84Um1Sjnr!L72vZ209T89T!f= zP(fT0sI3s`skRAAU7txd4y#I^x~X@T^nt3K!d%**yZcD8ht_f#C7MOuwS(t_Xso)2 zhjq%1e;k4pr)k3Nl6=NiORgmiLyAB_tCPkrzrMplqjJq50^pxoceg^MXA-|*M!2!M z#@6>04cm9P9C8*ua;40vB<~k}r=x;!eouEX#{tRE_=Jwo6uQYcm1vbd%C)gQu871I zn+6mAXG?vNC!t=9lzo{nGwZ9Oc}J@2f8a#3qR<=7UzB;H0RPV9*u)20Out@L_qmw7 zS%L3FPV^U>4vOrtGbh2zV*&J^@6Ru1Y?(b$HE@(T<{LsWwJmrL09ykI7h_=G z4@=>zw(1Cn57Vpw`TyV(NoY)l@SP%dI0Bl@wt5=eoro9*C-p&uq}7JQ;f9`71SMIH z+VCj@rHv>lJ`O$v3_4e6!f+W0T$O~=OR>$FHD($&tEAqw zj~oZH;FpIjx54!1+s_uuC)1`N=`zK?<44v`_GeWJtOGlLGFE7u)N-_@w^x zkpQ9J9SunhpQ#Q|{rELW5x5n9J*md~e>MLOG7Z$BI&$MdTEl4xrBA!+Q6D;*=!~;x z=cSoX7Ezo;6D(|LY>!GmroYNmdHk5Ln!7@iznLTR2fYmyA(fcT`vWX@s&dcf&?B#7 zaf}0N8}ZebvBYADSOg18o(rW)!R6Zq2jXQ=GOzA&ab-UX z9vc?&4We$HwhC1fM={)w_|>!vaO^Y&>y`HcX2zkPIa3S5>OEe30A?u@@CTsU!!5tx zs4I!vB^&m)h+eHM(`7#^+WQ$wuRWtJ;NjLEQ?EO3TC;=YqEAliz6Z>1V1@K8hnYbJ zZvqHfrm-JSyL}X#o-p^_J%WKB>qClV0S=^;FjA7#rh*yiH-58saZ-l%AVHWiQ~byS zM6$V|wUY-mhnP0MtE969wJ*i61Zs^Mc>aI(h4D&(fo~& zr*0#k7I2bYTdU@LTHw2V?$4h(6jGyG6IHYKS|NV50P%V)E){-sb&K$pwN-?4O}m&FbGogme8+3XuPfz2UMq5PCHmNph8p2e2GsiT33v=2ST$Z{Y>nhB z$2SDv^I*-Qn#9oW{Fuc@eRkS9z8~QJArJqx_8_k0xAFVQXFu-jg$9k$J_-j+9HXb_ zWu27CIR#NsD2&5cGT?F{n3U0xRN*H??(}O{v_Rzc3Z$4BN#_hUNQp4h_n8y`LQBB# zS|!6B^B*cw8(k^wRbl*Ot~E|1K{xa8B#cuRP#SsU0$zvr++Dq{MX7!!wiGruzxCw|{_<8;1o2A1N{cS@d!uYBLg9kTa@al|0$nY3|^R#k#lT_TJ`dhG^@+bj-zT7cs zY(u8+wbwjMVewA@>&`6O4(gMpk3AmO=;1_+GU4a!`3x2Mubdc!hJE1LG7($o(e-Y5 z(jWOb@pac+!0+~=A@1yE5jQ-AYKVSmJ`rtByM>hh5XS5LJTgk!tkMUa9Ybd&CPHt+ zTu<*teIIIYpK1k_DkIxmyfOoWeO}XH2jiTF8K?E%KNY!0cK{?oNdzI!nZUy0Gy7HAWUJH}7W_A1Ky5|+q2+93>z~3d%`ksNhhZYx-3J zOfEhf4O*!Aob>nf{Mz%bYsqS^O9x-zrpj!su5n@R%zWMIL-Q-;@z%Y3 z1bp8xgg#;H$~~6C?x0j@>R($BXJg+Z@Y>G0hv43i>(JzJ*Ow$7PoTb3IxMJSuia?s ziR3>lpMF&LO1HJRTBrA=LC6~Ug4nf@<8X5a>AYCe8dc~M(Yk&>Jd1kt=KQ6Ac;eDb zyl+V_;Q8{7tit2)w)cB}!2l(w7$1@K7vgw(F0<(ZNpUp}yTYUjqb*#!*-1D@!=cu_ zGlgh3Z<)1RI|>Me#ZCiaBaA_O7fa|H0J9F>PY42Ut{F;Xj#DFtB=g(8{)~f|!X>dE z(FIh8-*B)%Ta=2Da1b_El{PFtL&d@mg4sM+Zalb=6_$~EoGxmFbuRe$^S2oXm#0~E zm@sp+8jrqUNt+7s?xux9-7CI%<_a!=8A`HxYO39kcl$SjM2DF7(ycYYheV(IA@OJj z&P_ipDS-=97c#f_1vNy9gSm4oJc)Z;J-2cW9x=Ge26}% zi{w)+0mGj*tCk8sqUY}R|EhaHIx}1KXy^F*n%I6*`|L=6uW)s4`&&qh6f^v49kmo; zE>P3(%~S2`v)vn?(@T=N9<{9ycj&Kk6U0eRLybK#i$@t`0ovu}b8+VV8?P~FNtrH+ z1%T}7e0Mu~5$+p|lJJ4;r-f?GhHi#?t&|=qx3w?nN!K>!>=(SVTTqi1u=o2^9qrhx zF~Ij@?uW+Pu6!sm)4G(*n_UjHTaL(-wEpsOg_Y?7RCW#z5hq=pIQnvaauF<0nbWkc zm|8hBL-$Xn1VAL&b)Zp2CYAbBc!S+?BR3gRp!oL4%Sg@AEGv6JJa7;g|BYPk-DEb1*_qw@pBIM!Tm(-Wv z{?d)fQlcbj7JMZ=KVNxlNd%L=5L%87dIX=ct-QN;)8-J#cCsu5t;)4AEM0ym zw%*4-^{2}DR8|ZPOaxrIM3|eaCk_QuZJA7VrvWAV*NdC`L#4+4;HRrYAqBA?4|H6* zJ(x6lC{GS?e}t4JW$aNz#z~SRUjYoE>oZ`##GQRm=i}>$M2Qd7er6B&Y+jQ(4hStV zGOvl#hvPE5ZfKs_(W9sErdTYAVZ-%IpT=t$W>$DM8wcd|H^LujnAz370{8<3nNXoV z3Yh!RAj{S^Tm2ffz=mi|z$dh~|Fp zrndru7*hBQ2KXXHJN5f9xLeU$q+P+rL=O_e7puRDS=giblP>rQQ7RSsPS_ zhcA0x^tnrsJcnnn0-t&@^{S4NDqFE>apagLvWvYamK8@2LwAxe(#-Up6<6|i}uO=TXcE# z?sKKzavoar1N_tD_?S81CRRj1uL^T10IGRbACoG4J0wD2GZoLSCE%p$b3-?01vzBS zF7PipDDQx7*;n?jaw&DNCG5KWW!5EDY&68++j}T8ZfMSE*`oO{L`;>n?mi#SPaYf!=qH z5vMdRvBct5K1E9l)_!azhu#zn?t##EOVZ_=p>Y$NFS!=e)D*S7H|x)=?*noYu(B6u zcA~RR4}7$%pDw=5GKDbB|18{+VoX)z5e!mS(^wE9GA{ZvRPoj4gKy& zQesu8y+@VW)zVUXZ?S6CD2f)PXjP5cqeh}AYLBYDYVS>m)KGhU_ zn@6Y*_l5kWa{%a79z@)y@7N|W(CtzPfRG(>KLLeZ=33PAr%ZSui_WPPKu7v_<~Z~h zh$2;a+D<+)AO2gd&4bQSc?o>RjL?bTGuj5hMgpMdBoCY3&<`njiIhstdM|H*-RUl# zmE*o!w@3KQzh_05Pzo(v?F(K%cOAN6pvL^7IM3QG@t5F+jG3;TUB)e|4@Or!uP(5? zgWeyj<7szTtI$Ng8;aYK2mSfrN@JS1+Yt?GQDjlQaysndhf%`o$E0U4X7ovu%aYaX>DCo;-BctO(xPmzAvdV*a?k)l_@zP32H+LX8A-uYv7#8jJOpQiMf z#ll|A?MRp1NbN;SWAJc&VfPy`WQOPDhl43{Xv98?@L!7~e&Yi&p zPhScx%+TvinjJ~2&~*gbLKv< zYrOuc5o6ZyqX2foA`{!CR(H_kkhTio7wRE6e>SX74eY-%SaWfy9Y&vQt?&tapVNbb z_>X6&PfNSUI}ctO+OgM9<(DzdLw}9^xU9Ik3f^4v7Zb6oRw5{-_zjg{sxSvAY|Xm< zAy7yN#Z!WmJ6hQkemn2s^H(BR^}V74t~aI8c=L~?jJ3aNcONTUpWppw zCpZx0Ry6OcTz>u_u1pw^ZSOG7u%*>{zRi@w+31BfDIpA{M zXEdQVo62SwDG^fpvcX;jU?E;mUsAWmJnDK*5OAz(TXg)^(gvO@W#2XU{C2n@8 z>>e|onzK2OwVSX6qbv!~f0GalD}4k|=evDKlGl309=f{s^t7t6Xa}qju_^=?ogzyC zkHi9$ALatOlu~p)nDh6R=^xD!cgRK<+ zPjmxrJ0{O#fzeotnSgiHWi%dljwhs%^_&O{p8?0u;Uu4LR<#r48cKPmcH>7;I=JH> zB`sB&Q-8N08pwFCL<$I5$Z5POrSP%}l|Ca3Yq?YdLup&oZAGxsG6VC1XYNS{ucV{# zS?x)+!!~9Vcw**7A+$Ye!+iE={L*bm8^dn$Kcr|;Rno4gI6p zvG21#`ICMVSC)i7YBM86h}pUBUaICcs07D6%c~tDC;oK2cToL{YHka(pt762>$kl; zC8T_}E}E4&M8}MXgG8|PDP89oq6^Ma`e`AaNG^^%`^nB_!iMK+4gb)t)K1qP?dJ3^ z<&&T$R{M9=T$}w3m>W!I72o+uRoRkV<^VOiY;$p*f$hEtmg$(<3S%Px&oe7Uw_A4Q zwT=`Zff1~cfVbOgLVc&9cBKafnhfk>-;?@&gc$VyCICl!LByR!3+STW1tj!5A&itO zU?t6I!N#FqxP;#J{xV$bJvE`x)?c`$ynnSp4~W0@D;8kr?T;Rh2aqW@2_PX+C){}| z{e>2Jjuh)3x6BsbELv%j_3$@EX1=@5uV(u z`xtz1+U87wubaS2ouj(GT>cXP4y?mzb+sA+GpvAD+I8=`J(Ny&-9}q1aKDS9n8&Bx zCzsywzp(haY_aYqwrJ4}-mL1CGk1FQ{yPW3OWj4Vkqwl_8b>1SOH#ujIuO5}}T zb768F+v3Oo>D4RNrEXtI(&}(BsYJe+!8NCQeBagXn6s(H%?qY=4|8ApvUdzjz2b+GyX>-HUy>vYWX*I+h4^p zsWGntT4no&_e1Xe#C5J#)8eNA0lC#znlv& znhp@A*)2=?`9&BQJ-k(N*A1Y?{;7zUp@=VknFRTQp}CEH<2XZ$SLgZa@$W;7(HtRXdB-N{6N%l4mmiG=MLw-2o}M1?=0p%aCf@On+0d0&MEMl_|&GMKVB+? ze9+d7e^zh84H&a!Cy{Icy1WONwhP@AM^;n0x(+8GpWb3U+1ePt&Zro5E%XV`tnf2t zp(&k_TS`}qpVH&@ea1TM`OvjKxx6sMc($#7pS`&zc!K2JkS$gHn6LTt`*nWq;~&4j ze#{mdNy6s&NXY55NKa)GnJwrW*4yyTB+?qh#J*s78#ppUKRi|M+Bo+45oPQ^Z2@OH z_I{y3uJgCqX9J?sH^(Qct4h!EZ6p@K5s5d~XIaaY>5!J?VfP5<*xua{3s^nxfCJ;p zFLNNsE%1Sm%(*HUAo*%{j`t#$8Rr#@J+$e{83QI6<^s7S#+v8X;zx;E1ZY`2h335BvsMz7HGpGEgqn%B1QZ(Be=eRsPQ#l3Yc?r z^1{)W3zh-#cl2=holZ;yRNCtB@nI-)N&Q~#$=EQD@_Em9eQN?&9o6 z{-%^*n-tA7A?3F8U2?mmpL}<_&D^SjnfdB=OP?Z`FZZ^|DACXR!jKOjl-;DXgs!D! zH4JJEl8XK}H3n=iy$%z>u5Y!z*u@c!t?3E=|HzWo?g^b)?yy(2w2}%8`^XG^Ypki7UY7SoOoX|7SH>!=Ea;TJyB(FWj2ZJ znQ`Qb$WhlwAfVcVs%(ZDJmDqW+H6l~#v^0>Zh|I10>b@7L0>4)>(`AEJZPZJ6*laZjsG%*)>#sE5KQ15ZMl0vVWR#q>FSU#RdKgL z`SX*i~?S z?c?#+;+1sQr~2wVLL#DWBoiOaQ~0vS0qKK-U)P4qHNQ4=Wc@`Z!1 z{2uTo`D3w1!E9A-Rtp*3Z(ZOuR4-TE=2k_dZ}(NdvuSilzU8NajBv3=VI&9_XE{~! zZRVJI`9Qgc1)9M8;M8~`m=b#2ajafz! zS6=tQ#(r)+tI3^z8K#YMNz#xYP?~{FrV#Orr!RMLYinM$=4u5!XFq!eCSP~FOK_s% zCY)$7(Y^}UEbLqrN$02_271T}c|}cSrbY_TQw4Z(qE0cWaQ)DWJ~J?U5ZayPp3b}G z-FccrLG<}wXa3Be#3mF=&jy9X#RIx!O6U>b?sUQl$2lc41L%xJ_qwyOzTv5x&t8!; z#Jww;i76u|(gCf5e^_j{#jLfs@t&6&!}1|S!U>?%mo+a$+d{vZrR48Ae7QpV8ecyF zcwP19Io~(+X=LK^21US3y5Em?2Y22yLc>Y3z$<`WJ4MxqN>K|eSB{nw?5{ts|8dr0 zoaDknfF<|(@iCB|w32B4L<|{406UX1{?bnh$ij)_0~kqOpW+G3ZzkoHbkBbkH7H7# z`TZ3`HYi1Kps9J4lXUytqAdId$F2_RS?>#wuOxtNM?@GT#hCBj#Y0(pB|IZs*}m{W z0TuFF+;VTln*kJ-{b1%hRCb-);Z5O@X1?Mx+sJ#w=$&{a4G%_rDK|Pni(!f`7aI%K z0ynN=(wYZY=}kbnYLUv&`E6Vkrp*r&I0)F%sTq4oG=$xAZYaLDBW#n+7E0wK^ow7d zEn!VJH$q)82I`@=1CPqDrD z12$f5FO#@?JQh&$n%ccHro;GRK=4EPK9SZ*I02Mi2G#QbB&`ra#&*S5$+70!;IlhA zv{X;mahZ#r&*_ipwEXJok#K}w^)4f41oAhdxZB#jFwIL;T3Htf)vw{Oxg53h*br{~ zw3;o)J=h(|V12V~#+3jSizAyP^HDVp06;ubIT{nvVa)Uxz_b|j&3;=kKt}~~kd@Q1 zQgL*>bVw4y?l&6qk3L!STtfBg9<*~e61cAh5j9PW{Eh{_~1KCg&YzTj>{e4IN<*L}`{7A6*$9$`{TECcW*HGq> zb~?Yidn8`g-`T7BUuvQlZ%pqpFn$Ezc>jj1Uddmnd~^~VRr+Vp(5o!SNslmm%A9ZI ztDdtE3zVCP-Mc@%xjQpn9(O|1lsq5YEH<+z;gnhD`tVfXmfE26dX6%~ZX(TwRmOt{ z_ACg6^Gw_}E1~CuEnPDK(-+Hm5Fg(vKo|dnof5{c@-PZ@XWco}uo@_^!b5~i{oUag zRLnx#`Q}YfETjO?R~-+K2KC?hJpyFfkhsequ41MB%!h7r=bmPOkkZ8Sg=GjIR8*id z@vV+$1#9?`;fAh|{4O1YU3Hm?sy*~OM; zVVpH?T|)7?Ex$=(OKK4B+PY<2+^0Q-nk~i3b7cGK`Vt-wayB#Gf5dCEuu0|oN_o~D z(VUAVr!6G(iLYhJ5mpNyvL#fPSa@bI&n%{`o{kC3Wlss`if;QxMYeJi`%!S##^w&q zM{k*2O7F@MWa!_s^o;${^RxMxsTXBOX6Gs-h&MJhMJUr4#)%%YLi!J4?Z%=CI-FvQj9~_ezm5++)0o2W$@=|Q= zBY2AV_-N@CRTPigdZrpOeP&6Y9qqRtXmf%LkXLSwm(jdGRyaVqS+h~i^N}TH_#28V zV#ri;{5!Kk{LYdkdPNXU4iSC0Y_nJ6>;G$}44*lPjk*4hHl9<>loq`X)#dAE53h?r zNua{tqM3fYY}==PV?F#*yYJk5J5=cwBL8v4zz9+Oz4e`Hsb6lz4L&4i7X==AX3I8} zWR&~d#dlt`0!o0-NMWKf&D^9JFO3tFy_Ny6t(MXjqNA+Ho2_ZiVv@r$2dQFJ#R@q! z%MC9?xLaKwgIqPkZV8>-##x#R^u_) zpAvQYW}P-=&R^!MqI!F9;XR<8vL1y-Z0grW>rvpMB!9lj(o;EF4<1G$IIxUngg9cQ zIEe9*%o7@d|VCRVJu|Rnh+LcL$ABr*^J?4)&0gh__FRDV!o&=8kTQ! z`tCXq>)xx**NZngO|$%!k=<IX0+{kmz`5+yoxG;C%hid}a6=Zn&|A|R1{(K=a zJZRC57oi%L3-j?t6sD16L=h=ZD-Y;b2GgB;S68k#uUb|c>i~XM4 zYY>!@+;vNynsSjWITqLTeSPWr-0;1P-vDN~_uxl83E&7+<#IqQ6{z;Sm8smGBR8S- zf$K~9!8iK@5pplYoZaSKcD^?x-xWcT?J);TEA4-Fb4^LzhPoKW`L?JN)n)a3?-v5W z#Se^={T_f{BoLjcAdjv+p|H%=r$4@h9xb3V@1MGHlZ%@|ZGT3>c?U}&?z|EL-y^@x zU&$*E?}{{u+x80sC_EF9mN0icvO83tbH6T{IG0x07iX zF=DT-^w2GGg4Oo=Q73S8*Err?4{51EtD#XAlzl_?7lMg=l0R{8?24=bJvedj2_(hYF2dObplW+^R=@*}kRr3T?Ju4`8q!)F>wR!=_XqkVWFT}fp z>kcHAsO1Hi#Sb~y7irOr+%!GwQWIf$31E_9S74qo2|LF8;@fO?;nshEfs}2vcMDM$ z*aC29%-KB}|E$~~b)A7=+fl{4tX|FE-&QVnoxL^OCMeEu7sdQcR2gA$T<*h_(^p?# zGFSb8pD-WD_VDX_gHmRhhKUd_v@Ev3(KuB^kkOVt4-vmxmlnd5J&{=CX&~}TDP#7m zl|7!rIrfAfgFWlonnSo*D2#BsGB3GtO83-^(J}hP{Okb0`X0tA83JB2ubPOu<%7b znvd%&=~qy;N`ohW*gMLeVj>2D%HBn}g7qpkUjiKdw1+;kKi(oq96=tnd^MC_)O7tSdtwZlckU{R-^86q;aIz)Oxj19w8;{Sg zGo5~itr5%~$4=KY3}1utuNOX<;LrE2l>S9Ulgtw~J_;4iJ=0P`*Z+JM@NkBKuoPIe z!SIyolT*P=19TxR-961ZpAB8IXh2=?yxYxcxvR-_HZU8bFr)Y$LtHFA(XXhve0K~2rtEIOob0>uC%vjAr#w1WDDAG$dn0>%));h)oaJ}-8{<(o`w#y_F|t_!(Bx)0 z;gC%A!M4L>j?GqJ1QXDR5qST!Dh!G`oC>4id2JsD7ln2=E`}1+F!kA@+n~-HfThyp z)>ygrlF1K+I78&$MB9w0ZN4mKMqou6;ui!s%#LB*_tW}i6{0!y2*bRh9IhE8w|4rm z86htB>)i<`iA;NsYn(B^`>|q8l);*dLacD#$)%j{N=P`h=0;BSF|nr>cHq>?>$>ky zz=!%7I01{~3PDQ+exfZ_6=!^5BJO?S*f(QszB=as*!5g@1g)i6VW z`KH+eU(v5XTe4Fb!G_%DnXMu|v$(pedZnEoY(c`LQY^aL%QOkBW;QM!Gk^EtBzlzF z_g0_0L{;v*QF`cSXT$B*@hW0(CA4-A=_P1ac;sEMF-MitH?ojDU*RRHkbiq&#_jo3 z+ReA<-aC_8iMvz}_s8Y~W*D4a1m96REorJ87nLTE6E#RecU!xHVg$0h{3x|eKFJJ} zjImS&OvM5S@f<$b2`@Y_7F3|Zz8?G3+}*}u_BQlbm!}J6WHh2H zD&j%YNnmWoNfZ;*1dPBLwM@i1(BVeEvSi;^NSzR*2`#K&v-Dh0?*Nl56Mmz~9kx1X zELHFlysoIrEp#=Cy1NVj6wiqOHOh!?l7D-EU8RWPFQ#{=Gr-mC>Kg6f$uzwa$iQ(J zmuPSty%8uCN1D_`{m0__x0D3j9l%ipoZdQAgoli_hui;WxS(Yd8R`C=LtwZd{K$V! z5m+>Ye0Q2GxvzL(P>v55^dW+OBL+U9(4MzG#ao{I0}Qr{f|S1`S6|c1q|0MkG(fFEF*2fZ8$HoH@TtI zbLMF?ZNEt&t!xHkIXI}T#k?&x+CMbZlLf1p2x&y%{MiYPLPc%==R9afRwN_v_MIA4 zVBZ0 zZO~fbV&jXGAZtR~dH_qNOJTB7OPxM4z6}is5gIDs1sX7cz8IqUp+Y4r$S>wMKSF>d zIpaEOCbYx55O#WbdP`hs0oltcrSf|zLGE@%%W$EhJd=&ZA`Ir|Bn?c7Z!KNaMM6&D zPfp&pN|Z{}vjkG0d%sZI{Z{2J%am_*e;U4L+R#71aR^Yu-O+EK>ja}asgqdTj)4+w zmdChv9683@VeZEDIQ^yr8}W$Ik_~T*-}p1@)?LnjetZ`HeRR~=!)0ywlg;G2{2sd} z84{^191X7%WO&{^J@Z z(>d&yVc2r5+9>^^^h%iSKCjocFGzLu?8@%&%5DzoMtXMYU}pLtI5|8pxnbj0 zppE#S7Cf!w5m&^Y4HN}ru;a!yYTyc0yM>4SH}c0tu(5fR$^Te?UbW=twWpP~Yu~{% z+O&QzvU&iH5)0&CQ`nbm|9-3}+7FUZ!zRKL^M)(?~h_*=+TKAInr~h~$dPPkrS{7J) z&~p={0^cz3CM?MN3OQJbAsC}V_7tPf{k&t*pD3aJov98jA`(2V1TKgUY!kXe9K5wx zc*?0L@pLtET+fZvUGH}Va7w~l)D-U?sO#y#Xa_0>+7 zBU{4Gr zO&EtTx+W%u;9xP$Mm^9zve*vW59R!ycP6CsLmK=zS9XSWE3qS>UAW8`aklyUtEh8V z^=-lR;Xt0DQ+&g3qQ+;r+Vx&~F!8<#iV4Huotvw6 zKrQUH!01^HgP_ui^+ZIDnc4*CEvByIL-QfsJpyn@I=28LaM5buSn~=Rz`@7vP-2^~ zB^vkVf$WChZdFyJ45x?0!lLY8V+Ls>;RKklKb9V2KhY?b@*wl6>EUANf!pRyHsd(O z^>idZ8PiBOStfS)HIn*<*{k&gvXRw-U_ISa&C$TmDqVoDc3P^3}hh z%y={)nx5Cd5h!J+_fJ(@q{d*C%`G6H3H_0tmO0Z7bojpBfLwa(RJ9Exv!7>LhrfJv zn9KD7I))jj$4q z5vDZf{n8Aqjq=(55etG*wVzYM`{*xqq8jVw*hmqNe@~sP3Eacpj%nE27zOvW`xchV z%`~U4*TEYsn{{uwn#K4x_tI^OwN#3TLvkEKr2Blg1)cp{CEA0oLK|^^oKcRSs#g5w zFjP0@a|&~@c&g0?a`pMVfuKo+O`5{XlZmDQRRcEfzPpL#yrZmLV$8t}BnaG2h5)QC zP$gd&KA&1}K88j1kBgu)=&OCx&yGZ93+2 zfU;-ux~AIdNhEE?X2bu=&UO35!+*V2pH&DxonLoXefVpWd+iBm{js||xdUInraA@T z1icvgg%gMqX<_LcKuxY`x_jN$?A)?Sk{#p@j)1NS{el9Zy>c?vG%CX9eLj5K$hzTv zh4xCPqv3nGsN6I73Cb z43T04*=sV+WfrkxFZT-ur+GzTOr9k=Jpsot3k*ML;HBwa{Qz_K+E2f#sKYJzesfLs z`JcHdkEPm2XQ0zF>9Vw@MP%4?+{1a@Wi(q%gwCxutj}@quwF_T)s5%;JUGia?VEM- zO5V;*k)-wcxGM*vL-QiXXfpMxwt0!BZ2je!Zcgj3_i|+=ZDROC*#}P z19ig8nB0^<3ajgu9_ab~*4>@oEJ8eL0{BN@_{_sJlEr7bF|ku*(4mJZ8&(^vB{%Cp zqC$v&_NAI|0)JpehTGf#y!4QE&De|sJS_2s0>NZ{6(4@?w;VJaY|JG;WwNzSde_M1 z@mj()8trP_pCSwO5HEK(!Vh5s1#g;8VMpIDuIYpy~V)^+G$N zoQ_`M#lmMX^itYetlZf_yJJ^a7+R9UeU%y_YD@lIPi}=v_ME}#iZGWjvp3@VU#=QD zh=Sy9UZZnBRcA{UD7-wo*$gwK@Do}dO6i(@}(8eyJs=%J`H z4-K6$qXDmd z`$iM*hNAevaRQe**gbpy65!<3=yDTr3v^-0Zg4cF5d(Q>#CbfXpW^=fQm760W1*|H zJM=LHw)b^*mU~)KW0HjHS}a>Y{vhCm2bx*&Bp~>(L!Z|`*o!AcHAPDcQ9{V!k#YhXa*w1)>w-U{ z-4dK*vM|uBc?_y_>a!GBec4D(#7%~#22y6yt+D;gYyIj&{~6Ip4)n|E;P~J!tpkrj zHV3N{1O3r5*qTl4tb-FW49$JpBT}6wUjy*<8+omQWwEu-ufvMZ2^rgjPf#+rtm`Qt zhmvR#u|1vjwr^Qs{uwz|CNm(Qs&;1ez5K;5n}s+DtkibsHcKPl>C8t|3!`G0zlX3+ zBD~0D8c_l?hzIv4h3E^&X;YJV06y3Yz z=)+B?`EC;L?5;c+zNr%Qh>n-GwsY=7TK%t2bLqF38(qGNT%fNz+?@yV;ya6k0Y23^ z%-#gbBUIp!Ak$$d3F9|0q|!V9_nV-o1D3YSXjF{;{nKoj2-Kp0nq}YXZU4@J!`N-_ z$ZYS#Y&b9X67SyMi`*capD`Upn}9kgfV%`yACDPRdt#9#7s5RkEKQp@zs?F&Ux^4e zw7G9LD)lKn#fS-{SQ4aB;78&SY7V^rw*69c9NU?D=czh9BipGD|IYeY&%;1$ePu({>F1sp)iK?E;@W z-|Ri}<(~3Jsey%*X^GNnqsLvlR$A0w#^_I`h8#cs7Ux5mo`caAJ>xW2Yi;@~YPMGI z;`;;Rlgf|v3>4dAW%Re=lnOyg&N5ChXaOc5hNYJp;5S3-VJ$jTl-QPt_Ir7`h7kj%<+q9eDY()J+ghr}JS^7$(jQTa1^Vl(Rep(t`+rFdkXFfAz23 zmPI+OO0Uyq1o86ajQ9Y4e7%}V^2WInuuNU7Fh*p#$ z{emto=-sQ3?Vi|NIbRR#rm`|_>ydgj;3 zMj`;cPLBoaF6ICC0%)8n_E^$odu{avhvfCvcMn?kN^e2VRJM!=@K`!k zP)oT|C*fg9qEkVpn;s2()=mYj9|u|@P3J*;{5nzW?S5H3+-UB`6bigmyHWz?4#e#s zitE7Co!QT`O0nWMPLNr0T~P<_pLj|7VKFzR7|?C8j538+vwJoTo%Mt!V`;AbBenH8 zKMh4&5OuI+haER&9OaFcJZ`fu#6>qAIU*+Q=pil|HEGVz-9#=jRSve*Dif7$Zh~^6 z{toyCcBdp4-ZsaSpJbz!B0e=HpJZm4_h8qjN>ClidfZq$-ijXX+q zG;fpn8cTcSnjz9p9nTTkEidL~O?hS;Z{q<1M)F0mH#NxuGQqKcr&tLCnSc z^-Cnuo+&Xu5{798#Of{5wFmv6BfgKS^aq-qm~^MHLfMb@=+SRz2O=98hD!Dy$Pgs` z^RC;r9cMh#F&UEOckh0DX3C)rikrSxKoWm#+&&eWt3=Y#Mju8>lhI_%E`Sqz^eN#V z+yRjf(Bbmbk|pJeGiNqS&)qxnx(WNYLG&Fmq;5UTl*)FRQ9)oke6cK%y;qX%?)X%l> zc{wE4v4J>sC?C#hzPt3y)cLdOxa3jAV5P^aJaYk-_sjb^9GsycZyu{V&cbookm@;y zdal?$HNda(yQI(KDn&qMzH_=`LRVrRcd0!>yO}-rcjoN2QH=H&<=H2qFSw65hzs|= z!g>1@Qzw)RcrP8d!9rh?5hph}4H=uMUXc+9hILNG3P?(S_3GF(-Z=E@l*_lLzqftA zs2m?Zc+?PmlL<+jtca5jJ?}zAwYT__;qb4R{tV*$zfXa@V>K*0Y@L__VcR2Kj}{9 zxg*W~EIg>Gk!Uz`+^%!+=X!#m_9v<=*rw6^ag*oU+MKEWq<*5HCyAuQ<-gvJIAit{4byb zF;%`WE^Rr6{5g)w)Ua$$!#};!EeWX{3^xX^h2hpSlrpovHZj*METCva%o;ieRx6?MkH>2_OaV{wgl9 zu0v-yHu4ydTHj)HlupFyq6Lt03_D#+fCGN1nAYDaSE5q_{gz1ucSj#~G~DPWw*9tg zO&>rD1<9~awx_8dP?J)-L$W5@4Uv)EY!LshFk<8HqhAzjM-}CiA;54V<$5faP95I- zU_YWq@wbH6`)$SlIe_#@;hP4H)B>qf!(ONSXN3Yhi^$ zw!^K4$+FsdC@DmMc<%j%v?!^JRaYqJr8}HSnwa%{;1egfduRkX;8D}m7slcz{+lK8 zBK^@fsTL@yMwC{M?fuGl0Q=?i*+cU`1N+X`&g(q}p~E_U{QA6hA{*mg;`v@%{Rhkw zN|ldI^NZ$J0=>l~%SL&5ss1tYkui)w0RKtXS3>)S8Q%zi+Pk zjl)s;Jks_hxpVvt9CnRrHjfzs_Aq?eD)*K44Fm%r8?;}xa(*QPYNsr+-%ZYfR70MD zQmWDIy56XkjV$i#Uw^Y|b`vg6ga-#(6_RIURl7I3z z1jyZ!QVRZMY_T5(u#Y-np0Deyg#Bord4dB`2Lqbx78hLJ;I(hzlDY8&U`ukpQOFIH z#C{Xs@bCwG(g8qU`MGmy*~iRk2|?}jK9}(P-=O>uEsIQqF0heGB*3lM>ZrM!cgW?B zsQ%1j>dA?_8_t(2wC|UeZvDAEvkmWs{M|1K-DV9- zn#olB-BJ_(oqZ$z(K^AH5w@m9-<>z-b1LzJp7m9&yED6Fdfip&p}jvy)UAAsH7NVRXJuqx zAHAIXWslf$sTfY--)qAb{MQ0c+vnN2%nRmx^Sm>wQX?XnQy2p0xZ{ET0lY6;$tNFj z{y2EKqT3??>1G{9ALfB)O^B^eg`^_kL`aw>6H)A<#$r|q;y^Qn1yVK=ng7%BSEb?8->q zA1bEq{qN}(<>7Sm1w?Is+hU3Cg9uxMhA4;L?{nqEcmc;4#G43o9U>no3>M!ty;|VK zuin3TuCqU1b8hPPzrOa0#uU>T>I{dGz9K7rCYb-IoV4j)Yvt9q~RD^Py}nWCB&@?H>qP0{WsaP=O!4sNf0i-4(iJ z*dRj#ES#<-9Si-28aPs=^;e&*^HbV6(Y}qNk>l9bT*z;PIh!LL&G+SyF05S+-%SEf zv^GBvP@6w-0RNk4kcA6^0F(W4>tG?mw{({;0b%77v`@A{>D=H1HUOZ2oOtJ8QygM!oWB_tqhXrjtH=D5J(P?V$pylo#|%iuU&Mym+cf^%rdNhQKx=?gpK?S?rd4>HawO%Y1Y>X*`>3*pKsE(3v z+$ZN2oBe(|u!F|5v#~GT~JTwO)a)OwAsazk>#+wA?}v|Ns``&*=IgRa|ANvwH~u2&vFwVGWS!ZV1kHm&yY%>b<9!$I*az@1>5=883QriKS@fO@SoHo5Dx7v;%gfR zXQ0~&q8sc_j?9yW-<*+B#sFSDBcwN z*(GvJ`%r#B(r=h+Qt1*3G_gPRmZ+`B+WB8KT#zB|f1U+GnVT~?q5MpQhEd-}m8)YCyhidAx@roC5G**S4vNUXRSb2)zF-PbU@0b&s09 zM%fkp_>MvwFl~>r-q?yPWqveTJhWM@#<$rzsPa*J@9w<$%!mvb2wdFlQ$?{z>FC!o zeXJj98TTVtk=7Th@w5ATA39^ceE@UzPI($0)VD-% ziMlQA`X>fF_N*fc5imAVLLz9PDg`;yu*W8*+E!WsGG6k(>IoeluAc(SrQHuMnHZ1w z58PoJDWR~Roj}Sj_Ib(`K=i>1P~8^ZqGWnO9Y$ulY3QH-=wjV+bwMTu%{i|N;a11b zam$esut8es8@%VhLShg8lOvGuxP-_WMLK{W^hcXMzsk#b*sA{XN8?}H>-KbepZYbu z{3aixuX*_Mj`i6Lgv>K3mf3P^rLFUh!DpNA`k5RQpOR8em}|1-v+ugrL#xzIf|j+! zTseg)WU5zBIB6*8e{mx6xrLmgx}{N9ip&1T0!-}O z^Whq_e0BoLNc9xr?#`?hBK`_HWMt%XP!iY|%p5stsqW8rKTf^kKFQePz41H?Q}Y{t zUw1}4)rr0PSBN4>TP2m)OABMBO4eFoWHRHkD6fW`K8=HXObsPfr>5jsPHsQ2Ugy3W z8ryt9anAnvgrOoOP7#{LKKX_T4jxf1rP2xW%jupBrS;~A8#T?nGbaQ#pH ziH!cSWaSaAog~N<9e8DEJl{i?qkK6~bScNI?KfrEmdV7iN{Mfh97ScahR#%H!aL8` z><|C)<<60F54i4%CP=h!28G^md-m0 zdDp~qT{&e7B9tuUNj((0Z1Yo0{@MpS)qv;5Vc&*DZU22qm|`jBgA-JV_ljZk^a3QV z1?uh;S!7C*5qfuEmE?>q;_PGw;PBuRq24%{!u#pxcZ{?kFeJ-u!Wc9=v#p(i#EAT? zwpH*=6a=z^NNNZ9AGvpGU4CQ=-EXarWJyKtAEx=c1X9PGaI?R5N3WoDe(&w)Jjf?| zLN2o(mPoJ!!IpK~2y+B|2d_(67WMX`XL#^I=d=IOqolNTX{}wF{lnj-?W^b>&0h@3 zN%_qm{otf3-6nWC1tZY>jx+1-qHpv6U1Y>ke%UPEkY%8KHP9CCB4A9o?A+qj5= zM3Dn06k<+RD(_8X3siv*DC}$37wbE==649GZ$`YJFUFGk^uXrR)1xKBBeAoe!8^>= z|2)~7T`oL;N^@+vcs}Y}6>wEDV`dDR_zobrcn9s!-@6|{gidbQ`bF!9ZP|3xw9(o{%7GdoK92#LEAf+Nv}OWU0Z)W^te$X^{J4|wF0wbWnu);s{`rq5eRa~ zqgFxG&%zsT*@^1k4mk5WGnu8jr(uvGF&3yhi52YtWH2B-^$N=Ft(@7~tZJpBmi9Rd zp806onqJu_q-{@IvXHjgMxPYl1{Kv>RRoia>1pFEp!s(CJkvEBd8NS0m>FlT!A8VL zGVV`+ePe=k+8B;l{yZ?<^Q&s2)@lOm(+j|esL#T31P_HKK-A(^XV^Fs;cOmv{+sPn z6a?`(yDUJVpWwD){I)|>$QE6-sL!MuH5CxxC-X!`A_yl}=+U2pCBZN%$58r(%RT(- z{nks=-+`-zXfLQ?Eh~>MD!gcO~qJr zSPm;(g~aj?K;!oxd4f zOaFdudy5*^oB2EU`_6okL3bM0^WlWOeQ04!)%p4KuqIF_B8Ks9VA4gag9#~;g%puo zpBvYkv3y_`cnqTX1+G`QG5uM?Sby*UmGR8AZT(foG2kR1sIM!ntf+}J(@RiHWaWH# z1y3h=C2#sIU-}z_>+D=$J$kT_=>PHb)=^Qv&-?Iemt|RcX^?Itr9o;%8bMG>LP8Xz z1?kwO6agt|DFqej?v@5A=?3ZUTy`IPe}A86{@vYk_S|P??rUbQYwjlo%{p5SVJY8) zZe3rwCs@Cj$GvKS&Um{ky8d4rVBw}nh-vQ7-{DwGb?f;8A}w>+s) z!N?&_$0vM`8UGT&qL-cyV)DJtrWU$poH1n216Gm0NRcO6v(1-;a5j!SGvvEmcV-Q5 zMuxBVq`(n>XW!9%VplqH`S1lFH)v6<14pWI&08`20XrK7%Nyt3e6yN#mGTD$$fpoyzyAoR)E>7ta8Ab7o^h#w;V!!rR z$xaAp0MsnG@t)c2)U2<>GP2BRtCsAl@0qE_j&I+|c;)rND8WX-`mNp+&LAQERbtL^ z5Myo!y_d3tHBPRgdU&@1=+fT=xk%tMG>Gni7lRj zI6hxL{C2QuJu^;h@dq4Eo3&gwsE805tQe!SIP8AW96l&}mJf%oZSVhmUwpIRwOgpr zx$S&I=d{MG7)NfRO7bDmRJ6tNLC_Bf?BQZd!Tqate0-)bqBY1-ft))>2_Lnv;ewx3 z5pEm@A+C{Ex)A<($^n;rkmQ_D+tS^bjc)LYYYsEfO|!~%N-Y790rqpb_4)Drd2h&B zR@z95DI5BSKBlj7HveED4jX&?pRPC{J7-%_;c>yAeYAgb{Apn_aX!_GUS&UFC2AHQ zSBl1KA2-JL^`M6j%g4w}&Nb2(H>x(%yJwp>H`7|ZcfA+n2GW1;Ch!0y<38JF%a87R zi@MckEhM5vMI(kDG77Ln0FIsmm!uUWmWCuj&-T-&_!lT}_?Pxn24(s$M{)zAyU$^m zU;Gru&5|?YN9nOoS-nhfEXsG+{-n6kfFR$EFY+v|_f1A!ar{4yPiQ{?w~7mO>B~k>7o9Ta56DYx7RKe`(!+iIOQDG>Rm@B-h6RgN@ZCSl?}=N*yD?B&o7ex5S8roH z38S+lCbgJ)Y>E!?TKX_U7_HXAha~}r#B>BR6DbmvSzB`8lyxYp1rtL}EC#)l9@o>K zKYDCFP-r(Xzi_V1&tz2?M#aZ|eC?s1F8`zcucw~PdJ-KSU3FySLS0Autx1B|KVsjf z$l?)ESO;>dz$Y9bqrLrglp!$<8~u`P1U{SvZM0&jq`5e9sy6P4h_$?KTN zR970A29Nl$j4TT(+&AxI_uMB>Z_a(o2~k~I6}tVTA~~XXZ$J)D3bVeOziv1+zentu zpJ;xZo0H!AFskCh_tMFOu;%EIuY-CWLB2zOa4c!q?UO=yYpf%~*|dP!v9Njc{G~qo zI@@JRa`MHucDGQ#hXld7>A?;7Hd8@(cFG+hQF=F+&n#IXW19MK9f!}~b+yQ}ly z0=a6-+Dp8AJ8aCYF3qws$||-uC28BaJO+D@UVkfX$ToXW3 z+WNml<#}PhWYQy6;vjcite-B`wX08hl9FpbSoF-U-p?=el{1 zes*I?v!c{7s5j8eEdQjX<+h`C>$aSo*Dz24C!n)Yx4^k5T?#rA0H?Abj%_pb6W1fx z=oM1VU#kROS9r`B$^fiMbAZV0l0{A(3!ns$CJUoWqma5+7Ew>pKHBU+%wvo-^56*5 z6-Pe+V-uus3E6m{N|1IqnKgHi+{flK-N(zt_Ba0Kd$HGa2hRU|F2Ajd|>d!zD zqLNM#F6qp+hlp*32_ z@9$q1Pc*_?=BukUTXY>z9_E=IgKSx|KVF7sU5!2|f~XMM{IuNbVcaz(0No* z?WcS{d9mfTQgZ4Y!oN_;Y*(M{z9KZ}Hi$ zMQTc~nTAb5oK3g%O&C@lY_KK`IJTE%+JRiFn!Fui#7 zWm4Zoa7oJJfMK?(4#4?+rtPTF zUmQvfRab#gt-`UCNG%Y7`XJ&vd9KikQQMXXYtC{aTwRH@f3YUr9G`;>Bi^gP%7)5? zAJayZ2Edf_oz)dW1GbxPZw+xbtVt!NsgUb3Gx^rj)rNkGHgjKIgu$#p2%v=qRrA;5 z{%L0U4vFm#|JBd|qoU5YpRN(w+s@^%wc-P8*H6U%xBxoElxJxNsV^_wLU6NfCd<4B z?k^|zV&kjtR%kUmF)AP0D}F;U(%_^4Lzr9 zsCGIHh={v@`Sxy@wIXbTBKrMJrJOGre+v&yI~qj=+SgR_cN=qsm9$t6J9`mXAyQB! z6%a;u>$`I;cgzT1e<(7(lY2MV%5A3t^W3{Mc-h z!~9Alru+`eW#%?wmjrSBA@t%G1SH!ga0g2tCU5iNTJ-i|VTxu!+FdYEv6wUAfzR{0 zdK^fde{$-Tt&CFG3kBu2q1Fh7W^wtA8LPs+qwjP_yO9Y~mIsvfjND+Mubzhh#+vFU zo~6)xtyhXN<5k;QXCDDj<6UH;Oh)!;^^NHN1+C!+Qr2s`Xb*cPN#l!mip)4VmrA-L zu3b;JxDz5zMDb9_|HcZw|g9d&y5|)Gx91;%WU_ z<*ZqVufqgFf5fulY^;3_zGb3@al7yI_{go#1DRdH9=Bk^3LhA;;xopB77|t^0x7%R zighm4n!nCEgqsTcJ9k!6;Q-qgvb#@gzWW5Lt0%1`o}7M34hp)BB*Gvn)5x9+gNUd;UM^KGmqU*NJuH=A>{UN}Ir3>>D+!`OE& zawks8c=uCWOP-R`-G-U6Rk``^UMu89>$h(78Ix*%At2K8q2HST zug=eT2JLya&#gX~Qiij<0o_YQX4(GSk5U}_m0Wt9_{`|sB_ID>!}4R{ca44f-nl(G z!-DGb0N>VD0fHBDhP9aoh{9h}?Pe8Vvcc%vHCi*`W0s@DcQ_vNCFAov4)aN73f3>Q?mDNFnU2!P=d{D{$`oq&<$R1yJ}T#EV_D>XtsJa$Z}x! zn9_-iWTjm0U;H~=@?4L} zFK52Cj`k7RA*{*0z6Bir4w)V>8G$4(hxQ$GVCtl;<);oO}y(n zshp@e8qq8xy|-hA`C~)VhIq=Kep$OQ+%t}iqN#9fl~sC`LRXMB5ZNL~f@+q`R=G)5 zMBPTBw=A~=>O0-_8D0v``3uN~J|As~0cdycIju$CtHF*tvA}10f#O|;D#`ra*v;X~ z=Y0#VjIjYXg%e$hDA%FPw!xBmYX0oyI0+J%;TJ>df0e9{Dm`+jXgVQdhVFzWed z;7FZNXcXFH{)OgO!4ds8y^YFMsUs$Ba!;TO%On_i(;JUWAQ-Tpc^ zLhefhXi@jC_)b=bY_Gm<14@sbKl6%j;mn?i3*ijPq5^l4Gt=+1svemCJ(k&|#vc@j z%WMq8`VLxgrHMN4Z3mdQ*MV;|vwY>JQKTuh^tFGC)+AYjlma0uyT22%-B{ovD$fGF zT|{2UeA(cj=yz7=-KkBdd%#B^#o8iT0OPu?7+xO;TjB7WSwS}FoT-4U-w{qxQ{^Nj z6NSj;<~@I`b4Tol#^>jbIvJ8&H>pn60tcqc3jR0Tw?A#-s#$cvRpsBNwt5@$-8V!3 zw>)T*c;`+NEsiL;U|7EXm8HoVe^9WRmYbtsJ`)@uPjG}5ZhPtakOvK;@pN5zW5-Kg z4X(c1e{na!KC*@ksULwGQkq{A%IoJCDV3FSWp8m2V)f=t{KUqSDe8c0WplqRc^^Nr z!^zLxH~L0Bziby2?d9W!330dln}&-E#8uMWB0|>3vyjy_1Q#Nc2-44!kcd*R8o>MR zZhk8{JM2TPG7>UWSz+S&4mj+&OJ1+WyQ;a228Fy8@yyLaJi@F>Md0sBKvA($vh+B7 zpsWv9-{BTk%Z5z*^z<#}<Mi?2p5mV|1Dc(ZCP3?5#$4lXz|IfEs0QlrnypX7T#@)f^dl z-~8!!aNg#AmHVV~k`_lNV&g2mp#zDPj{CeGgEcuWd6Hiq>r+r<_%#A2%K&gEL0?Mf6%c`7aTH%NgEyy>o8-IHQ_SXJme-7@FAwJH*jt7xN!`h+!6CHwa>VWu zma6U(IP3mhTlM&D0>tkw=~6b5r4ynYzY@K^u6tzriZNnJvJO!Ixh9&Qb5iV^7*d9%#g>u3^UhNc`NFE9hKU zeayxXL;sM)I*ugC7r#Z3?1QM4MmmnVBJ={k#jGcW?#?3?QMf3M?pK%#^AgtYKjWjLX+Q7N>@1YiaOf1{ z)>;iRuD;Q$QpfzuY)&0rMxX5rCc2wt=Ebo|uKH~j`11R+R$4(lwpitDx9PG_HiHmL-nUE0& zRfD^&d|f004^;vvbVa8Bw3%;DW^Xetc{XQW7sN-nC>7_QyM3hr+j~=KlE=2T_S?H4GxFVvUt<1)TBAsvlqUjWzlflVMHNvAWOx9aez< zAz3%FdVI`HHTB=BGwG;)tX#iwbJ3i;K+!}LnROq{fi>Amq;%4%@7%5y zW#WzCD=pwT`&>2+82J5hn$IwB9(S>O`kI>NK&}sEQqb+Z^nO05kq}lT{o^@HTZ7K1s}f z!YI_!T4OpPb~D)1XM#fs9|^VhcOP=z`^JS!056YebmzjM)Ylct_=%6G0csHDiyJ`+e1oCz!b=HV+ znL71exh~YGbyu(eg;(#B^! z(C?2PJRZuw49RcH0J63h(;LpMD0vDl-w~*_X~PjMFy)h%Gf!gpz4rG5-+G+s%>j2p@marJUDu{+ zB1m9g0Nqcvh3#6c;mV%PUVjnkvElghTw$Bt&7Tj>dJz}(`+B`W`nGHPCXDnnvhHp# zmg#a~c*}F*tN@x8iXh>kZaeE!v@k`b#SCPIOB;h-pg*w+ZwV2N2_+;Nf13^MCQba& ztL3RW7ayx<>2c9dJMJ9|<2Q~GEJ_#st!^iwb!4}_-03Es*f%(hY%^|PM6vn0PL&)NeFOnoxy{< z7e|lqw!Z&h?h$DP;fb=sy+zJYV516ew*JTJjVv!R#KiBU_}g5)&DGg|MOKGbX62?} z!x`qzW%c>XNUG;?iDnsj{e3OTbO|TxLP+MzBV0uYIH95L2`{src353AZA?T?OL%!g zj2-1w$VkXtUHvd#e)EbuZa4MSxQlMVzoNUnl9-(a2iNSeojN?Jo-{g>Y@@VZMMN&I z9Z)UexgFN8V+!;%PRbHKeNE$=oqHo{M1%VB!CUkkp*6Ya2?A29km7NX8gW8Ty#5?& zgqIgpx?cWh#UduhR!ka5rFef8phs=-ku>IzNH z1F`=Aw7HPVtLy3*to<~ycz00XW97K`8uq2X_UZKM4^h9WQN(#r<45Q7*NoR- zi8U<63(_)L6`{Jsr`-A|9a+C;P&9c^0D7%mi-qzU*6TkkHo!Cd+dG#$Xvs7HZovyR zd}kfJ7)?3V!D~9pd4C(Qyo$||sR4!J`Fzp%W*R}+2}v-`^0wQj-Fp>}4JEA;S92l3 z%$v3v@P0Ldt|T@bBX!EJjJHe+>5JkHO#H7@*Hul9I{fDHvgU1!O{xok{wk`U>hXCw zs^6qZ?vH;Wp90G9o3zSZ8Y6s=nTSS(D;-NM4zxUSeF z?0X2P0rxr(>^$iC|9mlh^3Moqk#FHm44U~h)?YqO-tF*P(GJ<H8~`(xb0lu<>U&@u7YtW^f4IETpnGUkw8n z(0es@v1A*9x$!eda}$whqCUA7HaRuLkIaH~nt@AmUnkPPUbY1U5-Z3M7OcUa+F?@} za}CZe`yo%cV+gg&hESHf5F<%z6a~&P(R}b?k}*dYvzRZ7+h^|@m|X+ z2)lp!d}C7_jnTSoqSUdWB(P^Ll>GbBmyt(uSLdwmQ-oKZjy8ekg?;~SllJT7Y2#B{ z|9pjo_h|D!m~x}eEq|ZiDQ}-XyZ%wF(0)0`%*M_>L0EXB%&+ho0*P}OIw(w(+)}G9FRRPs z7@RcpStEB`wtal4dikL#NTsIZ2GRN`Y@GYoiusMvV!yl~&yqw+J{eS}ipZ1(2#gVt z{Asqxrp6)+vJ`adQiKVEe?q|RDP;RF=}dSVPM89&U?%8rW|fJTv0b>j5O}Jh0u`u* zLinAawJ>;3N?3fVm4j^l>$KWtD)3us&}mL>*RM{Jf6Yi9o`p|`4R4e}{rOSr8@uNj z=d$L$`>7>{(>GL)TWp!q|La(GEKwWWXS>-$m-jUP=Bl@M=r(YI{GRr7vy@uTyvYPa zmg6GM6ygr#+BS6yPy*`xsULItfV;ljU~>1hU6kl`Go^Cw5i9Z6`IKJzUFqZ9@%hj8 zTTkxQG;vc-%GxhFxt#jU*NJW{9Dl3*m4Bun7`i-_PDcKsTy!>tko#7Y`f?()PSg~a z-66gPmkVa`*VCb!vwOu|`KON78AaaRU}%*lg643j>XUd<@%qC{e|sVz!hZ9nDZYB8 zvHA}R$wd(;!ropu%Ka__jbrkZ)>ppo+5|68&Ckkxhev`@39wPIUX0pvSzfOHMWh3U z4+7donHcjl`O9_PO6(T2g)T)DMgxJ|Q)Mx4p+v;CyRs$)Dvo!V#|AP)@}bgjN`=Xt8o;_2rE&*X2*(w!->OtXaG3=RamGR9R?`ZKWx94g9KEHaC&Ek=L zXxKn{nY0klhseL7p}?wOl>L4ay`=q-i7^8r#l`f?c|9LHTRB_k$OUKyh1!-Adg`Cu zDJ(6+>%)=5dxE?q^FB-4rRHVDr@Ul_3KVd6)4_Hjqy03DDRlK6+_0e z@j2p^h3|}ZSgMsMx=7KZ&8MbJP48)+{1yuPDhzkI|Ff-hwzh26IRyB~b9T~RbVW4| zo_1~Hc>CaH9YkUO8zYY*-Fm(m$9n23gXd+AHOX)*lubC1q}V@-B)tF4OV1wm;bcFe zWV82ooJE5VzLHw)f%}NZTPN5<`0!FV&kt&b=iex8o<@aaj84CQJrsOrMMzA6m~+7A zmm1vS&F|tDID5zCUQTG>#w3&(ao*%}SC43P%q)(bT>Ld0xW~##a}lIdH3$RjlpYnO zwBy3`Bv#cT$?uZ$T1bO7-@}qJ$I-NrX|(-xY2>bghd`WE+Dck`+OG?W(sXO+C*4;k z!k~(G5``FOT~99ot_t>FJ(BU^fC=mWsaNb4{Rtlucg@!&>`qU7-kqr|z5QP0k-$pk zZt9&EyU5>?bh}9P^ONjm5}md?LgHHrIyK$TdYW76e&T+;W0%p3kH|;u^@f>Maqg=i*qsI_9+nrm$&E{CB(rj{hKj^t z91u6B_QqHfw6=t?FZ=5!oEI7*w1QgGe;hw-^ipQ4g^Z7=^XQ=~9*g)_*4eYa@C zil$Ex?oE}gK9yw0`SJMhA{)!3v|dL^YA@VQ3^2_T=!#n6grAp&Bs~3=bBvwv{VEWi z9Aomw6LImShFWjinF0sp-R^OlBQP5-0tNs{(L(AVEO3aQG*TvbKeZ{+}6 zj*)Hpi#0l{VXYZoc5#{zmXG|T<{aa{1T*R(WgoKxiS*!rgBdVD^Ezg-9<0>ISn7Bo z=jNx`?o7OQUV8s|@x)q1;*k}G47BYvObX64k7J=*r{@0h{e^cH8H)=v5x1uAyk_OS zt6-_oYdDH~=<*z%84%Vtm#K&kg{`#1gh323$8?J*HT*2iDd@eJH@GYi>l9eG{>XSQ z0jNUQ;RK%SOJ22H@E_k{dHa{)58q6;>3^4SvHTgN(RU5wDtNtp+l4XSX%~?XI*iEMcL{B?NdT_yr(CxS&~n(i%EHJkO@@`a-R`PgmKZKadGf zBW$ZvmdH?=H-Un2fKIhPkSa2+FsS;h0m{fDeTt#H4>+BcA29wEKl9!6>{}fn!6j7P zmYS*O?XS1YF+PswF+Lx8!)A@st4XE9Avx>kCX(QI-=GOE*~Rj>zkMW~w8r;bvQ7Z> zj4L{xLW&!pS$X?F!=|JTZ2bmzk)sHADl$57KQ_U_iHm*z&f@;*#7^^V)$#xAR*N^D zKaWNZ5=&$|wVEAQOq`~1VmpFWn+F0fw0`U@+RA`;mQr6c?E`&GJqW{MQm;tyL`9Pd%3<e5;0ZK~KHFx6__n^6=RJbJ1<2_CeE9qwmP4Y`-GNtEyJ+x`kRL zmz^qb`}heC>a|6X6P0bI?l=E0Y93&eQpn?FP1zj0sny^Jd@NlUA5Ic7>~z!fO%qmh zq&Xx_M3G}9zSjxR1d`jSP$<0%`3@1VYZT0KkQ~9yH?~7>lHo*trxtJWScX{UUt0Um z4lDnv(R*c6wZvR(n|l_;A*Bry9HyQDB?ZHUXXcqnhZlHQaTGW3yINuPZYv&>g3vJ5 z-t>Zh8p--O{-^sllB`ViKdadQ}@)7NrB2R98VmpOB4CU(Ls90(0%g zD8frt`JI52&zj1xS6S9?$^yx~!DQ?Qa9btC&VT5+ZpTo-ezaczh9SDUe+rAKC|(`yFWv6*W+YVKkOa4BxSwJ!rotl*z8w9q zMbnV)D&#t-u8R$(DC75!x(QQU6Y;zrWi-wnHIa02_bobH>KD!E8+2mbWiP~VRm~*$ z?Nn;iFb1D!JYks`D@F^zu_V~{ATr#!sipRfcy!}d)r~LJ0 z_|$s>oE>OD2Rbu6R0YhYD}2!dh*VAzZ$T~kG((aO-Sa<-sSuO&Dzwi1TG%rMl_`vu%<&O>t`8VEiN1yiXQT@d`^Anut+S29ww$%)| z`!dQ>8uXV(9k_twrK{@MOYV1awnSho8n@&D5P=KOE$`0}Rs-n0e%`;>fKr5b-DM(1ltK_9f}khEf?1iQ5ZI0h)XUG^vi2V^`9I5uPIu3r z2qXF9tDL0!y_T_ltCSmiWrz=c$nO@|?<$h?HVJuR7&DK4K2=+&g5lQT3Ax!_e<}0E ztmqT}k$RP|Di1+-i{8c8gVy+5xW3a&YNjq_+%1DvHTxzyiow&gXOTJ+Yd={|&b;cG zUw>Iy7BcDOm^I=)4ILN2_}%S&QF*3~i0Gp+naCKsZ!VX=>X-BCec_}74r~-p5W${E zpZmI|cFGv=vJF+4p#@a>{5O#}f8Q-Y=APSTQ{4djn91*c^p zLWv%Qh`SturfS8Zw=9!ps;r9vy+81~=8=wzVt-Z``Ga)? zOO7af-{?8NqG`rK&pHyHbm8Va)l(xu5(TR`Kc8zrpr@w#?VQXY8Il$#A^r<4M2Ozl zxYR77%bPz)Dxi1E*rIs|>Wwf3fCyOzQ|`;Vn&%0+<8w+`O0bAYAY=^+XWvqR5ZGf# z#WY=rnT#%D$-sJL48tAow~uBsAn7SsWaODT z(B_xSS>ubQmzLCW+Zlu{OuV>4bkZ~^dW+X-P%)rZL!_<{Dzhv!1MJ252Y;4nkQiBH zvbk0DLYPv!-2UPNxia({jXiKXEqb|2a}DU!PD>0zMdRsxd5tOFALHquFc2%YW5K#r z&Omlml$tGv0={o?f7PZ2yWHGfzmN76&sBITc*qz@>BJ6!(Z$oU-ji}Gg#s~mD|Fj^ zgMb@3a_FR(uN3+1q~w3*H&#Pk-dn7%ni}94@Oaz&Ve5KpJQy|%B(nsKYRn@hf4 z7dOo)TsotIO@*;v@eiziO^Tih3I@laau zu-Ks%LMo!Kmoj3~wG{)^LLZKf?T!|xzMg7ogwWQ|utD#_laB4ZLb?6k5$3o5FfD$- zIk51nPY82o-v8QMD3Do~9~6Vnkj4oJ!-x)o;B?aYaD+#4Wak}()@A-rV8}W+W5XoH z((@Nk4a5ImEZ>p{2pA5tO|-DPgL2EHx7@Hj@VH`@CvNx9q$v>;XOpnuW)2I#D`)ZL z)lWu)C+xtAX3OR%lcuR42NyqlR{@rv3drm>7n`YC49BMh<~|jf-o>Q;^eczgbf_+1 znC`|$`(2(No$oFB4$`kO4_91ntz{{q^7tj|fn3fSS9y9;62v!ud~)-pn2aRCanR=- z&TTLd_Mw&B>AODv9>*KUlxL!(_C+odcf7NrKOC5D5-h?h4Ai23PN4JE<;56JU%#gY zMXfl85c?+L3>={zuw?g~kEF85Im$2(c~F8$zQF>Uo6&@qhETdtG&c)>%MS%JdxIQ;r>DcDdVbgxjsYW+E>&WfTFWyq ziAoQLnZ|q~bDn#}$JoRnW;_N8Cts31^mRM*!25QlVR}Fv;a;EpfC`N8s1q%k?F}8Q zqd?XE2;N*iW-4ZYU%b*{l6YNL6BIIg794{uXRXign^oPOXq)J?tnHFm{Fc%*VFb83 ze$*n@`#q??wWQ|bFZS`9ya(d)twiQt02RqaMQZi!_9b{rylP)^zc|(U{ucnxHM7_1fIKTr4DDn{pi7~N09+X~=^qUm>=IUc^6u6dsS+0FfCVDzx^<5)F= zsE;7twMjx#B6Z32jj0#zAwd7!n=hpOC!ZJ542QdkmsZ|b+eD<4`rOG>U4R-Y+;qj<$QAyga8e$D^**uJPR-D zNHVzc&zvy2)LD<#N?yuv@Hh(cngexjS7hbe*_{`(zeOf>{Xu2;F<;hLQwVw#@>w{^ zpDb4iSctt5tsxHMC#Fe>4Qj_>QOq$^6JBbk;aBhU<)^B%w!o+J%)D7|sm3~{?IShw zTGi4JcPT$%+iyAEY;Rbs-{6PAC^e0Z;o!ih5S}1#4+_8A>PW{y>j~kSA>OywLq(1X zog@G=FU`Le*Z-Lm5KZ~$`MpJ45UnlxG>z?xO~7u=9`RtU9fldZD6}*ejlECJ+wkzx z*oZKN6quKvUwZrCPlvv}73;N9+uhvMH9o!T%-Zw`iy(t-(yUIur%gX952u~P-I!x{ z^Fl)Z21P~e){)$+DxFhFT;!5lN!kC!qIX?P;N*T2rI}e|3~JBF&lC6m!Sqtadcvb$ zjinuMSvG#(Z+h`n-q{)yvjQq){6_WYg&rGkw>r3KpGEqK**65u+bX;Ji& zDEl8%w}h^vbh)kB0f!e|CrI|jpijI%#}^3Lcgj`wKzr$r5!jeTax@?f;Lg;~*F=f=yW4qSM znT@e)S_9C(QOo~X^)j{OlkHGBor!U4nKkTZBcQgKw6Wc4U ziH3y0alJu{XPkeQ>G}F-h52^Px(sY z#LqJ~|9?V3kF5aedM($NNI4fhK zB^LDo)?DdjNCP}+1@5#KI{|SjH{v_;G238kVgJXkABBrg&e_6^BGFeew%m0U^gSeB zC@6`{z&rbaDcxKoEugwUGUEai0TooL=kYQdNqA`ukRUju{mD;6Qj`gQ1)l$s_7G)(g5ME0M)8XPtC;U7a-tM<$7!p1c8#aI$wsDyjQnXy78?_kk=d ztSrE|9Ac~JjQjSlOH%Ypimm+b0=(75fy=lIISp*4oefSj(4K|B&i?4a%rk;`w2;EJ ziPteH2J9MO-`!l0Z-^QQIVI`lZu^{Bm~b#A0CHo;vQm^t{6UT!-Fbkj^W*Dbc>ObC z6M1-J$miTVR+I7KuKka1Km6rAw3fCmmx>TAamed_(0aw|f+w}2I}%R2O$F2P`BYIB z+tfN@7bX#*Vb6KU6cx(iVxHOiiY!kOnySFwIbAvejh&fSw7{L7CebfH#@10;daBrEsb7gg_h$UnF?6Fguk z0&FVD!)8M`G|=h<3_l4#VlnNC=NAw<(oK-MgO(y-8{2|{%+7%h(Krq0OJq>j!i8t- zawfkFVH^Rdmm{Wyg#eZot5bzz9i@D{fDdLP2e**L(!%f`>Gnso4}=Di^CA3=s?ssS zUvC9J|1-arw2m&JvoNgS;e^|O_;^W7!3>%V(3Zj?vBdM}gJ#7Q(<-uN(ds9@-T+jyI@w96;uf9;7JGswg}Io z2XJL3`R9aQW|m(6#}@hrotr!yU<+X{sUgI@V@ut$NO~DWIZq&leB_rx%jR|S$F4za zrd1xZCAtY8)Z%J5K6b$l#iI?xgHInYYr5of2#(q*VH1;y7?Ea7W@>Uq7YGs54a z=CwmEzUTbZFml)+wEd5)Ow7{TcQw~7M`Omms+adiucEuL?~278)S9

~G~dqhsUG zO7Dbvi5^PEvR9bC;S1Y9e10%#K2AVB_K2pX_8Gm^<2)$|4?=5={a`@lY2X0=!}xko z=xE-hLgMoF-p&2{e1J2?g<~^TdlY)~JLIK2mkP*IcT4@?awLSAjD<=s{pk0iS6-j#kY5>6o>0NA5yDJ%iN<*+_h0%~J zQbDXk`YqtDMsVwGLnbl<5joXhmn(7k(MQGZpKzy>> z!;Bbv7;r%qsR4{Ee5VN@ltn1uy+inQ^1@RpE`9d8e2Uu2f@|a%aWZIejz%JQ9r0&> zKO*>n<@O8YL?NkL&pq%hKN0^?T=J=^A(SzhPj`1mEru50A9PLxbm^5}eIT4$5^oBY zOcIJUU_Hjq(x2Y#zw%%o!AX4I9KO1V?ATmw4)5zzAl#kmH~sdRf%dN3N4ywO_V%H4 z&GsxfT!cLo#-`HECa|oNnqXvlD`x$lHT4Y98l1(zrDRAf#nE{(Hua^M4)t>ZICPsQ zblM6ReO^~R*pRfxVM<|qf#s}Nr7%wfgf$KI=Vj}R5Vc;LBJe@N?W650EDsqrn-ER& zp*`+deCMNt_1w+o%A=xItcas&z1-nro&4alnYgXqZ$!cq_ieADoV}&K2@aW1c=fp! zpDx(`e*kwth`-ziLI(dFHTNI6Ijq1?Py*r2$#LA;rm?w;*D^#?FC9*%F5R|`j;)Br_up7*JEZYJG*)lP!AV4Din^+yie?OOf9!myhWXelg zTmj^SK2R+Ih_MiG$pNp|Z{vhFIP;~v^yl)@lDJ|Za|s9n0@BZM_vXLA-P=Ef)3^UM zT<-qqexPLrLM|Hq%m@L+MIbOjSn4_C1vwP6C=Og?79s?(F#-0vbz)*-Vq#)qVq)TJ zfiu6RN@(o)P~-vKVpatLqS4otE;ko|=F^?Q=XW^$(SM7_AN@6?O9}E9W83D9#Fsh- zLOctVcmf_DS3GerhI*wx6Wi-Ek6#}#% z@239pfNY4r<4PgW#|0BHk{69mYZv*##7U%WP;qv0QT7UyEn1P%#LXdflQkl*&Aq3e9 zLPj7#e*|J|mb?HER{}&+jC%r3z}wI7@g(G+aoIv(Ry6V-9Qh|h z{(++SQ!O;mEW0D2?%q|)0Tl9|+jf3Rgf;pW>d^t8sW3=Cg zRs*g~T^Zz_-8gVN{8GzQk5wUlLF0NTbzH5Y*9uI~Ecv5m$ZazTpHLOEMH!0{fL-AA z@ggE<6rBiQWvHft6!t3!h}Jr`%0z0KY>!HM)eIiZEI#F4`#ucNU^Vnd=K6#CuS$O~ z$lDJZL;AH${u$Lr#S9GV7oyum*q0;$)eWEu3ofF8cUuMqm4xVhAekH_K{i4Fm8uvC zR%8^x0tgFU;*${ZCciCt5`Z(6T>_%iKcNhhMf>*);PH3x;_-Ko9$p}ae-`}W&mq0| zGsy8pa~BCkB2~6LEVe2HMh2HjQFbuM;`vk?eQpOhF)=YQF)=YQ@lC^Kv-Mo{a75-} z&PCI&;qG%bTcu?n1L@&=xcu~YaQ^)7;`H|Wh++*u5aO?zT;(1nYFu1^a4O~b;%E1T zLYU<Xh3&HE??gZN(Tx+fOLMgNzy@AgaYceF+&6clge-;*#It9Y2+}ON^KaaTTc}qXsF2 zx)u>Ymq$eUI|%2$19}&c@J|BkZ-VJNK>Q96e*;*516aQU-2VwM2M|KFHeoP`2_PNn z)}%x0H8C+UF)=YQF){Hqqg&V;Z6N{7626asvT*(k{P0J}?|zDO{yA{^6zSJFP3p^)2#DNCgo@h*AuL=3ElGq3SvCJ!aIqjDS1^qYAcBC> zub<#`c!5)Xz=_V~r8*TelBjjOC!{mlppywJ10`|=eIK}dA4`)nKxRc5fiHj*HvuX# z(9Yem_n(izx)+f(-;m}g-%&oJd%~5pR+MMo_@lDi`uykKdsIgpT)VZ7{`dQoeJ_7s zwh!$Wo3G~moVtC;GjC~=Tl4ex0N2}o^bTEX^Y?}O*eSGUf8M{RS*-cJdpw z|8wRapXd9G0iW(;%-h$>@|-oo-uVx$)3rXn<=s*2y+a@R9e(b2^{A}x^Sysgxo*u* z@4K~3*V^N&j-y#UEoF-!dW}0IJn~zloXU;_TKC!r zMd%PR;lcnB*Qg=dCP5!3H&X(bAFHcLXN{tgt%O}4o(@**N2X!z{W^xV!Tj%N(!)JN zCu=YD8;KBkklu1xPfq{bI3^M1`9p$K8wmw=9eTMIQUj{9IHfgke*`?3u$DP8ClZ`mo z{oWhrwtT*w^|=>p+s)_i%QI?g`%K^Fn%Aw9ETA%uN z!sqGR(dTX76<@BseEm3D(7%&*E<9?lqj$v4DYk7Cc8=6>Y17zt_hbE9-oUtB_m20* zyFJ#qd%^gvzOH&yN-R4%sm_n!9XzJML{i zS?u+heTJ`-|8DH{{b)W1`gd;BZuWSTx6NympB>MmIZ^M~Ywx}BZp&rccvK(1UbFSF z&-V4+n;R|kIr{w3{4)9mjoNoFt;@GAtzGp9g_Kf2vG^a&w+daeGZihr{EHU=t57E< zoWET0?jho0#~}f_f7Z$Xg8VYU{U{UGg#b1_|3uQ(RFt$)GnD7q{x#%ZHQVr82cnt$ zYrwza-fc4SYf#_Da(<|JAE0}_O>ge=(}jITch1kTKEDB1NN54`M~d8<&bk4hMf$fg zswC+mik@^+H)jD*9$Ly1fXWe@1wcy{G;O=l)IzvoCFoANWqJF?cLR`UktXXw3;-py z!HpOjD55?CgUX{3^p1q19d{hcbl6_MsP^AJcQin}a6n%=m0H;{j`H3k{`#z1|3Fpq zpH<4Hy8|TZo)M(eBTBj|G)N>bFV%V=mlwZG2&Mv%Yi2A&A}qAvHGK!K=r!UcAO)0v zr}9r5DrZK%lt6{)#NabG9-a5Bndlw5jy z-BwwD+YhdN;ThYP46ckjeO}9_f3Gb*{X$9WY(JR#Z?woBL0vTTpB4StMTvq;nv1!I z-95IP_OG_RohUCR&FGnX(lAYL7lI1Es5*lGz>an#vCkZRl+sRGj}fs4T0d(0*mm>% zViy){(z82fpD`L+wtS08>(6QVV9E#dZGJR4jez@p(r53@4gS0*eIBE+sL$5#k$w+GbK|vg_&G>_ z@69ozvFooJH^|NUMB)3+AN#iU-+4b*kMbJT-3z1rbNu@VmtM=O`INMJu{wzZJ8 z2tvG<-=c930Ky44zg_U=$1m{ZTOt2hmh)N2Jqr1!P`369As}!7alpa>F%)%iNB#lC zrE!i?#ZnTIzeqD`3;+YG_4i~r%Q&-Ks)KSXcHS11!S$XYh+2KCpI@iO6o5U@`nY*p5O!IJ85OOV{BVS+dWi?Hty{9`FnJL7BUI z^|z$;^|k4{*Z`SoyKV$2ndRB+z7jB-gUb`C=!6BS8QdSi&7%ufKs;{4Lz& zdt5?s0Z0!(N+Jxn0C+;(^&zudI|D2llazro7hjo?!j^XyAM6$YHJx7oj3(s05BbMh z0E{#bKu;WHqOKKHWb_Nl(E`B2ED+t!BwoZL0L02qDT^_pFb`7}|o@e2UINYQyn zkL}|1;?8T{UdtwV4dHf>YOr|sYcHa08U&x1t}+y3`|Z*4*}2cn6S$P;);72AqnR)2 zJA(Euv;DSeKaA#nnPcqy$?iE-QK9*1*=(EZ_}4sbo9@k-BMS`sEZdg0zx-Ojfwhl( z5<5Qaw^;Kx&+htRH1F8;iat-@AFa<~zC-@|A!XQ1XSpwPEr*VI->=-hdG&iZm~$&S z4dI|pFlE+qoA=$WQ){|dtJ5{FLY4We2m%%e*^lyf1>#xA-wMPNfRo(E0dK#2fiHgk z5^ug(aZ0G;hN7(3h0lWsB*Z?1a&1|Qseh1we?*}p|GKqV3FJOlOo|Iau-n*GYwnW% zD7IP4$27fpOkHL%n!=QJNV~Z=!jXUTK17#ZyU6pzdQO{S@C^HIb}}UDKl{mFw-9cY z&~vmjSa+nmGfVw-cBeLJ+sbKr6$Hp6f=nR3!yUcCxA-^l>h9aPqnAi1K|&G% z>8T7}JfSUgPDS&dWMRio<$8L;*2jg-$Co9aM}QvsG_}YiX-<+}iNwYI)k)ef9&I|) z1YH`9b)d&uE?t~jKf||ZA45&2*DRs6i-RZg*FM)Q9_sOfsVhwWUK^6pFY;_Y`d1fo zwq7d4Ih$)XKVPyKK?m8z2HG*1s6^q!0K3ptC&tt^7R! zbV6Oox?OwvcL3;xQF?6ZK{Ef;U1H7q4J%x*^|xc@Z2B&lc47dbe9z-u(dNC&i=XyEMe*U!ctE~?KWXG0}E=6D;+!@9lm6&9CbNZBO*e7?Lqcb__h3d;B`#(adWX_x_;t zU0v>R%d)GdzP~yT*l&MEd-rv$oh2XpJvMzr_P2k}AJK@ZzDs`2(Ye#Mg?`g)JL>u= zw@1*EY14B{-z^n&eCYE}W{k-gS8`mZ7+G78jCX!A?QZiE;UP(Tk@Nw4e)`OdTZh=W zC!rU9`*n;Lg2~^yW(fT~tn!#g0XO74Mh20|c&J1WHg z0^kzy?u(cB;tyZq)1TktbPAQrP%IjJ;vzj+L-F%pf(H5vWs5%!SY-WPxA_B!;;*z= zOwD{hn79G0f>h_Id=X9~L>8GUUpbrg-q3hM-HUmv$*%<5t+PC_@A4g;Gd$66Q?F^O z24DKh^G92MFC4Jn%8@`pdF4o{t^7@`C@fFiSkueJu=Mo83Q6;Zo14mV8Bg3*s$3s$ z1L&j$HLHOdU$YtsiWFHg+0|h0-D4k6XQT2b`3^H=e&i$Kg9uiM4s8e0(bf~gW81EB zt`1UmX?=OJZi9zzZ%oDJSfnY#HY7rjyt3~YDM&TxiEQqX_GUenl?20l!KI=`nh`HR zlAQ&J7sRmO_2ng)GBVCME$@Jwk?sJTw1Hi8U zS^*A)e6TvnXuF@gQufXA1n@oDLT*L*Xud%EsNR_y9ss;F_iHog$4?hM(cH`E7RoGj zqzv#olU6?q0Q9Q-W|I5w<-@&}XYw7|f;gfs zCi!0Szn3~*`QcIqyg)CxS0ZqarXkYu>+es{g4k*s!S&fa8tlhM+dq|O zBS_ndK!)zx#~+J4_bku;r2OSohP93Zt--L&DEa7O1=8k9WaRy0JN8$phm@mh4>D!CZ{w`C%}LtE-n+JTfR5ou^j|I^d31-O zNssLr(089v;*Droboxt@zoyl3V>M%1{0o{!SO={Uj?ule#XEF9u0-a!n%4{I2OZz{ z(syUm7K%H0_r<@owzswkOgUt{63bkf<*$t0+OGG~j`uB}Dt!0^ZJgbk^g4f7l48f% zoyk}0r1P2;EJ*$6V$$gQICyt-Uevm5o-g;+cl!eHCupRLOkUczknyc!`%cC}K%Il? z{HA1GR(N%c*zu+o19shI$FqF9te}G*W|o`UqI`^?18=Bv# z<;N!(C-){_`?)8yZ=qQ?u|5Z_t*WJl_P@??cW9(g=5Km|s>P>O+VB(eowo0t&X?@I z!)iq#W2?5iwl~@}TBOXO<+=IBX}+dCt6;&5A9_*h$I=I+^M>}%{UC2$bLexVY%J|Y zgest6wCCea`bW=MA%Y##tF%Wzo9oTHUgsJ+R#H2rM=AeWzL9uDnZu&gQw4eYjzZ=- zKNeBy%PFPDX8Fzrpy>@J$OntR(Dr_dSHJi+d^_!hh``*scwVaUeoji}N z5JoLE&TWlZ*GIDc#LM!1LEg^ge3o(Wh&C4p;jjJj*7Vz5Iai0dJ|hEo1>l|lW}M!x zIK5lJ7s6R%3v{s$%!@)Y#H^pcDh0^|`jg22q&Ec=Z5>ewKnD#tS0eZaGJ2{mnx2Db z{?*st!vC)1U-KRCHG3@*`api>*qg*#GT$E9e%ti*9-F7jn^k z7Fh8>8D51Ja-&y}R;9E;KBUFkkGnZLXD(YsW{CE=Tyl@kz=g$W#N?X+8jydjuNDR^ z-%9#B!Gxk$e9eJH#FB8hm<~0j9rcEamY7r#^3xc5@800V2o!Gs~_3 z)(zq)a6}gv(4_6iJsD{c!!(X5U;;wq+DC!3FXSsvY`(01^r7~FlHnwcL5PSs;3Z^b zf8rvR$P92o4uy^IDFY!PKW2m%0G)tzFA}da%8d4aj(h?#!OP;&w{s;?mh9W&uc+ik z7rS)<#xlw4M?%ikCb-CgP4m70^b+7VXdlTJXc`(Nb=K_6n)S-Uh=SBfKbE?1Smi^h z3mkTfLT!^MA6i|=sRoovo}}bXKjyN4VlkPdlj+?k&(Jnr<)0zVd;;h-K<^|Evp5FO z2!mZ{>Amuyt&;YarhToJLl*WbhpOkayh_>)2_bDKH5ZT4Ke_-=;%*l$w(d!WyewV` zB|W`q3)A{aXuq9I8I%m@n3X=G6<}QeShIQ~kl`=n+>5r*(0734F9lOpX@|O4)i%^G zg4G~qNvDf!pa~#!@$*m?QT$rkDWdKgTHh(iIMe4?L6(+7+O#fM?QgX71OQ*65fdYr z!iq}V7%0TsYVS0y3QB-ZYg@;yKL1Ne|3dmt1#(s>qtL#)pe@W^lxL}MrIu5_CFjz% z{1(l$Tc7`>jCpMwom-TsnQ@3!%KAdiS+nzZ(grU~UXW)isl9J`Rg?eNk`uS_N=lNo z9y$)9j1g@ET?lKtF6OxjP1p!oCp^f#N$L{JJysa_vgB9Ci@e*Fu*>3{?plAtEALaW7PC;9t`@@@H+jDe^2y!wvoT&ivS67}1nxKvA}4;01;YN;f}V;B6WEq=X8z~s{^`kZL7j> zdXzD*-#(ppUYh#p`>1}2I&aB$kQpFZ`|!PtrB@{hol6;6g%di5!{q4)*QKp9YFTtF zd?DpD=I*t8`h8>+!YXrVowGh^`P4c4OZgtU@l%DoXzP4_DSdWECC$XrmQ=o1bdO4Y zSuK%r`F7O$==V;)7xz+U{Z8rlM`_n8xYIfLo#drT5?upGI}^~{i*$a{zjmy@GT$*q zA>cY6zCyFS(RR5r-()F=u9qrwjX`a!k-p0(sk5%PVA}i<;HQWbA~H%Kt;@M=wTCzT z_x9|Gta0^NwJwBW<_|y#zOMNR8u1@Z>n~}eMBtC2{Z~?&buq!Rm7NF?sueXPTD?(1 zdQ0nOMbLAZK+Qt9l-KkLwJD~8+;7_!p0t~LEXT40zSFd0paAv)gdM@ao}&2(wr=(L z14XWlY1ft8RzMF4%`#HjHmYJrp=&p=j1#W>k$(0!{>AU5^!QuL@&Yj~KwJ?QM#N|M z>_?>pYF36wxYGef0e7K1oybU+0A%f5v)>>HPK+Z000zMQevw@o8JtIaSR13;CPF*M z5&oLjU;ueHfz9B6NsqpR_M}XHK#v3oRU0J-PEpXE>#i9g0I0l>`oe|C$n4xN2ngh? z2O_G~KzkO~cmV-Gu3~7IHrrh$wRBw)MnKuMK&6VEun10~HIP?yAC+s+g-qNzH_GSM zb9-g+b>1thwHG?7%X9Dd=a0(1m*=Q%d+lOBh@;=#3pXe$-1%GD&*$p}5JzS9d5p%$ z=w92NzRXAE^6h*}|6J=^+eV|l^kqHj1CP-&{XKflw)+u$8Aok?OWF3yden!bKCs^e z`*)Q0(K-LzYjqg)>-)|@_V?a9GkTwagmV&URv8$ z*ZS+)H+Yn8@0=eaKHX@cct}w5wPt6%o&sz@Oo$cyI;#rM%(C z@#26Osi?wvJ%=pf846~GuXn#2U*+CEt_wDRT=UtHxk5B9yT)b5b=m(u|CgElcl(e3 z`0qBxf0*sV_Wl1e`)~jE|NDRaKmX%@>SO%iRGBg1^Y(v!hSUj{k0Lb8J-=_^$iZjs z>afbZ_&zg7X<)90Upv+JQxkWy{Kx_X@`vAZLA@%RxX<`_|8PC}1A2DC-Np^`noL!} zKYpfrUz3O5&-YvDK$!>RVy=;49cHK9Nu4JkpOvpWiD0MMX2a6{Mt^0`$;)W8?4Lp#OmeK00butC;70?9ZD}>bp3!o z)z1LSIaIiKpNd`;VDi1o^ce8AEQEyz zh8^^Yh2;<7qN8z89hWKW1LYMRT*^u(T$#6cFrC>)6W(&%IA~!y{8I%zT|XmbT{XW#KU&k0<|@A-Iic;H;1`p5BgT+tqLQS&Fu z8tRxzLSTKR?>s-@@5}0;b!u!JfA-II%Si-f$B-BP_icKvyWl(0fb%5tOX&l`VV&e_ zNDJfZ^8wQ7_&BU@ah>fV`yE=XH3VVY|b8Q#y$Ea4h}vn|?F?9lVtuUhv28 z{8Qai_OqMY@E+pny6ofhHEzGx59TxX#m9_0+`oPp5Zm(SHpbzHr_ICq#`gUE$G+Lv zx7%mGZom58x-stO{Ksbd^tJadeAa*5+ra1V6#VZ`ypZCJ;|~72bKAB*MzF>oVMf!D zw!e?(6WM$7B7f(Y3WNW+*Vyj^?uQD#*?OzEN1TRmFYOj#JgDT8>pyQ;u!p~#BsCrL zoo4n G&~ZnW`L;Mfv=y|uZX$H3KHPh!pAPT*!BEF%k(3&^(T--yU*PXMftOF=aT z;)vu7sQo*x=$lplfI0ppOSF}s~GP6ao)dOH(jijC?c(dID#AhqSmYd@V z_D>whJ@2^Kh9_g5Xd+%2n9#Y*u3_6eYy=YvkwBP+$N$XX*mb{;(!1b*#P09Anb|o~ zf@Wt2z^od0gWu=?urpz%9Zz-u@EXGY!~yWOd=2sQwtuGsfS*5g0I&})f0YBE#_kgb z;3q{n0P5af=>Q-MrXAaM2Hf>O4ZO5YERo7HNfZx?Ug{#BDx+;mv`1^c}a&MNCG%DGs zit9GcAzXZnRbq$dA>^gc)yNMVYphQ`SdqC-mXLo4i)|J2Ny!890%h5C_Mku3Wuz5t zI@%GmkF4u#5AhnVo;%JwpBi05{Qa?ugYI#A?w@5x`6h360r5f|TG=|Z`Q9FR&2uTw z0jtu{#rN?sD(_3ddYel+zt80YWh3UPr?cTF^Pj`M1|TwqJ&R=7cOLWaYlG|C_W0Ig zGuvm-Z+BuKPWRvLo!R!iMIyltSoIIvI0F%g58|v!ND#;~$F?CKRX$7~e3t@yNZ9Pa z4*z~Yn-uh0HWR6MoJV!}eizgh@dbHZxw#3pS&}jJmN&D?&(j}igZ8`=9BFy3FESfl z!Wy2d-RjnR2)jD~6^q>RJO7RV)XOo0S)d35+q5;@gXlSEe7N3z%|9O~NQ_|MANIoQ z+r!Yb+AK0YSPsO+NH7>TYzO!sgkzd>huJlQ={OWHqmTvT8g{~K`aBu%jG`MCOM*4u z4ROQA4BugfiG$5-uG&u+ex&hbwq4^nGvE}vKQ6Ov*7wH|p6i*O_`c7vIqt_w_H*3d z&)a}GqjnL<;Cn;k_k@QD7c6ld;JSS5;7r9^9d>{#f`GU-zkB_F$2#S*vS;JC`?Vcl zeP#Im=XL#Z!lf}l`)<@gRRZ{cSKLZE#dp7^7OlGRg0qkV&@7L><%xxL0)L>w#Bz$b6T=@!+}BwWMnTCc;G??mjQ9Y$`Jot|vC2 z@4$L3@Ics-C#onrq5@IBu57Z8y^jZvE&u3@4ca7tjc-w6SV=F?e!{f`7lIAEAN)do zpnVkAeO#g~jbE{uULWZIdzKbD1bd=B(!qLT0AKa5WX|P>l^v@Ip9yVs&`-WM>lJlf z=vH{{0w2bs;H!4-@x0TFb>ERm-b7J$9C>;SvupUXv}XJ5Pk-55<@e9IL)L6WQXnm) z&45340@xk&e~lURZ`(ZRZ%ZrwccSc$sP{2o<=E07V0>`buQ5gX4X^uC!9FJjuDO_) zF3&D7&Pq8c2*7y@f3!h6j_^dmMd1=RUm^!a{T}LOP$LavZJjXpb9GUMb`HP8mIbO5kLG7>(!wPs`7VqrDc4{#x zIhIpF_TB6_p3h@m^ZO;S4*e-y4fw=3m%8mmsxomt2$?h+;g{WS)L@(hkM`Ye)9|h9A`jFUq<8irF2p%E^e+7r}D1{!cVcVc!t>h)) zNxaATAk7PGvr$)|t;A{C*{&%0d$V6WW`3=OYnr!V1JDkA&H7}MS4G(}oa++qz{>)A z%BKhAZ@TEE@S+(z9xIlMuDirPAv0Vr*+$Vv(i3(6jbJFs4#(%jRoWYVY_0yRdW5vk z`M;}!eRp`NK7uB)CEZ-jcy+q|(^7S2MyO*vZ*-WkkU!Udp7-P1=Y0Hi-zQ$9`a_$a zmp0~O1H0Xcg(x%G;DP?P-H!vzE$8RAS*-X^^zclsQ=IglUhDn^u+7R_%F3aCyWMXO z!1xN{vxwgy;b+c6BCC|yCh_}f>YAQYcC!=%w&%WaU4%>R;ycDW4N%1|e2d%ie0+>s z3t)9`ly+ZFq5z__{i%Olb_>~+kr}fST;RY}UQ7hU_sfM003B2Kjdl#o&logd^6u!m z01Tyg6EA9mhfuhXDFcjkQ}O|YR0y(JR#+4Se54@m1OBN#4Q^T`7vDFtxf9@S^aTIU zrvr6`Pl*;dEiff=~r*YDjS<7wxWFSGr;!Wk}x5E!;i z2f!aK;es;v2lBqD^H*hf;x7okhP2V3o zjTz1QPQS<325{1E;6=gL>UiylmuKF|fecu)-cEcCJ^;Oc5H3s`^TH2@_xrrc=sYkQ zi;^vcG1)HA58!9YyaMil2N`WFr?(;AAP=JMVfQ4A^6Yd2zDc$&;MbI^Gzla0!!R06J53>{dFpE|D;G@yl2Qw|{6!T&OUBUj7TpQeTIdr}P?z?3i z&9cl#|3cQhtwDS5ugKl;oQljgz~vOj(s>i`aSWiJKL8iEfg!sW*a&;zswg1l2#^)5 zpXQJwkMp)a@@8~D^g+nYKKl!|0`Lj_aoGmEaXb)bgX@K_O5U!AZvj`>gcES~xdac- z#UxzVE`n``-H!JD=$Dy2yN{^PVoQw5VMQj3Z6-T`^5gee$54L)f8;6PgR)+1$!@Wb z;4=wl;Jcc83%@=X&aP>;m1gXMDp32;7R_ zg?N*`1UYC&Ud>6@eJPW{V zp2B{3yl}7nZ4PMM*SDDv@XLRg!6AM&<9iIW#)Htje$N%pf?Wjh`-sg5UeuQV=nJ1! zH%q&XRVl&$e4IC8D$8-2ilO2@Ey@~P=feb}aDu^VJ->#F$mO;2w217mOTQFLCj6Zb zMm_}G<2Sz*8`2cCxn?de0~T3_se{@#wF6+4Wf(S-0E3i#vl688ZJS8qyCe;ivwPhg zPGq>fh%+rwt{QZ^z(sJ4gLdm()Qv46KnwQ^=-=%q5#WNvS*+cRUw-fu!XwQcQ(1U7 zgxV31?$4PejQ;oYc{~2&Ue%|}-|f_od*Xvczy|$r7k zMYXCHmv(#X0MQyNT-4qy%e_05Xe-Pf8A7=`6tlZN;Q3V7DciLY5Qnh29w4GqbWIMfI~I4p*kjw-tB&BYw=gfKT=n z^_^{nnLVq;F4tgFl3il^z;nt`@lZB}cX%Dg#KCS~#>dg;)YI>itwTO}{d5Qz4+5?X zPdkBhvkW=!G3ZLN2ekP7=C(i0S)h+T#=PR~cI*7kw1pm#>?c_jwhiy$I66-c#Xzgq z+?GcB>5@sggCqm;5$B|YiwApZPBPF3g)hq%+*A4zcJ%8BX40;dqt3z6&cPp#jNon( z=C9wEcH8R#T%3PPMQ*p)+It;KA?X968t)K|98CX;(C$g)q#( zo^w!d&G^zHulJGqg@8U5Y60&Z>)~)f%!f7{ao~kdGtK7|56?cj_=(5jJk8>r;Z8`2 z&o4XAksg)#I?dwr`MZ~=#dTeNew!{=egxnTNJ?#Aj+8yY<^h>~She-csuHln?A{{J zK{b9th6!=A*|ubMEDMHTPtP!M7ReqsI}5RaFgdG~mh(EAAgu z-$dP@fpFo~oK4g_Rh#RcoQ!b$!zxX_BL`ua^T6_b#t-pQFm|fTbajRAJI^DdxmjKf z$MHDl=O$L%fc7pyrB=`+e%I3n!oyeioZR1K_6KD9S)Q@K!Z(yZ!W(6<$Q0nP8b>A9 zJhoocb$>IBZ*jEQ_aDq)?{m_V_yss&GSWzY5dVb31Q2bZ52wwDM&?2Mp7Q_4R%IdD zPFu?39``{;I@`8Nn({n=+ep2E&tGWEcJJHfs@L`4vYDk_c$?WXZ~GXseY_q%Ab#R?(-AJ+4W~TGc08nQD)^%hcnA4_JzOAGVj?QI1dVK<*Ai1Li`QT!zS4LG^1V9=TRyujXpf5bY@^oBhITNZGq z?lO%|Tkr_LsS>hy0!~}*OUew{>=?VtlJ4So{IOMktHsCrCVrX11xoQ{hWCA+lXU=~ z{k-fL)5%whgWl0FORrL&+%kiH7PTTys9>$sTd#3{|N3p|V zSnSks*_Asrn2@e4;`VtGC7uD`g-p+7kpw_wE}xYl=t>a2vr#+Eo=gWcxSvhWJ)^>* zpp*ksoar9a(+a%fAA<+D^3DKtIF;EWSh48s^&R&79!{6pv$F5^FM&HGH1HgNm~6bi zG45yWP4h^VdlDaZo>#61@0|2k1>OR8RYAM#K0Dwv^CW~Dmeb6($lT9{V>bG>{C-Qo zHwo?-Nuam6&E$46;86pjt)>0a^~&~W0i!eYi0_-T&h#<<-S}~kVN`#0P1R9+Q@Hbe zRT%e`+?5IN<@*XZYoP)lr z!c_d~Sx% z--{j(({l2H^U9{K>^%0(McGx>0hDW{$N0(iOX0PY-%-bq8acg<282;a8Zk!_l=R> zND=eIF?#&AVJ8NmGw_ecn~srJ_&xAEG7Rreji*0;6rYaQ3?_J0+zbL`kJ-#h;E()B zi?n7raG!;D3*b)gQqROaN(QJzgW3&)IHkH2OD_E=Z!|1DPPCcX%hMK5LsHcHDU)H( zp%YxL5YSsM<5__%S+e1$1042L#&S^dMn7 zd`o=@C1k|>g#Ks)pXu3t$SAoW;|_y(iymCtp>5z|mXZ+;qwHiD&c6qVZbDS0Y}-Li z8jsUsV?0tj`H-I9oNo*`C%k09=c@nqWwssniKS0sVb+0(82<3;cy@A=(MEL`Z(qZCmIiw6{fKinjh4B zfV3ez9G9-}{Ov($H}+nyo9Cmwm|vAYY*zW#;VkKadDIw=J-kUiiqV*kIyT%d^VBT! zK(#CS05X$aMb34geS#}=gXhU;dp*~c%q!b2?dQmR!af4lnDoR6{m+U0CtSN1y7FoM zoBy`lZlCw}dHs+1J>E=p+}BPxr9brJ3vk>=;tA4g=j?O(=MjK@TRLyVV25qztFh0w z*}XG?SJlf(zgTotEAhO_AG^0PK7-%sQJw4goe~C@#aq6-kM0d7UGSgi0K9Q`0Pcjr z5y;J$F)v;nnXbHL)jqH zl%9jYc1BLtp+g|7Ic~DNuyXVP3?BLBTx;cI+@fdl?q-;=m)T({Bi9UX8QuzlN1 zDgYjSZ@xk>wwOZTz<~hM6^6(S?}Wo!A|K;CuhW>uWG+^GN&(qHK7>Pr1{Uh3$%yF? zcyTb6mWLXWsE^)Q=oF?HgQre%Mj9yd@+(QyYJ!Hgj}HfDE=Xy z^LyfjYHz?(;sqvVPU21dBMk?yr=*$ z$q4Ep*yc1ZS%sT|IG$giUkuVhMu0cYD>+B9owi3-*!CblQt*gl2E0uCXW4?e1IH76 zSuC6oV0*>minb~75AZB@ANZ8`)!0u%elZ_d#sMeP|C9O(nD)q#*F`vHFL3_vu0V&$@ZoBjCD-ViFh*IjuVd|CP9)6o#Wv>95Tp>lI8S&o8=nWRJ?9c^~_%iJTFjqFl5Me~tkL*($)s$E;(qbu1nMhx+L; zYJ(VVJh2(WL0ml9klx~U!7c-!CE6pdPj@A*wfV`1h^|V+PTyi)MOpEf2yph|;X8-% zUsUgwnCmnfW|v(L{N~};0k=aD=rqi9-Ru#hgA6|t2ZUhI9*07}_dt;+RK7n450eN8 zPqoJcLJH3DzCHK-dlYKNk2P?TA-%H{J~sfm0g@_<8wnS`KNA7>tNAQ#H?d>(0eflu z^?wO&nsi5IzW3vYkNBeim)SPY2e@T|jLX-9ZzKnd97CF^LEgsU71&eMx#g<-6Cx z`wbn4uTi}OhjA7CEBqNoNdlO5RG44PkAi2zo8s*HcO3hn$FHL|=mD4~^9xGSKgN~n zB}at&RB|o!Le<@fpWY_vDbhh>)#?Y?7uHSeUpjfC)|awM^l|%wqxHH|oB_+Qi?b%$ zFzimo%GhI3vd!N+8){l^aZK6qVY;v_e~$pBpfMTfF;2*(pZY;W9VWou>3Rh6P_L|>UsX8b8Gb;=gy7)HeG2Zcr#)t(l7QIL53cy*`2#AU zpfbl^9woWY1~3zb6H0NwwqfV@^<)!a-^^(S*B=A5?N?bba6Hd#gyGrf?E!IkAeS$D z&Jak3?qPgfo9~^C^CRHR25|_XH@ZmC23&I-k*8C^QUPNV{6s~9wwUEcSx?s*n8gWW znD`rUD+WH}(T8$T966|;oH(=_By%eHvH>i_{WR*^8vx#gAA}#)Z+UbPZ5PlEc&alr&;i0e zm>(*TQmdG;n+EFO4uoPuP*%w%EBQ>akGwe6 z#PP#GCSuc}6fUv~;8T}jR8Qe=Y%;uz zXIyT&M|&{v+wWtHCwKTA&vTh)@kJY+ORx{8Z@zl{eguHF{NJw= zu#h|n>$1o^AaLbqq^`4&aWwiog*99#YYX`%>;oP(E!XqGrzdeZj+8bUR1W#aZJzWn1J_X zH%}npQ4%lQm!R2U%3!OxA7kiv&=#>z;3~c2%y2ruv%_ajerclPF@yqcifw37e?EiA zGQ+ygvXz2>2Ja#V{=DO2ToZgjBTmX2>MtZG3*TsEM)3iE7kR?OXG62&(`^k*IGd$p zXgA((GAiL7R^`F#Kz*a@QIrE~&v>9M>G-!b@|FtlO4??De}{X=zs_c|j-ae{_Mf-$ z8p8R&jwxA|dPzLyI;tN7z3S|Jl9Og$L|o6K!a&=ZXk6f0>cBPTb<|y~q$+s`+avmZS!!ovWzY13WL*7&()%tnY4x4Z4c?jVlI43@vBRFIH&CK8s zz$^xS2%yroOJGtInh;h_h8q@iY!O~O&w)aL-ywU$b$B)2+r@Jpqu%4^Ld>e*;DHj@ z#*B6&i27=ajNzxq-XtEBjP7%Rhom#=_r+o7e7-1*@z(>+5BEb@=P~SlD}g<$9FQsY0g``F+U>jw^SJ;n*_-MK&gqwP#VAN7wsjPXQ%asOYLrjS*( zaY}x_pSGB6Mm`~(B~i+D70aIQr@0I4c;K*SI7z?Uw(M5uK|51q7=EK{a1tT~VazwO z18AF2HoAGmpuX+ZUqSnVJanEU+R%0)54}(JXZDO+Is^{u(P!|&aYub(m|oXq4(1>; zOS=G)z1)F5gpKwabsTl{d$xs3=LLN;A3&zqmp~kRe7J1PJG7~&qn7KHWQaYyNr?`# zNNg~yr?E$|^Y0qZYYkzAKX!zSr;qcpJL)~>4U7>c-Ka-4Y`%JZ`sJ2!c3SvPB~ap3 zgucLPPtc7qkH=dWpW)7JJT)Jmug6EU{ouX>>p>q*o8u5|N8Y6!2L=L;xXT;qa6Ek+ z;%tBm7JY{$8hO_Mmc(Zm7xY0+vu79Gm1VX|TzLuE8Z$n>_+DMgCv!H@o3CUmn5*7Y zaV6hp+uibkQi}{8U-xG=I56ka{xL{Sbwxmq87F#-J>NI+90(0OuKK%hIE?am0`ecS zVU9}_%9uGVX}3WtPbX#aF+Fr~9rkUdi3%M60|7nEHhf?Q_SFT<3qH|!%{uYyl4rVH z=`jyw*JD&gmYEGLuHJF5*{!-oq5B-XC|Tvh4>=LBfsAuTnI{p36EJY%XJ?SQJfCc;f5|r6T@rp+{85#JoJ)+iZ2?U7v3di#!fINfuK8Ms12SDf%n&NGnIDWu4-2=VTZr2{=$KD`U(*xX$%dUn!vh z$uVBlXFC~nnU`#PP(H+N3oi&0@kHB&cw!=t`{w{K+EGEhJI(Sc8|ZjCF4+soMbWe4 zxsLpkyN_6&QMQCl3;f|;nY(CQlrD2}vGBme*rJ{Ls`SY*nQ$4ul*Ds7;QX7i+X&jG zqzcJ4+Oh3B-;Xwyzo*0r$hMiKb9#bC)T6Jbo?OvHI>=6`U2sl!v1jw%X@WQ1d#=Obed(x$F?MDERO+}L@&^t#$ka&^3w#piq061k?SD+d|k>p z&~P@_5he#QW$eo1r$iILa~dPDOV?r>{=^;cm%1otzRPMd(>ah>wCFz;S5&z z&8$9_h~F_z7!#hC9me$AN&u5=@A(A%j%V@P825A!cHBZRZ3e+M#5KO1f1;8O(u-IP z>ZV}ZtUMNwc7Jnh2mMU8)bj`6-Xte27Td6r7$|LhF_w2W5FVO;j}47$klEP2o-tQR z7cb{^J-nEY0LWePfJ=%S&j**LTy|k}-J~CQw}zSJ?EhvdlOZ#eP?d3%gJv~o=WCep zDKIz?wPBpERJ&e>@!=Qi$x0?qQO z+H?Za#EXdfPJ;P|iK7r1Qivqf*YlZ3@tp&HobvqJ{BxhLSJ`I96zXofXE%W+vwI*Q z>#4G9GUJB)u=0^%ege_asl?tfIr|brVej7dfV!ydxZz<7x}s z4dlY=_^Yun`mL^Uu!VS?20Ek0w1FFVqjLyj6b`gyn$pU$f=B%-T)L9@_f=`e_PqM+ z_c(CkU{~RTE0-t($|*^O$9d5g>Ya}pwo5wKC7%ce;^6m^yf$^XDEW|fhwxQ_H3#U$ z{*!Ht00ZE4v>~oYXVERUkswE%Ep|FMA*tpM$rFJyu2^^aE^)x$&<6$w@!7cm03ZNK zL_t(yX^!Av*+>RV@R#Q_mU~fF0Z+pBo5m1iz0<>_f8P)9rGH^2lTV0`8l%t$9xKLU z)(cqQ1QT&19Zfa>lNy2+)?w8BmCX3>qIX}lO=$-ndiMeaO>&PhToo=PULa0Lw$c~I%$}VV)7Lf|&+Gfl?jZjgbj?@!F@bdY zE`iKXB{OKp(Vib;=-9_##T{1gJsE(({0`UBA?yZmm_6~B_l0p7$4a6SB#D8W0FDeh4anKcy+k4UB z%X~gy;QofD4Ctr?mn_sT4nXIa@G5)?#IIT>>WXWTp#mK7J7%Of*p96s&}TYv5|RzP zE1>L3P;f0POUi_KzZlPwG2yoaC_~Jp7yvxk$UB}HS#*AIoDy7ku3JS`^y%#2Subw->U;Iuu4*v|R6P5jTBFXwUJ z^TSiTt!?h!A7UygxSszco@u^_KE(w3_i&s1W7vz=0k7Nrdn@=R^U^GB1cu&Gq z93{sQ_{j1R@M(O5P006|AvgbSx=lKd!s%2-g!I(T`MK3LY_vBM_Yn?`Emrk$cHTIR z*VBf1U3SC}KhN{#@wR2!PZ*5YLUvB@JXnB#Jj)1KWPHRY>EdP0AOP=C_x#y3)~74; z80|ngs0(@J?ah+Tr}0oI0uRxCo(JgS3HMzl93ExU(<;uev|_W&{!d#KvS8DeY!3}# z4==)(Wn3ka={k;W=%@ohp4c-+s0(OMlAQvXVBW_yf{#ooege}qabR2Kd_X(Rc9HF! zDjm+)9`Px-&Cl(6#tUf++c#}P(IeE=%$jU(CvE9aHE)aa5wtII%QkOo#)s+z?RedC zpR$ln@_~A+-_EBEY&{=781;zv2D(&i6Ua}XK4r(@o!l??g&;}3kmn&!Kwt3K!2a$J z-X}5e1g5fV0!hjCbn|5NPmPgw*0nT0?Sl8fleC~md}7}%+Mz6QFa`9)L6g&Vg-wEb zsR-XH{33!K?R^|*PWKXgCHxQ%bzo|tyRk|9n%6Ryj9Gb`{rD=r?--zS1AIryX_j`w z-m<5dEykAa6&pQ91j7+Sam;f)aZ39v{RLa{FNlXGuW-9Q$2SNg+K*>9fm!Z97S^wv zY%scJR(BJ`Yv%zEPE^>u=ScvL;mkBI)9)Ea__%IgmTaKm_+vY_cjzC%831OMcB$X? zXY}zwDwE)g-zfnl`6VP{rRguEm))G>`4Wx@l@mTU_)CQ|pR_8_Ismka8#@E$3jM}e$SK4T zAQ(6RAS4OTVM&lXgS~B*PEB>ZQeZvdp)(ef4h9Qs%+ZlQpSt06_(Xp|{bAV%0DbA; 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 diff --git a/taskchampion/docs/assets/cgi/icon_square/icon_square_128.png b/taskchampion/docs/assets/cgi/icon_square/icon_square_128.png deleted file mode 100755 index 2600bae3bcb8f11cd6ab9496a70e082ff7f866b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14641 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 diff --git a/taskchampion/docs/assets/cgi/icon_square/icon_square_16.png b/taskchampion/docs/assets/cgi/icon_square/icon_square_16.png deleted file mode 100755 index e11979d52ea7384e525d9c0b14c2289edab5b92a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1066 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%0A+!#%` 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

}FfAM5b;0=tuz^I)N!= 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 WI~wbg%^2YMk(XBaP$_8=@_zsf))uY+ diff --git a/taskchampion/docs/assets/cgi/icon_square/icon_square_32.png b/taskchampion/docs/assets/cgi/icon_square/icon_square_32.png deleted file mode 100755 index 24e9a609718148873ecd925265a16f3a8e589541..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2011 zcmV<12PF83P)~n;^kfunj1PHeQAP1=ENjm9?}gn zFFYnZv643&-V|=pQ`(a7v2dp;2Nb?`^q9*}4kumyaM)C%5%4w%E6HCR`^9?7%m~ZP z31-p-{pUQ+P3q&>nQ8HDG6Gmc86}LuIKxB;(aWIN5MA`qEi)>__k71sk&{?mtU(cz zWs;=G($W-b4bPkDy@=nlI%uEVwoMS?BN; z)Kz(^3)tR)4%y$h_yq)TXSuOh7p(vQ010qNS#tmYE+YT{E+YYWr9XB600sX^L_t(o zg{7DKi(OY0$3JWBeeQFTiJ2swOeSfklY~$$1rt%xv>0g<+X|&vEJdXj!GA$Tp(;Xu z_NP`6LCFtVwS|xrX$46zgcfSGN==g(GtC$?H0@-1XD0XFv-k3cv+tdA=ha8n;oQ5= zK6|gncfIzJ*7>jh&N@>E3AwTkg=ow^J?H%ECO7gfL)98x%b7oPh34(^cmXh^xj8oX z?2a+*D>IFy=ZKGPXxFpy-m7;b%?c=v z#=g5S=ho%br>rrpNG=4x1XKYj#%OVCajPiVX2;h1M_33Qa*MRO9XfN7@YfN#0r=P$ zCRu;a01uWw3*@GrARq~V97H`@+liPK^F2~<0m7GzvFU%)nVqB6U1X%Wk?Yr$d{Nmj zac~LuvJWC)MrdEcETne=3cieph^M~qXK3YCP`}KB$rUlePoV*9RwaZ$HAb9ssA}?W zBoHu+Kmfqw$T}Cf@%pz>Ddqw_1#&=)Y~v2dwy*29o{%0n=Wxq>9th;*oP761-kdqh z#PA-zIC?MMkE4xS2n*8%x)Hofnm8Z?@G%q*ACY>(kDmQG&vk!Kz~F7AQgs~M_<5wq z-XK`Q_$K77ph)22jel{x^E?ql5WqM!eTqO{xFs))h2>6;D;a(mDy$D2J@*T`(m@eG zune}=w;@R#0B1b~a*7zZN#QYt-k;t%&OhWeK#56Qy;J9bonOY`z!KsCddXf;esit} z1Que89=*V9f6zldLZ*X}wCGeMI3oIlukSFZrKdNrDRnw&!C!cI(%0 z>Iw&x3$7&>$SO=f3`HVW!W1W4F93=lL=3}tWa2R@)NnWy9dNY@MGvt6Qm$S=#L{mO z$+`wuJ?o!6{|s&IP&Q}3JHR~~4&l`=&D!9w94Z#tO%lImPZ1wTz`qb zcVDImJO~IC^?YOUTex&-8T$a@AmrdhSn89JR6+yi=l;waS6^Xb+X40te*uT1LS;aS zK%mVcFT8a$F_1obM~3ca-0#9^fKAa{PEH=EHaO#ATnToah9{Klf1d`7cvA%iaR`y?0ljWcxzanWJ*e^sLLkq zoqURs%3eebkC6d&gxUO`96R@YPEY+B4|o(WLcq9h(|z1l-CYX2RVXk}{1tprF92prEZE#@XN%l1Fp{hm= zqnYkubsQP}Wj&_C&(PA?yV9gWQ6HC)qNxlkyLWtt@y&;MZ|)S0q5U+QpF@xjCLAalf#A}a&;x`B zPK7Y{UwmfMr8)-}jMvve%)Z;K#w$xlCltKGh` zMGh!4NXGJ3Q(D+>bVgXXh=hPs>C5z(6-Of#BH;1>t#gFb2PiRj<*~3LXnV)|KK0f} z0eA0REG^b9VTn!N+a&{xT#0diyvn!iiYO>Gxuthf01^#M&AZ`!&xdy|eU^YCLhNl? tEi2ag)?Pk0_#M7-pBQ+Hx@q*e{{su0PQrUKeBb~8002ovPDHLkV1oC?q&olr diff --git a/taskchampion/docs/assets/cgi/icon_square/icon_square_512.png b/taskchampion/docs/assets/cgi/icon_square/icon_square_512.png deleted file mode 100755 index da117347a9b34cbe8a66856f79f58a9d1608df31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 155209 zcmV(aLI1vqP)~n;^kfunj1PHeQAP1=ENjm9?}gn zFFYnZv643&-V|=pQ`(a7v2dp;2Nb?`^q9*}4kumyaM)C%5%4w%E6HCR`^9?7%m~ZP z31-p-{pUQ+P3q&>nQ8HDG6Gmc86}LuIKxB;(aWIN5MA`qEi)>__k71sk&{?mtU(cz zWs;=G($W-b4bPkDy@=nlI%uEVwoMS?BN; z)Kz(^3)tR)4%y$h_yq)TXSuOh7p(vQ010qNS#tmYE+YT{E+YYWr9XB603ZNKL_t(| zob0_xtZhr0nDs@on%$o6cJIDEGpovSQC051Mxum`F<_9ZKuDfgfF)pnXr?d$6$8Y; z0}zM_60#UDDIrVX2|}WgY-7q`yDZbVDl;qJ%lBTs+nsK=tBVK*5wUlib=ThK+-!=; z3u$ZZwIbq=Kf1q*KOzK2g8S`X7N`*p1!#15MEjx|xw?|sWp}O_U@7L#0zC-{PHD}tRz#4tL=)@ za>A;qD0PN)7J=aD{EV+}ubAWolhKH;t}ijpa5kFKn2t&Eke4%aKB&mE!g42J0Lb<2g6g3g;XnopWO<_vGLE)ytdz_5T>l z)jDg9#W_C=GA*&zzmIZ7(;8fe)JoDCOQ952hTEL~?9=Q(xPA*J*~R0@o77-uOnjWNzy91?+$g3cHO z0_z+S$fRc7)MQ#=jKw;KRtldB3aMzV#W?F>AgGLCmS?n`!Q$vb=f-KQKYCJs@UQ-< z&07O>h@Si`Y0RNA;`_Ul{jPZSX~?^l_p=JWUpPd5_OmD})S=&{)&kDi&R|X3nA@u_ z&G&xiuUH&dmv)Ckdi z{+SH=zX)(#&kTIOu62yxvsXHkH(yi z#w@BWb=Pq=DtF916OvWi;&7anBZLtA?weQ4^MY|v0PNr!YuI)zliYuQG@0>xmoF() zhL#G8XXdRjESri+mZO#8BrmzCx6F!y+87qqhIu(*S#5DRPDT?tV|YG4<;&YEs;)sx z%_z%wy4CU?P z+lSe3r+*#52CNFqe*?A!E7|p%{mv+o^n5KcWJ9I7Y@k| z_tCF757V+^o>=y=(1)MCHvx8p--Tbq*M#p6p|5e?_Hj(hGx)(Jd->8fHSB3rj5|a> zj)fs>Dg&@$`yTrKF7)gn<@dv}I$HnTXy_U+2AlzCGAVE0D<-$U`uyX^f8p`7KlI6) zSFeA|n8sNzb1UnR4y3aP>kuxn*8C^G$yWWwzotsh{6~^(u55OuojTR%kup~4s%dpw ze(>uiQ-9go&;J=AwxyPkWgxvc4mHRU#0JC!MCA)mhunA8Y@){4DRyrSov~H4Kk*W=4u0G z1+EO^cNzLOmQQ%L0slIbQ-Znmo#rZ#jy<7hfJu0FA@4fCiREhFY32r;0@s4813VM( zj7G`GJSCVew1Z_ovEyWqZM*el8tLnU_WeT(W*G#yI>6|XI_5sDxda(a`pb|m2eSs5 zd-x^?xd`=KgtAS7Q|yDN=r!T#!eEymT93DD!L2=Ech+nF&Vkv28##m(i0><|t1Yv# zL<`BfX>bmvMb4^jI3JHGgX!8@$HipIZMEguY)U31owd}a<*Xc0WEq9lJMH@QcEu>m zSX33`ET^@GOe(fr%e*LQt)=Q3I%|1vcEa^`OWn1MbVg+jb=P8?Ws+xD>!`YpXYoAu_ zUhr`%-(E6f$Gpul%qb0K5$0)|;9CWJO`sL45I+ia*#=s$1jeCW>x3q3!n0&g+iQXu zDby{%XX~3@E<#x{z-{ro;Wng~f%b|$%`urDhb{xT32j&J+s<_UJ1*pnw7dyrME;@K zGxWe@&26C1!neJ#p&g1)|83~Ev8OxI`F1x=XwL?W3jGuMgX=)^+`c`G3w62*ZDSL< zZT4+M7{lZN&yD|YmO&`5*z-99!Ultyuu5e&-+K1ZlRtm{=vPi(ef@*K?VJ+`q;p6c zL_FtZcqb(M=l?Bh;r>Q>hAt)9nYL=8(dWvcRcks`S6gXDC;w$zfA)_n<;`qL1uCrs zvX!)(0_UV(pR^JsL||Crj-vkiu^bTXA3u${5I_m?W3wespt*kdk%X z&{@MI%Xu=L^VQ;-c{wJNl3Z(kczeZJIboXTY}*zk1wu%2rD!^DfiTYTVs*<|Ip*ut zg0a%%TJyolIoq~j+14l_xZQ5}`1FEhU9tQ}uDJa4#ht}LCA_wtDbFOe03{)p0-gE3 zNR=Uh7s!P2Z_aux-Z^&GB_)E!?e->ng0(;^iL;I_to3N^{T*=L@?aCyC9Vi)Dd>#z z0tEq|=vt7jqY7W2C@i|LCXgvX=X_jay)|M7hkgD`3bK!W4n3O=j^BsSibG@j*!+K2 zczrlA|GU!PcP)D;{e84?2*yuR*PnL%4)x7_`HsQ4Z@fH&rkXIJ9356S7#gTSl^~Vy z^G|?tk1xOX_uu@RS6APC=^R!HI^mZCs$5f*is}#lOMD^RZ=IZzooe)HX7#CZWV-E~ z+H~&B>h?EX_oI)65@gy-#T3vjOWKVL>usJ=2qnpsKxl98kYTdl@$oW2Y8nVJ?oOI_ z?Ab$kkCl5277XB&h8}+Xbi4Nh^+?MYihC%|KaI2xf$`&%bF7XJF4uYMke8$#)`AJX z08t2luwL7N`mSaE--z90S=KePqF~*&Y?_)$QF2<2$hBrwZ@Jod#y!$Gt?e+@GR_Ou zO+}_O4#!zJ=FMisv*`)1)(f6Y<}}vu_405Qsvsk)X;+c3=wq!4VnhIu~XYP&{C!3QVje0h6~lmb=2 z%U}Nit!#H|`k_I|GJzJ342M>NObasMP3_LPoq6AN7M)3y6Ic-#d5{3K5VXc2Bs*UO z*Exjt>kQU-he)K?_#?wMmcYpc7Uxa#4jOBbz5%f=5WXRY+qm{GKqF|4#e{bm{2WAd zL=5tUv5q{@G~=unTE-_#zWwb7joF9Jkwf(5$C;mfzuaSa@BB~F@8)X)i+k#lrg;Z{ z-a*_4;|}0@6kadSu{!j5&QC%E-<38-NnnUy?BY3GXAvzst;5mM$TdRx@v1bS1dd7d z@{7^G|JnEc=Kl#;Auv)<=RpYg6MvEKso(fFo{q@QbF0rX$24n=&>JT{HTLqa;@&*f zN|525`nsy5y2>$aAQEvRNr?~wEu>UOL)LqN@WP){$QfnTMr<7Sn(-|gt&NMH$-fpnYF)IqLHfu&Y=i{>rzPPz!UY0zW z&FQS+tQ@mwDn2+l=gn%tD9b12w)VeuaGtu*pIFM5Cvt-9I^<{_^6@s~`MM z7}g@Jg1Eo@-}74i#=rIP)atXssi|&-%vSYgWLf`ri0d=01S<2hVskyBtL2U^M3bJc ze9-=d^h`UfA@0_r@X`*+yNFIJU6`!tR=Di0=(F2S2ycr-DoGRb4~dIi5*OoD$k*-h z5h}@>NM|Cf$M5h?-&3cb6c-uFmAx{YucJ65F?I>uLqYZ8Q(RRKR}wtp|NBJhG(Pl^ zi2awgUv%b4RgjMTdfTBg5`5A&-?d2`7ZHZ@>zmPK%o)#gkRe_29SQ0aMe{edn9e)j zIR~w^jPi`gC5>s3LNdv7w3M{YqJqzWF$N(7)1u&PJlT2JPqG{# z1h3aip3UZby}adOJRy^k+H`#9;xVt+ON0(89#3bqUF(OZby(-fm1a>_ zOpAg{`t5<^yr42I%erQo7l^Bj?T2f`sM{@*s=&NO6a?^sfCF;v{}>ynO4v%^oM*UE zqt2D|&Utw4Mz~8+XX;>PFJ3p21d7bk@p|uVv_jED%`k^GdEsa`= zoj@SOetAGCzqBAE!6(-3{NOYKDgCy^;?Xno{PeJz-Q9hU^IlZjyZ zw<7EY5O)UconS@peW0SY_daOXI*`T=eEUh#A1Yh&Hq3q)N^{g!*NYz_I(>W(;eQC% zcY%AX{SMWsU(T_RmVICS)>lQXWi8%~RoWz%q4j#xD{Rojqh zO=m6Zx*}7GSuq0OYO^9!nrE{)H`RtkwdJGJb6%}iC@C@4P-Z!=R!fX?Je$t^nzj%u zs)|A>)@_4vj$e5EjBQ=>qvZ{c#xs-@T+bK0`7>W2<(~0JEj`zgT!8XE#gsw>rX47Q z^9<97WQ|@3I_uD3JD*m9wzIo6cCGf?{Jcd$VomJB{o1~k!uP4%e-{oqEE?f=C1^p@ z*lB~V%*g<^A+`}Mr&Mkoa;n*5a{NBU`IgO?s>HpZkhhV1PW%3=~?$8uwZ0ubpCGd1F{gBX;mYe2}a7n(WgCu>XdDC+HY5Mi( zw@V8DJ}tcuyWg(q_C(!F*T-q7y&fu5gx_wxEJ*TFt^{FtQ@hZE3C7ejYiD#f=$zq z3B_%-WtQihm18pL&l`BPyydpuGA~Eere)PsoG)k0zc>qf!&4;RMDpE;-5U6=0cCIf zB+YNayApTSuJ5F^3CvhdT3-4s;veydFl7QOmgRc!efY8NJNM=AyaJs6lxXP%n5*rYk53QB9P7ic==Rg-{P?TI>c~ob~vM8gX|LAntm_$7k4+zuPAf|EnZTx_?6S8c8LM75B-e4VZ=$cKqK@ zClVNk%1yuP-g_VCetqf1_n&FHV|z#X|NA(b-Y>;p58%@eX}mu0ogUo&_kQ2T?#zB0b;Bsjy%j)8o=j$8tFB{SjHo-m zE%9P9K}yN%%@QpX%bS|w@1Eh3?RLrujDgNL8td?D1b}n?Xbt1QI?re=yX||LK(npa zLaec5T88BSxzko~4yhzE{Ey0pF&1p_HF5$iWy1Ue9|%4LPI>_Z*!?%o(V1Wzb>94M zthWSM0bPVGJkDAKF+(UupM3iE81K`JL*u_s7w+5D6u(6tLGOKtHudL5KhL4K_)O<= zIw$*a>AW0*8S(Ga-#&frfA5!-*0JBlDGx~LTc1x1wNw9>(#RMzciSe#VF+dn{XKqt z7%86p`lZiZy9@~_?1|aVVh{v}xGWIjo$)ZN5Xd0RW^#%%U^+i1$EUY{UT1{{q(dN$ zm8y}=&v*6be@rP*THsoR4WgzYvfKr1b^uOcW8>owmr^zqI0MNKG4um{R&Z zJ;}}`&)6yPe}74(KZyG*JB)=6D<5ew{-anM??w@JC&VWGO)GC1HT}*K?L_#jF@H zFUA<*sGFJ>%LQlUm`R?os5YFIBd)g_PRbF+I*he+wnIt5x~*wV$H~YqA=Gt^5|UR- zf40E$`6*g!F4rr*zP>~YNoOqIzIcprj*HQh&Kj!LoAW!{p{3;YdP!pp#yQ%q1F87x z_L?V?Ijg#&%yUl55s$`G>b6Bni7_3yR7~@nSzhpa*Kg=d=kG1hiYJp9Bb9Mdl#H_+ z3yiXi)*6hp?|^IZpI}a zEhRDtDLPnyI^%r@v12v1mtH=kY)ZhfltR~f1D1D@rdit zcpjQZDa}dW`!p=2L48<`ySSvG>!nHS5Wjc5xGqUE#E|=V#rPD5bgp+ve2Pa}PWptm z>B&&Kl-8#_ApNEFPw8V?=RO_kzo#^Ch(4vyG)>n_oAQK|7DoYPCu}B4bh=y&d&@8E zh@DIdSNP?5Eyi?VDL}Ct{lP!7@u2t_irq z{C6gjSP^lzQ$$j!zT@9eh)Hbner%2SP<;A*C{4edhr<1FM2_KlaM9-=b!p2#Q}FGx{w@WBtW8 zSKBosogt-QS^K^JmGMk@q%+pC=4!n{E5)YQzWMb403ZNKL_t(-D0S|ay%22bimvOJ zmm^eIZ(cPu%cf$Mdxt$KyvBO9T~lbyv?#f`y<(i@jPrs`+c4JtB*DhCJf2K>wO(>Q zns7E6Gb&5Yo#o5xOWq5I5S^D}aw&PWS}@9TUayyYFh66O7rfc7$fRbR<(y4s+-_D> zZNsK*Xl>`$%>^L*!AQ%x+U<_`{N{=e=BJ#EMr@h}trYryn<4-7)(ig@5Q1>;5>`62 z739)yl`Fzg!3g2k>9wTmJad)OZ~c?PYwDe`Uz@sAtc z72ZQ=&wcddA!YtFXwneOG(40Z48^_c|6^s`*KUXC*-#zs!@r;B7_C%(Ozt%NC^VU1 z5eSyHy=6cIEeMPiD4e$fIERq}X$vr0S{%7Bl%tz}3L*Ye=LE7bR+{?q7o`BDK^hs( z8TQDCus*TdKN~dvuxJ*Dv3OfzGeJ6e2=o6Yd=uU=#4TUDr2y}W63oX*y6~+3>5MmW zO)34^{Q%=EXWO;hZr5Z=bG2S^IvUeDOQAIHpPe%+OP*Sb#H;>7AsK@4^G|r2V|^383BQjaYW%4sJ+&Cb<@D%e z1MJ4E1QUHGB8^0#8T&cgIh0wKAAkDC{}_NYs{TdV+o>q=j!KIx?Bn%bvHnsNF909| z^CwQcfMm-2Q{mv7@J;v`2AK>qfnORpxD=4V=+|aD3jhd|l4vQpn9O)QnQ}gvaRl;*T3`QrAPTxniyR=ij($)sS@)-2nK#ssxq2rjoPERKtE!ltd^ zulq|c9PIYXTSsu<^S{n`fj|iF$*+a?6;goK5*0Ljz_0a({?cNfs|r*N?{kQlT~K}0d)Q#ApAOi+;gpVj#r^f{1Si(w;AS2?8eCXFts$ z`c3#I{Ny3!sp;~I5c{R~j=RdX@K(@Fc*}rP8P*WC-NNbPlRxL>XVd+QD0UY}xVZMO z*r`U+kU#cC$^q{k@;&A-^9d&rbql2=7C2$KVdNhr1`wyXJkD z@_&-D-nH!e+V#G?1Mxz54|S3Lk4~#X?$Wh?B%}ENmerPVp0jFdoO7H{rhIX8McZ`< zA-LYGvG}{=Z?_wa>G=HmHCNj;)1qYCb#&J6IiF;HyWP63sJo8Zv@C0Xt4C`LKX>tj z*7|FIOYJWfYE9=)`wNbE)1u&`vvYE-1O9?cYo5$b$g>DbkRB;%!hHf*@IC=zcbcIJ#BO&m5iU3#vI`0k@DWTu z{MN(g`_FOBwihSn@)GuoC6GJ4_o|XIN5LK7)aX>L+x>--|slG9BRu$ zWehnH9g6G!@3(J?XPS2ir( z4Hx4X7WloZ*JMgjwXMGfxNUY?`^ChU^Ln#ll;w1t;p@dM7qb)IY}YL6ExA-!Yq{Di z`FeGW5P~<=igD&o6x_5e>$YW97Q8<{<>_R`rfqP-v1x0{EMrk`nPvrxszOV}y590~ zz2LUqlIx63I26b`Ahu*mv+5d*b!efOj>mlb`G<&B`(_O0#?Cs7^o+H`3j)SLV*^u{ z&v9fSj$kiqdTbdCv^1>pkSbIuD0QtYAWtdPxghL4uM52`ORxNtp9K%~LV)88cmL9oy z-yPR6e}CIbx~*d<{X;{FS0Bbu-}K+#_3r9J>0?=k+9So~5UxXI_2Y*AA1gCE06Wd! z@6$AWzn>!R$J)LBUnIK%wErwsvJ@cM?~fHA-d7ZVV!}yLl554qc+7`qk0_M);5XJ1 zFP?ccnbMk$59ViFt(QEXpRlO5LF=wD&hd0Q=c-zB6ZY)iY}Y)S&ck-XTYmy!wISCT zQu?cgr)9~T%?cdcRvSiT!6?u8;_5Z;P3M#+Oa@WsG%B7jWFP z4RzO%E6wZmGMvHhC+J9LG}a&M`+DPj2-Z!*hbQM0DnkOZqTuCv!K$vf-mXznGE$k} z=a0jhj+@1rM}O~`Z))Mq>JqQjtFU#?I_pnn#CfJ`y;dI``8r29_Sd(oaD**-?v7(~ z5$+b~8h=)T3g-uOVd)?W3eme?1i_+n&I<>4p(1Ygi-!<-zbRjK@DvPZ82Is{bVl*X zCkF;HZ!^bZ^Zd|!8W^{CXxB0L{rp1*>>Mg5&2t~^8%lGGKBT{6+pbgi{rr8{4{g68 zI9c!ckZ|bWw)o6?Y3{4%F`S3s913@Y2C--IyHDcoc5Wx$hS`A?f=mJme{PR~U}_V8 z;1B!@LOWBBqZZ{4T!7utU9q4TAEk{L_m;^&PB89Dg@UjRLhR28xZ}mRNBFIMkEv;+ z|9+3!LvV)RrGn6*vg|mD7#$&VNjZXZcP6DjxWBxzcFi zm{nc#)y?&8JKp(Z#;fHm<1Ax5nbDXooNwT@*1Q<;_39;0#xri}t=|nWp0I6OF2)ls zw=1fy<>_R`s;$vN@_N1E)AI|?M^jc!#VjwefN@@M0k1t@Zq_`XoiZ*8zPP#KVl-iE zTZ&AxthWdusJjmRgM$1!6YA47&N#Aggs*8GC_$zqLgKxTgH}#h){l;OR(K{Y1=Pm+ zdk{GfUpfIII3^n5cH0b*p2l^~kq4*4$o$jo)h>*Q z{MpAy^^OE%XsjQj6^Fq0=k&31;``7X9-5zhIQ{sC(7T7y);L%DbaAL{9zvgw)u&IB zQXC$F^AJr$^E+75CH z)6-`{ny$(tyf!f(M6F+eH~*)|#x?&tLF^x?3G2=&a|{pKhlFDc>&Fc?`Ed_k{cke) zy-Rs%84tldEn`SHdMKO}-hJ_h2J)dvG4$TYyU*PF--h~NXp#?oOF~=a8>V^bx6^G_Nc_>kPp97dKUWH4Ezf2ro{pl$qviG-8$){^Fp;HJMTrI^%k~;kw!|D+;!4 z!zjyHtXD|<)wEUD@^~^sOT}q9V%=0sGk-{skdnq2PDW$ipP!H^McZ}$^gbzgxm@_Y z?#8fbTh7Wck0vuNw`=OQ<+L2{EC46P2(2`2*YUyG1y81PF8EbcQc?_3hwDnk74{84)IyHpv5W}_%m-7(CE-iD2e(!Jy{BqEe z(c`;uu-|dS_mACfK4-~<0Ijf%3uknIF2+&{>^`dtPBC6Q_Md_U!0xp8;4g3tsg&^_ z3qu3#K4$ii_(N%qG4Y{}Na5W#Fw+0`NsjL_i2C{78Z1Z3=)>vbH-vA5d5F;;6T*&_ zKSUrOB0L?N&~o5&vMwouFtA$+2o@^op!wsP{KK3g(>xkaIUi5>a(T^I=bVlvjEjP0 zUC|nY64Kub(6!vwTeOgz6n+by-!+i)X0_yZUj2xdiyL0A7R-v$ue~e9yePTct~no1 z5JIr5Ya%zzmG?(@vsqDOnoMb0V`;-O!_{`pyd1G?YNQZMi<}qBTYn>AQ?YDnv{XA> zzjyhH&RTA_Tehb4I}}vLX)&TPEwiHJ)3XaUUBkMm8D}}mrs8V5#5l{#^@6Wgx4eIL z!Md$jZ8v;={f5)^l>GNj=xkiki#PIlZM*e+8E3t);+(@-hmaD5Kf$mwmd;p+TlSLm zdViI;SJ4HYc8FlY&*lE=;JBQlguqFV`1?n!^=AhN>vp~haoeI0P)NU|U@U0q@$Z1q z$Dclo_B=$`zAx?}3QRh`j|m5dU_L}pc_{3ma7e@Nr#Zwh`}rS210Jd<{W$HP!hfi8 za}2LTLeWFMy+xM~tp^=h4iJg-oW9!t&xv@?by&`G9)p-Io=WJ#WQ)>)MC^bWlOmA( zcLU%0;aEcsG2@|h>H9GW^IgI*=`kH#{dE0-cSv*X<8|MlOUt`2Oz#PX9dQ2vwi0*t+R-5zzo2n>74gwClp}|;njA{*?3H$H0RSf<05BK zZP_+0Djd!GWOhQKH0!pe>RLKuc{V@ABYXD6)MhLh0Y9!fV9_fX~JSlqFB{7_sEm3IhsKmJ%4(x{K+AEKe}TIR8I_tD}X zmoC1GGIPw*k^Uc46qEJ+Lp)aX74u~2{?27gfnv1)LdeMB;|^UVLm8oyT`CidKp@s|CgxUaW4ZyN;V`>$jc(U*2A0x|UU4 zVR2p~*WO$4a=oB&hUc>rq>xOqoU7H6*Xtz+s5&n!yxFce4LA9%YOnQA@*J&XFp)dT zO!M8#*NpOj<|9I)@C02}vn-m-QG3 z4uziKTOs^)&jbg%uWJ1v4$iot*5`Os9&ciS`j8u$2{d9Ot z4r$5#dD|b8#~ANJ1cUn+??bE)LrnD0yuVMtAAN0?EilL^cw?k`*|Yl zKFmI>LvZ>I8YJ(r9(Rs#JW@^{_D~t_K-#!0bLjqoL-zsn>lTlkbiCb$Lw6_iVJ6E% zZ>2fZXUE!Ps2(XUL+6C|+Svit_zP{~HgMDWEniN#{eJW{=)bgNyY%Mp+H{QboJp2* zQ*9XQf>Bwr-fmboHCd+p5wo2k(~`^0nh)payjm~(F~3>PvaT5A-nGUA?bkX>)pcBq zCR|q=7InpU&L8vH^(9J5O0Ag{{@|U?S+-rvEYG>xZg~IX4Cfp#Rtwr7j69yqndE-I zf7P}WN@H=X>WU9e&bh8OT#P2%hBFE3u45Fe35C|wrX!PzMP2c9I`_L3wj1WlTs-d|b(?z()=uL`qqr%BVMUh*l88>$n$UGHOcP0LH^eA<>nJf_d0nKddkhCN-GSYo z1hHMxvitbQ%O(2tGYO0o_MMZalKg$Yk2IZ2-cx)IZI6oi)B4`mw`smK3~kFXAWYBn zkGB8_9V`F>KhP(@pDwV@GA~EFll+8~jJ2N>FK(}Rv0iX7ow2Gagb+NQP8sWh?_50L zX1nHeH0JUAlsDTI2tj8IZ#F9yO+~3RWw@N@^~PWR^JFr^I?E?#=bVqcsohw|b-g85 zibb_$l;@1|oF6T2{8mDxyaV89#ImXQ;msv==ePRJ%MqDYO!9)Smp9&GQ52pTO8@$o~g#gc_!7Ca&`8HS%q9IX6r@oL*!tPQZmslL5 zW9BYM{NX{7`CCWVCG=X3_RN3C#F?IP5-)#=<1W43FV3-a9H;ZDKSyE^yiLzc5*I^y zF-&~lId*ZEKBROgRi4rdZ&Do7dc}N0w}U#~7VcBHX`Nzx|H7ZX`8wjzR|Zl=XXpZ+ zK8;Az_32o|MKMe_Bg{jWxb^!h=I!%@w0?a7BW3=5dfUG}HvAXH^U8zP*>pKzDD3JH z2w!j{Y@-FC*KfPagPbAUr@YVapNA-OKdsm!c_)>)JNV*rsKNW%rRhCWDOC@|ry&(A z(uPk5YM*(kUYc~^A6g4Z8Fw7;v3?O=6k^gc;$Rxm(EA$h5OL_o4TZku5+_5Sfc4vb zXpr{>nlxRkNBZ7x%f7He@4Y`sravHeJJ}tI3sSlI3jMhDlKXu&66;s|`|V5Q0Uu<@;}5P^gTPvIGGi&CfV1 z$BfDmlcJ!phG|}g>w!1aZO7$i&2_cr$#l-7$ay;T*A`ETg4?R%rrJ;_#hcBF(_(~k zjzVQTo6Z?!IcK9WkH!-&H!H5!8w#C~tBfa;8Lt;NtlOG(U30Zr@pv+0UXIwb4MIpB zjc1$|BX9Dbo$}euC66a_uBuhoNg=7a4r5KY5J~f9yJYlw#OUSRH@nzd^&0EV{74bD z)p-tOomT>O3Qu6-LhiMF9nSuj&d1x?;mK3@2soL-RMCSN%EI*IxU|M@syM6sHu=$k6-qs4uJ! zm6hTh6_|cq(~!pXg^wYv{*cu&w!!X@xddjPo~3jC5D$n?oup6c^N@0u@(6faSnAVC zm!wPax{r3Hb?WtxFujgF=v$WZVG-I->`}a9T}bl>0X!}A#|k^`e*n4ky+0WH8gJU@ zrkAfTEv4ng(3hsu36&1Wp=%jC?si_E@uy6$A7Z`w+BeDnex1@ZDUlfxc;X<9b?Hmb zDWmOQfd z3k%^&UhB8>5zg(GV1o6)hOK&1OSBRQ<-Pn>v@SY8`I7-1Ua&BUSyY6jhq$Z&drMH) zS@ieb|C)jCU7T2`ZB~!`$|*a3YmtXIW$E3 zhZLAYWe&YZ7^$Ljj7j&u^#%IAg)+kHTRh{pv~T-M3w^jz>fg}z6!Q6L1v4s7=><43Hq(hyaO0#K$MCRO= zWK&ov{Qe{o3^TbtV@oG`zpQjJr5&0wyNKtIV3SIZBxzEMK{{a4K_2lZffM6WO}uYq zp_k{FwWM#AfV=&aeoI4NP)N&4+bq>=`nW~7ef;~ACMBXngmMTE5rlv!2-vU$;5^e8 zZod@ZzAc&lktyq~H;rdf@qB*Drm30bC5<(Fd3(*gD7aj&_~g-3CV9baU9oB!p3hFG zyN=sxyYmH@WX0}MA|?D8{gbkwc7|D5;4sXJf>)~r%eLZtJmF+CW?qzN?Y(wdYk0j{ zaxtD!D9zbu!m_FO{Q6CRD=@}}bN#)&Z`(C2>l!5lkET|t(1kZmbyoE*Lt&T4CDc+J`@fHDVt=IbT&V?qN?iWc)jI+l1D~C<+8xVeZLAX7? zbU_h+4It`idW4iu;?$k;D;NnR=t8At_`1&gqXwFG0}0;GAQUhnxDGBU9c=aXK2aZCgG%yYTF&(R> z;?ZadLQrbW_g=r^Y&79!v!Syc@6AuhWO}=y*V>=VPIgV-+K$`5V^LK>XZ$(*tplY5 zb?a!2Kg-`3e`t`vZ_RVTl$my~6yEXJTAxlyL8eqVSHSJBes=7(3d-OFD1qqwWP;i+ zv;Y-^gCG!OT9BW;kI2=q=00Q*=qowt7)-}ve-8AO6WGt|)C`>}1}UsmIY?plLthI{ zm7O8R7SjyPp%g}1UVpi-U-$mAEj?Z1001BWNkl%jtUY3l7@8dE zGC*9UOB=Dzu!pPxDdS6tyW;ME=r??y`S&Hue&|bNsa7!L40A|}?o06fiPC4PL-p-5 zwlwtD)6xz-B;cfY^x?!1+p;fk^uy3{z@ZS=)B0_DXpqO{nZ6Jh+cfR}wEi)^>%mIP zOC)RoV8gb+upLIaaB+}fzrHqw;-8pvF`i(Zu-Tc8GV@yiH*G_aXILChCv(2My#nF4?yVYsAyCz| zjIx|nThm&D5T3biy8n;8H~F<~Ne{e!5wV)Rc6ZLX=Qgj)m-&)Hl@b_BB@eI|;6i|` zl1fzwi6I4|o1>CEfte?a_yZ6UL;eONrX>#yLN=C#out#5`MUe=y{FmD+N+5O1`)A# z>^N)fbMDJ5WAOd1&fTkt=8ISn@wM?q$3}F^?^g*B86n% zx2%g9v#g+QTdtNX_FcoiYx!(;$?A`1e`>rMl7>Un|bEg_(f<>-_b)(E8pNTR601NF~TJXX_u1A`anvg9g%@3PdKbaz^>tC*$V5Sc*%> z=~(>scnz8&gsHYK*4&K|k8^8bTz9;_q$=xJvQKf-Nt$%d+vqnY(lIfN%T5Kfbe#&< zai69{IXsJ*q%ndzw)Ta*F{2c>Ic0j{^*P2pHV3A4jxly=d2t_)F-h@v3OJ^WP0AQW zYxe_RfEZFOLMSqo5``qM=%@1Sho^$}fcss+uNVh+7=03%7jO&k8>|-2tjO$}CCN;%O zTd;k`pp!gf1V4sUQxx}UpZt#15hQ>ppU4@;rxe~4AaK%uWD3as-8o-XI}UwEnPnIs z{3nx=bva|#wnzkT>%9vUlx5_e@MvrK)!iL8`yExDvB+~?EmqDJpXdDO>UuZ|`1Ni} zt`y(xHdIA{l7hJ^X#0MMVAyFxtMyR-JS+Is-3{BO;ilemS)8w?BvF_sCoI_73FseK%5*KVz3y(9)A~sPXopnl#UYGncmX%2CFTm zcS5~TPTDud()X?$>%tzmqk^qt5QK7COQVhVKyW(^w6^HpZT&1i{_(h!8vF2!&GFc@ z7fY!zv5C!gF`-ZA^EsjtZ707Q`*$D{P5izlp0V#5my^Z? z2z2%gZ!kaxCu;dgj@T*i zePC)3onwSji9VfxDa2~?kSCSW(}|RBM~TZy@8750Q@F@&xQh4XPyTjgv1C~xY&ZVU8dYWGQ`287qmLY`T z>+J@xT+bJ*3b)lz3h9V-mecmV+a-|Y%)Dmdx>|5s*UVH#(>tVRS%Jo~X&bg}!^N!P zi>v1}UB|v_dAr+ED8P(9fROQKVdDXpczxP*l!(PlK!DVbCnZXODNfbbW5P_#K&L{=IhpNHBjtqxn~$?%XMR2++pR8_g%{OYR#-h_7Y~w6#=u z#;4vsKJ&-vhWG>T4mG>31>l?QJ-rW}Kg$c&WyRbF47zV>HthlJqwq;7*|Z1lnwm1p zTo589`FMFjXIwC#tHpvmMcD4U9v0+HlFy;ixPq0)qKt6a?QnT&h383mz!IRg|F}LD3oGxKjZqZ zKSD_92)n`&bsV732<+YhJEX|>Ioq++M;`Jzejlg0GBp%CkG+@c4ZZQm18c# zqUQJm?-zh%@xvcK92-;W<}hBS=FJ$BoDzevd6=3Pri75kG>(ai_-uKp*)?5nY;-*~ zmt&{8xXdw}$AqvkykhBhOi)X49Mk-rlg?v^*PXF6F%vZP{v4bWRO0%>v$((G^l2Z) z>3gPq0H(~pF=SwbC6~v<1KGC02Oty{4XhzhN{r|fjBzDlyFx4tjzP=>^vUydv7ef- zq5LsBcl>?|(uta?;m_rN${+JY$?UYvgHTfI*| z#`&ifnl!I}FCwW&2I*gfYab$5f%p#=TwkrI@|;XM?m7GN^a(AftbbZN3t2Np>=d?HbE!(Ey*?hsetk|^;Uv6%E6g-FH zu5Ea`-?Au5+P-Hscl-7??ScElfnVOd0U^0>oSOc6x#DUzr^qtC-rl*G1BaS7^^Pjb z`26acXY(bk?)ky9=k&%f&r5FWn!9Gt)nbLk(rS00&$1|KjluREU*5mt&A~-J?6hXr zHN2^}%!?BKxP*gK`}f*n3>e{#^6gqfZ=F-14F~=R=aZ2MXAa0S<<37qfUw6iO#BW4 zjoU?FEZ7j?Q2>7H;9$lCC*@esSgL2AAhLXXK2MFAsd*O&>N&(r-!#;KK8Vf^=SovZvODK)|P%lgEfEObZLC)-y=)W4NT|f*6mn_4XWd zHU+7sGbZe58L^olWgKD?z}R;Ca}1hqUuTYc3JeTdFgayUco1=L3Iq@LCdOP#g2*Vu z6Yd<54=}~8PI2bxU^@p>YWT+{T?%9E7-Mv%^2UQ99<1Tr7!iC7hbamF`@ocBac)sf z)nkuA%7~3EFsZM@*!9T03lQYzlqX2;PaqBGwP5=X3_rhp>!R>U7Zk@@%U7E_Zkrlo z4XxG8i-LFiJytjZ?3`?zWPRv58r^d-tEju4naX&zbn0khojv@!-3BEkuNEslSzdBm z?|FCFQ)U_KvI1*al{0#4*wzOMm9cFP7;CvK=WOem%h`harlx5QPGey$*VPgw6>ZP!rAZT@x03zCmK@>{uI%P*V!?KIuNcg z$@nodSip0NxR2qP;_=wrOmRr_P6=T#{$u_YWAx^ICG+^Z#cRzuE%Q_BoMOnP%1O(N zVM_ZGn{r~9(g29$*N>YAyZB=85j8^~4LQW<_a9)$o0llhjVp|!Bg8l2IrWPcw)wN zJtpm>62q9?4o=$>tJ@L=PK4k6}SJxP0>AH?X-?3>9yjrdK$;*#;K3}pdOQ#8F4}7z^M+w2R`I5_OPN{No zA!)T?-?hBk?@&T;cc>9U@Z;w%SXK*OE!Q-k>}?m@?S?NNz0pJaHjA$r}qs%Wr{qz6q)gK5lA^mj~s zmT?;3D(+|L4++v>r5*OBTWS3>u*%Xc43|I6`DSy+b+zzPui(vY%etJ=8RHN7>FBNV zl*d9<6nuSu=OXuc=d|7!-t4!m%95t@y_>!XW%NRk+Z=c*jBQH3Jgpu)`^iownZp*bc+aSkDaL zv=2fEM0Me$|8x$7@fyz?o1_WP!uW~DnM|(ZF&mH1bbh64*wkE2=ZQ(u+9X|EPq+?w zrrMs?gKuk0DbB4sY5U{4;`L#y@6vwpe__YRgXpp0{weG3)Tw%LIoU|MIRCl#DUA8Q z1qkI#;XTGw#)5NYi0mB8A|3vE(n`zUBw(E8f*Rsw~F>pI=;Yv0Tv` zJ^WQ(<-hA1>aOGOUB6?%gEofsY|gr@xSlV0yW8?=eSs2!Wl^G}q_>(c zu3wQ!g|?QN%9!URg;GcqRZ;M4zT)kE!`*(%zHiB-r0yDLgSUpizI_7-gpmB=-FLj+ z-Sd3Dq{<6-0MVP0^;g$uFWGk*a^<$~dB^|3OrW9@1GRO#2!eLN?HD+&p~`}>ZeC>) zN+l_R`rqd_e*IFyJN~6(F?dCfjm30a#%9fU9HnDw$}Ss^O_H=}-ZWq8@jW(|Lpzf3 ze1d1XZES2l#`sUIJ!w7gunp;)oLXNdh^y`WdKuPGYq4FlhMMTP7@>q?>v%}^nD&9> zeb5>{RTDavF4QySXECKcnKBbknE_ywPUv$?4j}ycIAj$$C-x~5b7O&%n{rN>s_cD< z>r@$Io~MuHc?zVG{+YZ3K^Lb3G5i7NCo>#B>KI{9C*Ig%la_ssS?H6r=jxnVAmWJ~ zB!oCkn*W^XXKVpUnTQzgv4t{aJjQSc&%y#Z)jyJ9+uvXU5FC90qI|o5lyPqws?6={ zzpCb}=W|xY46WVKy#R0b8+Q_)c8+)N>J1lVrE>bdCzFy)NWR+Mk>@!F-P7of>xDBhWKs=h4p?hZQlgD!S(KDAxUU#_73v_Xr_-kamm_IL8=|y$=j6 zJe(h6^NEqPQ@EYOJH8)Vp8|ek%*eU*Yl;zhtj;Liu=c7`q3Z;m>a3SS?1wSshZNI^ zbM>5K4yNi(;oO>(NMW%U{nb7J13MDAC5EgyiLVP1Co`RN>AlsQ0{BtlcSr znL7ty(>7E^&Zp~3e)`?l02C_Ymp5;?-S6ENK_7!4lZxky6(G1d?0L0Zvu`_=dC3nh zp7HtRHTP|e_5Yq%EAm6e^S}7Ssdfb}U=J?jh})l!Q}+k`zi|XV_oo9|0j)J49J5d; zw-vDQCj@4mF=+gvl?g#6AQys66;z-9@NuFuCb^Hzn=wh6MjahQ_e)4Jla;(9{4JrDaR)jGiGJUyqW7{ep}J}F}m1hc33dvWG@ zYWtt^&5FxNeUyG3pDH*VtLgU^Jp7Icj5_p-V-Y70TnP%5QPb#}aXOmWLGjB(O&SySBfxkU(0ZFyr7^c2325XuJ<#<_(8 zrg08=#!Mb*-jpF2gA|7t*VGI!)?fa4&;kgeYk6b>^uJ%SdREc&9jl^5TSMD-%>4GX zkCtncl>Ff0ntj*sdUsE&HE;LMkxpAfmSrr8ibnUex}(Z->b~WAeaXAsmY4G-^P+IJ zbSbI3jyJmvZ}wY0USCjV8P6AM-tD)1x7*P5J*_d6QVkIRZ+2T+-BaZ`2E!~bIOv{b zS#o>WgSAvD=ceBKV{#?N8hX={DM^vLEsUkg=&dGKikriZ&i2&0<>z;AxNrB|wFkc3 z+|cwrFBWUA77O0BdurWLWEt(B35vR;GnQUkdfZXL)_~E@CMhiL909>_;EzN&nqYk} z9{_*254dHgH7>nZ8aP|0cML2(`r+xkP8ZK}jQC?xXgXKVtqD`(`W#HHGqIH2kGzlZ zi|1u3nWs$3IVNU|*pI;&VM>{%vA&OSo9f3YCMAV2!OZ^-rUS1fn4}x2uzF5N)f4Gr z8m4%EnsyFSjc)uacnER7oxB$(g1a1jAsMcRP{32F`}jI=gXzZ{+d(jPpTbnW^gTS( zMx^yTVvexFj&rSWIx)_1$8r8KqB=II#t3>KTw{cLY@)?ncr2-pao%Y;>Ef8Ci_^#T z#LrTAOnM0U(m|bSDaOhhTNFZ>>4FmHJEQU)4x0(40DCk845okr-u~N$TxMPh7yRP( z9q;NIZCyo5N`86&4ht;vl2txqS;P%vw~-f1zDCOrDWS5m=`6vl4Mzi6p-hNcoNX5iC{OThB_u;Hp&qH!k;y6A-kA1yCgmlbv2^2zFw zZx0)G?ZJg6E-Q*mQtO_V)dj2n?FDXMKL{iI=MeM~I&F~NNjToWpGhzV3dIoWw>QxH zZ{zn2x^L#s7zkSfDOac$&!)$FOh8j29@3;^Jfs`5$v#FT&k?Y51UF3=zfaeMGfeF$ zBN5M&DRV*!G0{$O8tb1_s}~C;Pgxt{>vX-HT7P03)BcI_pO|dVX`RPt#?e*yJW+2-H9S&tLt45PlaxuP{AiUPULRKnaP!?*bUaBZAS+BN0|m zP6o-a3n2Bn7tynndkGGPW8V33{qdjR&HQ{v=1Wi;k{EoOq<2 zFpgu~mh?LHk{<(`{Eqc)JWyg!;k3+iOjKIWRGQc;HT_N^`AwfqwK0aYeTJiVa~L<0 z@j6YCKgtuu-T`YJ;Wq^RwJYLdMfNj|zBTN1!=fzFSQdH7clA9|3g%hKi_0s%+PtI8 z3a(cRtcCloCXWmH+tO}(S{&U@b-w&aw0S4aaqls#47}~?hw|g;=ijmyqGV! zX=;q0j*Zm=Lt?O8&fN)uoi&sy<8{3O0n1{>eYa;>%rIWE|7LrOQi@sTn1n{VfJRCv zUac>@hF)^e9j}&`Xlp3*oYuO5-{E5Ke|+_Vx9y6(A zm5$9gPh56d=h*xY&*C*s@NoXeWt^KAaT!m6cwRg;PmD|ao8lRM)44ac{!Z0D)*ms1 z?Ii)iZlB=i5i?Hl+CKGdEXQN($8b3Z>HFX#caDBdl{>XdEoc&AAFQ-JVLZi;eE$hw zhSRuNdVK&SSt%g@$sfw0peO*sz)+0sgB%M|k#g7Y0HKrZAK`bb-^a=y%b(_n(~P}K zVBiAB)YD^9{s{+b!B||_IDhTqmSZ{H?GUch=P8I0T;FGM$K}SjM+5yFGjxLEQ|p>y z3R2BXn1lfzMy|)of$=^IV<%on)9;n~sicd(;K7&*kpZO=h#pkN5H?sEXA0n-%qh!^ zy6b$kmMnA5efRBwWmVF54a>4(S-Hb~=4Ht|FIki`W<}0cx8y2guUl%{^TTJ)5mIvB z*Wd%*+_W{9iv>u*UbpOZhs9DG&9W@1@`60eIGC2LZXAJh4h*zbBPHCnJHB}KoLOE_ zn}&5cXWO}O!w7h>STipQ^2|B?t!HzdRV%I*3-0}y2ZyfVyXSg!$-Zk4QqtIt zU01W3Rs7Y>SFC3huU2b*wY}x8uj#esU9)5Te_W#kbOxl77+fG=ZS3*WT4&SOI5R*N z7&437nclZL9&l9#Wf)tOPc)pD>t1-me=$=CS1B{L3IQ==I zGu5^@?_&bT_l0xoLR#ljgoUZR@q7>W@!ApA*-(-6Yj5g^9k1788jeUqFzn6>@IE&C zDeb`+oa>LVST(U3VTvxnv_=$;LTyyN1Vk6E!{Eu2k(F{4vAQH}bgFuapQVKGDKMs* ziKW-6a>hD7N*jBQ4lu^BoOHGyXxa}p8TDq|wm2W05~vsqr;{~i5M=UhD!opg#SqxS zhXrY`rZ!+-Ne=&pU|G$`^PG86u-7fU)fAcH%k2$WUa%|+7lOChvg;a*u(YNJ;k@=Y zeNCC?C@F{jD^$k1oUv*4AS`v)axtq|6eXAQITzJ}-t=^)Kjstg*~K%gFwDz>pWnW5 z+KE|3k!AdH^A@E9FBfZS-5`ZyD2gJZF&(o!=exr_DpOo6mb9khb$w5sXHH6&pfOls z`DXJLlyLj`h2dg0$6C!y<P)-6B0xaP-~FEGMTWQticWBHfQk-~ZIOQ7#9M!UWR z*9I~4z3YrH%>-byb#Vd^6e4;r{2-PYWA{46{lD|bwiOo)<|(V$3e{SRzV39_kk2b>PbAXz`q68QVn z6E!a9oSkWm*?J%DG}bDN<(tx`gbDZ9&W$vUnMfDsAG3{}%af*$uf@pw_+6Uk34JGx zvq8J@=?E6e*_fqvlSlu434eaT9t@kVW?jrFvz&d`P~|y)b@L5hT)*O7v!}{)uI4Ll z_ZzA_=iB`qrOddf7UWX0ZyL5;Ls?{O_ch8$u9i#MzUR$h!(}%T=4bojzVSJG&>sIqNLE*9&*vz}tGmXJ3BG|MkTUI@5I0I%!^jmR^9vdJO?!ECx$(46uGOYwPkE zz$#FFZW#;J%g?b2PUdYow!g1EHimS(!}&3PuC90;85_fKU60LybLFS^PnlCsX-|y* zxq8oO{L(yQcxmu4q{nH@lonCgBb`E=_nqMM+WKHp54`=yoB*e^)#>xG@i->+r%YK8 zfMQ&ywCrQ>AUw0T3-qCg6Ap@FBjf#!aVK@rvzSiCNX8#4w&%n|GbT={6dB=q7Ggr5 za)Z`$b5??(>Iitpzf*ATeTuVA?$c{^#yg;FfR%^tJ!xocU?{0cYJzzO>JD%ts>7^%`2pE zG>-*R-bRSNHGOGzXq7R=jI>NGbW^`Xesp3s%*F zLS?+(ZxBM#0rO(UYF4oh?Ps>u0f`;K{8 zu-EQ91F2j{;DhN9QedT})-Bt<1!1{dtU(H1F4t_jn)|M1S;?}I$fIjB z@;)FLLrSEdYVV5i8cRFIU4}6s?RlI2$-S?@jC}O)TH^*`_!ALQW5hA{_c_OblXL99 z$0kPlE?i4azd_DX<)5aVDmNc_pO3tY%RBcz%^z?Ic;%e7eTs99pP%cO#~{Wj#(k`w z6qk?(?+;M8$bdqCD4eQV7NCB2#^G9X&>gKYZ2OkIX(_UdO?#lX2Be_Ka^|y&b?LVM zHGZ3%R1$>12+MtcU|!9+?+zegR^)tgaYbu7go0U-ktxB!wA8wHA%acE+j_&ZYUvjY z!!PdN0G1xZ^?XTH6lh`C>V~`Sz-qQ&J)5!D4R_t1%c|m_Tkc!8_umK&(sI}AxNT|* zrKs|pt**JMD%SHQx6O_s&wKzLcMwvQWf)=k>CIO(y5;R*!?vrL=LN0N7!3K>Gs?RP z1RNs!zXlgIkRyRt{`g?+7wlHMlLVCrlE0tqMo@)#|MB=sziI3Q8_&O~IYaU;UWgx) z3e)__n3}9_tc(C*9*uc@rwo2P&wgDPn>*?IfZN!7P5Uk;@@fB^t3z{KTOSAp_EZRi zQ@@~8dd7NF!N~iBMp*AFGim1!n%@x`I_lFEzn{_&q?Dft|eq++(>NWA$sl$n^e=p<)=tYr5pQA^QIaA|8Cg7e-5YcE{+}*KK9833tW-E z?>L%A0>p$SL{thUj$j&yH3}knCPvakALzui5T!erMi?jpk9-z(3{jcy4N$KL_}xGd?D z+T*8^xSMcV9Tv<#Dmna5d#WO5mgT%V?3iUaWu9Y%rI5}!@b0ifNWp;)um~kkndHmu zTRvJ`@O-}HtNXXC%Zj(n4kaXoQQS7Q8){Obl<*FF8T-DW$TAky4248v*y)DPF0XmL zyJuC-$jh9!_xC8NP*SqWD|Y(8A}^eCUOA@_Zp+^F#TBpb-Z3i*R{4zk_CSy3x>~U9 zTB>};yLQJCOP*zH+M30zq&Eg7B?sGcxm+_-8JqsVYBpnTN@iKccg==bwnPcV5B~DU z{Ot12kis8GqMd41M)NfDA5$tox{$!VwywYZ_fpoIWLDSY^O<4z{5&-Q z+S5D_c9f)fqh;Y_oTd4radk2#VRT*$X(CWQ{V+Fu*+aU3i%9a05vPf@bZAGMCrul- z#pC-RZ`^L*Cn^3XZAjO{s6E5{B>INw`7&@6v^t-85HupwK}=wSxhR+i0s4cxwPS0c&;rb{sEVi+Y2tnaXCXDcqu=gWq|{gprm#Ay$7m_u0#8jNV+P% zk0*Z8z6beYNC^J};wer)(@<7Akth0pD9_`hZKRco%Mzz}#CcLv?1MV&@x4xQO8VkS z!hbYQ24-k#GDUepzw0F3fWKvk4-ksYy{cXaw^dgCK*7J3fq?Jodp^B*Mr$=zI4QcC zFSu*>%(9GKci_c*O^fEB8}6FD_s;Kmeb}Hf!M3mIt)|R#?z;n>?b!7#WtP*Jp38;X zm%l7ZE~+_;*_=&(KnX#i6mOdiR#-OufkSs7&vITY*BneoYdVw=>~zPA#gbirV6Qt` z+jF1d40gZVFx>&QtPM%p!oYbg6zpFRF{{x^$$V{JlgIU>b*w6Jv^iG=*PCqF(o}_UzP9&5d0TXfIV+#?d&qCd)^f8nb>Kv;-T3ECn zq&meTE;A|rL3>B~?15InK1{FdvCXkhRJu?pd(;9rzS6o0BSHH}bmI4CtiOiKZo!xL zZ^>28&O7AY?(c^!cb`6c!7p#W0|Y`!I$?NGEogep$E!=WZNpWyV%Ig4O3@lgk!7sQ zicNc9J)85Z?OR^X)_hstVyxl5t6AhTZo8T)D=4#qXI05BH*fiJ-|+eBny+@Zj)6dP zQOwzO4YOj#O!w}fA%BREkj|#wbS=hOI_uKU@`7*cJ1UiZMDa9z&% z`Q5ijDfxJD$=kz*ec$lW@``u6JEW9+bGRiFlJch)Y(KvNrQOt1?mHOcJg`eC(7vxF zF1lY2E_ASEV6w~WPq9LvtUZ1+KbGQg`e4kY`xV8*of0m!ND)}I!s4W?2r&(O!6Q3s85D8&%_|IF(((} z7k8eurwqZ+MthXryC|)-PTEhUMULF43hAsxalD@dar_%wpoW1y)^?vi-R&|aQKY80 zQ6|iuPVzWE(es!vjxjqa!;z-bN!oxVj?G@XNdJn3pq(OffHKWKy#4TUMnDD_rCm zx9tugK?}o8v*Whi@y-4Yjpn|qsq%tv5BI#7uldo{D;E(kQ%EJS!qC|cl}SEYUht#K z7uMi{OaD>9|{wz(tEB)83u%lVu_Dat%UWs+Pe?mK7qM}d%*Wm&N8_mo*iZ96vo zfvU(^%_?{3k5Vl1lGb!QTP(SpFPutW2(IT#c707&WXMc1lNnM-R3};f*)_e5TxJ51 z_ec6-U3~wb(aWU!4px9r0$Eh#uU<@#o3Xi-raMDKALNhb+34I%CF@igOzJ+F;|Y`S zpiYu>BU1cm`muR1#$=2U@sW9VvQCWQJXUsmKZb8Q_a`Lihs=RrV>@?3<>25KYX?FB znSUpv1uFOq#EhHfxNeWpwfO!qq4gXiIJOR_Z65o^Q=0V=dOIe(O`&%=Hg<%fD_w^9~i-gl-Xmaia41aC2FzS z?SV9Zju9BcGvqz@o5C3;U^4Av&*FEnjr1w$-v^$W{?{${DY&1hOTw@oBQv$@7#WWC0tnFj-FEF zAmB&WFR7b`R`=}s1IxTZB3Vhnwm(qo1^^=^tJ#d3!=6u9&#|WGU9&|BNg~!vA&vrQaP+|9p2xJ}gqB43rPotTTFNw~ z#Qq!&9Mh^lP_J_Ib+A#IXze~JJ_BKhwgDmrsZ2>QQzx;doKfnGFb0otjE`|tah_z* z*g<_0;Thu-6aR1@p7p-LVK+l&kM?d)z_pj|Q|Ui-Ms-|j#C<-M_y>F+JEuBsPk{c3 z`KJ6a&iQGaOEVU;|6Ez;xYu*}&K-@Jp6fnVc8up(+Q-U>!O^MXfkE=<4BRjjKSo9@6b@86)& z>`lw3>u2<69}%#nu^m-WAeEvwhHcj(1Z>(Jx2-!$*a(YKlAUgt&t_Pl$Z{8buy0vb z6}_=s%opr+%eFgkRV_J~4yhDEISs{k%{^6Ka^LRR>kcE}7n|4IHhcDcOKW>}edB@) z4P}`Q`fWiC~}CnYRs_C+Rk9Dp|=Lx z8~V;*jm8=S#$bAbF&3lEaZL{X0>H4PFc!cB-lvO)zGHZS^PD6f$_c+Qv7gcsoy&i2 zECu=-6D8rDEE7M06N=Oa4M3uv54Qi&klHb^e~jp-na^=L>9dqOnkaAh5Gf2QetT*L z2+vc)m?!*09umpk9+8UsRE;qmM8&>&@%b9Z^{ABrzO}A%R&L~yRFE(%Zboq=%cdYY@ zOOF{iDGo zqj5Pk_rh2mB+RM#6F-a587tGApgTTSlYHqC2Cm+CEjY=Rv?*MhsIrr#G3vD8o@70E zuxOo0pPiPOwl{5i45R51TSjj{-~}`9*A>3U&_*GUeoiPMFrMnV2$#Yw8bJdfJP{gb zgnt&Fc@rV)&bACHXvWPzMAuU^?J2Y_rD29s&3m|yKo5Pui4VUk?uhX5^CXPG$@92V z0+{44g@Fhszi9`J5&zgmcaFe5fmoj6;|mPbT?UVS25BEo?ZFyQ`4M3q|JlSLZs><& z0n&fCnE0Qd=JSj#Kt}uSHDLlI&5MXyoF^bU6lbI7$$}8q`GEMF({Z0Jtf!L`2yINC zhc-kDUtknSuETp{9!}_V(HQ1L!azI#8pUB>lW#)7H%LoENJLln`vX1DE9-r6jfPc-!2wnpJ$fyrjp_`_6Byi{Q8I z4t%+L$BV_9ecw=PCw;GG6)KY`e`Y|I3I6*2HD#8eG0cjBZGT{I8@7GJu5UQ#mY2(C zAgw>tXvVYol3nMv_bsa>N-3(mU}p|&y8}O1U-QZ889l}=rUJHI&89yv%L`sDuK4WY z1x1#z@Af=jU7!^7Xr3?DWJ1wfL#7m+*JjNB;+Zqw4&z(UYPYy+1AS*fIfg(ALAm}2 zo1G5Q69H{x9WrKIW;tQ@qL-<_7shHxABb*T&PlbtzVJMZ<1iNn=29N(lJXSCxc)dz zIu8KQ5uQn3j578<;gB{kwes{gq#N)~(#T_y<1=}R^MI@IcqZTHWIZvb!O|LxHkhDAIPv`oQZSK9-=22t&?rt#&r`JO zoC%yW&*S$)XGGUFxj!L9o>V8D8HzIgN^lYfz!N_MSy<>ogTj`G$X2eh$kQ-IE>u2d z7l|A2RKI{JCSi>62z5uNxE(Wb9Ce*_ZRkO%Q|aD$0Na6e(oujP5k|2x&O9IuWL;F( zLyj>u7sT+Ccj*9B2^X%+!#pRNiYL|=*QL(jpXMEXE+f3;DUJ{D8hJkq;4}Howk7JL zvARbmb<*Ckvconf0a$IJ@N0+$TUzSh&bauK729S@t}^EFzD`u+; z0P3z`S(YxgzfjzFd#+|ngn+m8{V+f7+8yh1!CV#``qqUhl>&?5=CGwUEzf5wGHcM* za8WI|Yj;Q?c`;wJX=^@NUGvrUhELbexIOIHcY7{ob9!sI?+#oQ6)MZfrR1i*=W4cO zUX;{b0|k7&zhza-SQQnw*09$txsY7W794bkQi^YP?^qUsPeD^$nQ-c`pJC|>Yu>%LArF!iR(=z(ugyE$~vavbt_pXPWmdzcLXQ2^>v2n z9E0fMh1lax>^`XYg%jYcK=%TJ1SKe}z({$FL6L#;9k8IqkqH=R$vBlB!Ums%vDafX zaSGzTo`TpHAjr581E=s-nV9a%hyk!6q4dr;V+cl0fCICklXS6~f{`*}B*xSqk8x4K zY#4K2V*@0BfmA=Nq`TMQD6yd&i}sZ`(~$LQERB^dMjRkFPC)cdliIp|kseQhcc2_& zSf=`6N*@;YPnsrWt~?&V={GsYNTubd1cxyOF1`-^o_@!mb4u%qzth+v5<8ln!+8o~ zT~~_ZRC~tCASoxb&-YI_Hp2j-x5r~NG}zyVw}0L-?<=-#&Bv=t-W_%{rsMOg7c^Z< zrZU>TV_wd9y}zfBiu-QE50=+hVbQ{JSuNOh4HvUH2v`;s_syP}axnoEg3cONvkE%5 ze}2;)xSlP2I|SNjcDez9JI!yl;HKHSSOwlySRkmfj9FPSqvWpH1C~vb zkJr!8TGQ%|Szbcll1a(iX2bPtMQ1wJvpJ3KkSgcC-P4+$MNzTq4qPwRY??i_@dqa< z#ip;h`0_b7|N3jPxhBiJlqsNa2O$zp1uC9DLsj+Tb19xv={SnVMV#l{bvic2=Ev0H z{2&hd6Yw6ZGab|Ca6I>ZO5Hp*N2bz`wK?wV$G*=LoMFhrb-Dzq4=9V!0;w#bleAKhXJD*wQ8|0z9l(V*5sO4E6EOkFw?1J4p0dWk`ENOvH(fWU zm;rx^Db@+M0j*&w2#~m7WUfyGqZH9h6*_N>7^gZOb>!$y5RD;TM8kdfo$91B&jayS zsoo3R>a35?l}dXe`9Mg9q7)`+C7bWODk_NYL8L|m4V0^Y%5ZE zRj_b*;_E$K#t@ zj)N+c9hVaz)D`g8Q5pS_$rW06^7_5h(th9w{X%b1{U=EW-eA z>U$2RW82m2b&JY#uB#P|=~(15w*7&>+PvnnTA-baE?@0#xNUa4uJ5>-FX_>gS%y+B zaL&Q>%zfUES68S^@vhn80tU^vm@SY>vh5qbJ>0UM&6(#Vv%DnFGOibE$~@<;-Sb!X z-#RJ1DCn)*xd4!5ify+i%QE(+;jY`#(YrGS%8Ga0mYZ(NRv#$yoZcF?+8q_Vo>d^> z?cp9F1$)!5C}upLubde`R%Czl3ZZ;=@9prtbO)jn2vd<|4wsW}7w*IF!GEhTHsi4^ zI1SYuzJ1Mc?gR+my~WL=Fg^}R{-aLSy^rTkIFn!-Jqz<7P8-HmN_69~7}^xS4|6Fz z5B(Ca9r$N4A&>hc%#kK(dw|_Y`uNO{(2fJAb0to{mKlbg` zJ@h@aofBHMwWz9L@nXmF)t=?c1|hw0+dHj*HU_N?))?ml6HEe_Kmd;W4&${+a!Mo< z_4*)?rb5{m4NW1YkEw7R!(&>2NFyKnutDT9Flb2J?~y~bI=?7!lP}g7NZi*7gjINxM24Ge3b!bg zxCvUhX9`!=iLXouk%_CDrT?w`^NoLY;3%zxMI`6ApzeKN<~~jCaS3>b1*8Xk@5>Bh zB``N3P05ivul;>!cMZDoX~Tlz%hK8rSRF5h#@`>@H`0wSFSH|O5Z?h;zE1)!x$7%6 z_hU(d>Ot2;p+$Wefxuk4UxTmbz;PD&;2OnGp#8I!;!DN1%?Q_F^G=0snuHJ@ERW7qAN=Ot2DuB!!K?QiH+$4$RsRaMk|L)|x&s-RGkn|4E0 z>sjg*QzyF<2nIy6<8lUu=F*OX<3l9FB<8fqF^YTNO0y`rb3C~~xtXsglI zQ1>l`P`qhd=ChLea9~-?xasd{ZI6_&$SX3bX!?%%tYlMbZuFL${f2e1peQno?NOPe zC^L{bvwq9o)|fXH>dO^!zDHIT4#FK2)IpyMsxN+oJ;;;8T88-%zJmre106H2rJti= zo^_}1$Z-=VjPo!a{n%Ao&`X~;$IYFPKhzuMO*n&~J{`9#aPzDJ?m3NAci<>J2Lh-g zyu+NwuOsh#{)HdY8%{M0B}a{%=6Lv4U~=*(O~-NW^&EA?xyPmPZHU_w`XIEk^zUXI z=T#TA9bnE;xLI%%Q2g8*=1)B5Lcaz)176LsZAMr$HR0ej8`_b1O}?_AG>eb+bo+wl zKErk(da%Mc$-fnt4Eh{0r1O;+G#s7^8}WeHPZHvkPQ8yNB-I+E3?WIKV>rm9>~PvH zUh0p_^;#V9%^&^NJ1P3BvV!_P#~d8NS4tv~6r}(5TLg$t==IrgOVVnKxEcQptB70zlu=5 z1+$N#c!nxILaIxIP)L;l3K5;}J_%3Z3O~T#da&Lf#Q5=Jjft**bM*+n07>DZ2!_-0 z#G_kAelrl-skO#f=Y(~)L)#uP7mUWa34x6w`sVIEof8(VK}d_t1XxR}Er;$FiK1KzsQzg=Yy-3@rxB#y zKDwTOG2%bn8Xj^vV=%@bM8rST8<>C~=?@O+%5M=dPVGO~fh6=A7qJfr`$-$SN1Fp# z0Z1{Z@+E&Cz4%?a{3l3N0t%&agvzlLA(2Bla9}oCw5HTKAGm=3&x4Q(YwW-i8|i}A z*f{LyZ>%m?i4-wGe2mllP1Wt%sgy)m7vWGyFQxw)#6Q51*oXEx@kGeKnf85v*1Pg0 z001BWNkl@Av)&gVCMR-7rc(YBbWJNC~4Qq$MOax=TPpQd&SjKtZ}YM5II- zL6oimWBcv>`MtjPUvQu2#C^_nuIHQuhN@kBOOCPZSE@Y}8PPB0~uOWu=OEvQB;3pBwIPy9(` z!C%D<_QIq98TW$OsJKTXTSxAx1sS_9K;o%FM8r9tW8aD__&Zj3JL8a!hw9kgJFK8a zSmPSCgHz&-2udkbImb>$a^$wesA_fx9$jIqD2VFluXh_oaFcJQ#DjdJDH8pY?{#Bm z4HEoS!oA;&jZJSfg$21ldFQ$)wp!}p1OJ)GoeHna^xml0Nk5E#)C|L0yL&vVjNzNN z#mq?kROS`x-1c0MggaID6xwwr7}DeakdzH@s3;SV8)*jFyINTqa1!{IhmkYm*sZka zU!W=D|9njS<970Dn;-SH@FiEwQu+`Vw8&|{aL_EP3HhqSv;wMar{ zK~lafX}U~9$>Tfb?;%$1j*Y>wRo27v%?gFDqw=x6Lp7t$%FZ`KY_l&hS+{>Nz+Yi0 zE$k8#TTiEs^kd|p>p0`Gfsu4){qNB7h8&mQ*q}8~%s(t}jS#SclQ?vHrsaYRQlCBi zCf%m?H`KxQ^`VO-!$cwjD9ux)yNq5+c&d!e0exjHLPj>${tc;gq?6AFO&<= z*|E{hSJioq24^`%$S=+VP?RKlNBI=mwC?qv8^S+_8OzX%igBY47XcU?$KTgZE=v0| z;F{fI3Dk4sE%{3In(Zyy_$|a%ske-RGBNX|=4r>t+%;yqWpu6^uHdF zKUU8o`#2@NpkHi$u!T;RS1EI@Dof$1|1hk*WjpuLezEJ_DaM6!()jBa@t#d)gNUPw zm6u8!yB6g-ckIrd0>!D6K1;$sIdK;#O{`j8(LW0DQlh4Bb_zZ>kK~A<0)Jm(N=rws zso0zSElXZTRWQz@O~M8_Fk-#V(^8zYs=s$E5{x8AK-DkGr_k zCqSiMFcQ!s7hk=WknYs^{Z%4qfMP{ro6^ z!BV$Wq*8i`Sx>BBNCgm`DFF8R3U1MV95jEwR}jE1O}G&s>}EqA=`|ZlPC}%`hUg|v zZAG+DZnI-|?KGG6v#;=d+W=unLlrL{#Gccxg}`^Tt15rTWN{!&&!ZQ@LpTG{(XYfZ zR86m?1;H^;XTO!L;z9~|1<@0;_n-1iud&(34ndxrA*DcuN(Q(3yukWE$2M*#RvlPz z2nMsB@LWSYo)Je6$0|f-zIa6i>CeL}w>w>iKgPh}8jJVsKS!RrjaS&ikIi0jiOg9( zdN~$z+H_hbNKNh6NosSatx;x$Gm{5krHp5r?P)3`5Imb;s?uAzAwM2kOeAWy0nG+!@Sv*O?8)>vXh;wr<1b>|w^-8=>?(^%=B7a+m+0 ztw5GA^X80m^{P0a8izxo;eb3^`6zeZ^Hc@*|1@DC>x15En;uSv&JuXv8hka)EFXoK zHQu6nobQ@3u?wNP-&8+8bZqsnM6B7DYY@`&m@Sow^?E15{&hZZufPpB^+a6z1hD-S zkoiWdqgiG|Pi4R7e8E1GfC)94fh14B4pp}oE>&vfmG8B1KhfhUX$d7X<%&Y3;YOEy(QtO8sq_Cg6Rg#*vvIzxy)aQiBoM;;36LVo*rr!l-CI^ykfzhl z-}O>+4BfA_Lz2oAamVy1ME-DrtUgG1Ney8jw21Yj{N-*+xjGT7h;Lr`Knd3;G zA6{1gyG1a?nE668d|KaZz6n?CW@()8JEnqj36PjhVhncoY=z9$@kZ^zurhJ4z94r<|V1 z`GvUIr3>VHLT8RF_i$9*zf2!gO|e!TbEw1Y(07b(AE^$3AnR?(nW zZoNKYVq;@5q23{1a%-d3-$)w}VxDTiM2WH%k7$8h9nmorT|d$Iw;>oAk}o|(u~A*) zkT3kQDPim-q-9zSGCuag!2s#3!S2@dz{8@gg15DtWz8pnp){F6Wb<97$#)|kt{>ax zD+1K}4k_hJIoEA;fViX47*71O#J8W`0*!`c^iG!->oFB{E!>#CxYrmvmIN+a9;Y)9#NErsX#Wu_YEobc#*fMt{@d7ZEdY;Js$N%2Jze44-HDKj{xNRqsi`D)9eG!YrTj2l3BUiUrEqb^#eX>LFHa4#{nfc zOQb#x<1W$vU9G_o3Ug^$JX+uU8c9{{1!Q+h^r*?F_dSm=L2Ze@-Ty#m%JAdOx$rJ{V(~|?;%R$=w&`&ro)+C2xE(?zD*-y5vBSTjK5M%E+ zeH=(oTK5R^1Me!X-9w&%9Gjys&`o>{P?!TCf+7|}=wd9c3bhgW=3eDh+Ixmaa79GF zZu;%__4+jgv^a+_E!%yk^U<@gS+X(3=0s$dJedkj0B9E1O_;=<`ii}3|G=;bQ!VZ6 zfl)=en8D8hQc_YV4`ZU^e4gWt+GF?PYp%LMP^!Qwjq=^lUd2W+-H1UI!TjhCpUzL{ zTaN}C;cS3$EUFIFILYmZ%WwzVl;KFGhB5YiXdQJdnlGrK1hy8`y{tFAOwD=!k7gW- zzk8xhm1ySZLgOV#XhdEaEy!0M)6=BE5$S94^|>OW&$89zpmLHmV?6%#8U$DDal8b+ z;6t3V@1Q+7)($fgQG)x%!iLnF91?h#UNk&dUv799j5V6JP;F*-zl-K znG|qdnSvSDK|7C&I>zBQ;^c9VfuBU5a2zM)Nr_9z&TzJxFbBsWr$^%k07{cNikEzw zI_mbT^EwekAe?}3hgfs4&-1E`;$Gik#qNCek@&bPi`U$SZ%Tl7C%Tm zw#)=j5CVWwL(-^w%k8CCZneUNsnN8x#d0&b|M@^0cUI)rD+|z9=6CP4KNNmvjSlO# zReK11ClXGGGr4I>P49~vB_K%9%n8W^6N0L;qAk6a1?W|ob#t1&+SESCqlb>S8T#=9 zNM=E63RG`!vj`X*=GnP(re<`17mgnvoc`Flwye|EwkB`qrN0#45kMqKA#w8f*#>5p zskA@vYYqX@g#nZcv_a7>qQn2}h5D(a&m!s2w~r2Q`DxQtk#W4Cq94T(QqnMi%y9LRu)+?e^j{ZsanF-?%z5P66vKR^W`}?4}o*U zOwu(oCgE;<=K@f@xw|9&)VrwHJ--hqIAJ7sj?t>Zd}P%m2ovPU!ve5^Mymnu$DPa087_{Q z8@{?{x8M1rF>ZG8m6TYwC}1;4*l7frP_0`QxVyvqv!>hi2y@p%oMQA>-cc-C9MXDw zLu{=Y#L9KTsb>wo75nPtY$*b01XdCxuRC-f8qxLL^;0P5s&^OT(6?4vOL zdbZT@p3}I_`Q(QEKy$T!Zqhunas3M1n}>7MWIIGEQ${xJ(wT90!?HLZr8=MH#K{3d z(uekSz2p*SEFV|z3XVtdC%`LKVs!rXyhwGhQV}%`hgWp{BYYFO@HSkbSR1Y}*+cl> zYb*nF_qoll7sJ6`J~b`Fzas4>7c6#j|6Wrsi4}-k`kGV}PT`dEMP+Ce1GnGgV7g?@8*24=OtNadRAdu_DAKZKt0ss{ZP;8_c!1 zvlK|L6=21qpamqntPOvDAsYl%9+Kya8`Jmx4kFL_+#%fvnQV0I%Z%Y6o}gPWH$Q;E zW_`XGftanS(D`cYp@>(WhynXvLUDB=rBB}XrEp#Z}AXDk_zT#G~8}DAq$1(Zn0%2pdRe*xQmld zj+odL2i2l&GSAe_|4}>IM-}jPx!4PEd*d_vV%L*k4T{pT5w}^$hB!=Q=Q6|?pTLxa zS&xCZmW`q)pecQ1HJB1z#Um{|F`*8OP| zXG#u7RFJU*K4wVdWaj_{i}@+jsx%(pA#UqvBP2Ks$0CAW|43kp(W!rtYHVP)?4cD6 zU(-BoC&~D0DQi!lRTd<8q`T=esg5iM5Xc~&m@xx0&mTWLcny7+^mC%z5g{R?QLW1e zsMtuN&eeHa)|sQUFp6*^A%1uO(mVL0iw`9=Z9M(4r-oDLyAXag#d1RQVi>NbrZ&J+ z!i#cOwN*0AeRC-mxjtami@>tWC`};a?0tH}LSCKGd^WGC#ConcJ-foG7h-=IhkWm2 zNSek6_Wb#{p}5Ez!4_%DVV(t&06F7RmPyjFT<&+UcKw1my!M!)HsHu6!?`mMR^xOGcP3Cm+vLe zumK-Vd*K(8Y*A_-%p5W*uD5C?>aZIZD$8(m(%X8yk#Wa1Psz8;`u1Li$>WyPV3P3jZvv){zTxZK>L@3schasOdzq>DgMq|6 z8zw0Yo#OR2@y~O$Dz>X~Gu{fH1T{xc&AY!Zr@7sgQ_TJJ^3@~1mS?KO>QEO%DaCt6 zFid>7d3=ihg75k5GtEH$qf+INo!uy7y~g<9-0Inc1xXzC-H4?aSLhUd7A1h( z`X@HW@zyNRKqmcQ1Hol=POD{uU2e8cR!&JnH|1>yzrfE(Q5Vyakj7pqzKd6|Mj3=l zBh6Fn^nLks($%nhfEQxy_+)FUcx&k0%x1_;C<|*c_&5xZriju-&X)D0hPeF{_Sp137*9s1SwgSF0X}YdDCY*Rk>vRuf_*)*61@Z~g+raSUVE z<|Joq)6kt}{t-o^mC<kFNycx> z{O=$8QKIA_& z{-h|qD~}!rXCAPyN+*=4vW54;u6|ueVnFapoO5D=Y)7GTJ%Xge5w9qg+WSEZ66>r3 zJh0-E9CHkUgAiUGr~-r~6Oh1xn@P$jderh4<)n9f4UuD!g(_9i;p|(+{Qld(^byC1 z(;~`7yQXVacJd&ObUE0gQ)9CrWSgfh=~Ln;*E8fd!=5NUTcpz3^@-T#mo-dB(8<1? ztc+bg^RVlnEGfe+q*&;;;B2t}eA%|BB5uh?(Bz0Hj`)GXRo?M$;pykDnmaqPOalqw z)dOpfc0`s&igD>AWPQln*niUxON9UgiF<2*|JFf-5S&PS*~wd~@*yCHD!JcSIpe)3 z^-47Tz#?#w^T%I$Q|JbB);>-xC&q_$)zVF0A8n+V2u2*MM*!bxR~*ek@Yby)64?{R zoVJcsO8(L~U*<;=OL!U{tU1kj$yRtSwJzsxknBi1bly(vTgJ^N84a`_F!`~#Ed6NIiI92&;(WdAon(}~; zy}KAO$T!N6@+hkrhXeR&Gp4TUqWHgu^b6g`MtlDsN&-tl{mMar=zW>~k!zJ{3Qxlk zYGjOo+V-ygU=i@82wGj42{OX$yrw$P?enZB`^=a@HK$SwFvLgzx zLSaq5(QQ%V-C)2_%(NLu<{`o>+vswUIpQZ%Jd8nIB_V&=-YB0$+`(z5e(Bcqda z6-;_W&n5)r*c&9#Uowzz+PDVGPd)WgzQrkm?^vMFGxxGDt2-NoQGu+URy)6i zgtHN!JWhD}YL$hZ{OV=aK~BBv1LvJNK?oUTd)ITa@tdRfuJ`+yk{?D=HG+dwpDK%@clibYE=M4To{82z4J&SO zc|A2QjF%&3eKFMTKb>eSfko};T_XgbUwF`Y;~_DAm)`?I_H zA(Rkdib;&oGcLUcW}9wfG(fh`-XwOoHXvKh#R^xP|DYVk@it{d%n2K$JQpL3GThk6 zJi+|?q7>JIOq)TBm1xBkz4jn=Sd>$^K5>sIQ?VMOHhu?(1c6H^OpuH!Co5L^IH z^iGDWf_xaNOh8-WoUpU={sWc9XC$dggSj@ZWW#Sk_;h?rePtu=feqVi=zBlpi5Q%w z2fY!u(KW*2IuX+;pNhgg@z~YvD=|_$(O1t>YgLlfgB0pXJkQ>ma@%AdZSqhnQf)gX z&7_Z`82Fca);+;n+d*bV683H_M;&28I4g6mhp$&bv1^&|ENdYwp|ugwtxkhO%~$D% zdA{3jywvsB*H-+rp!@jUsuvbR2`S~i-)-RH{rBbstRT!#c8*-?2`8})`t4;`Gce5L zzu%}4N5Aez&G!xnrO9>vI2Tt0%LUjoyx&-1gIKM+u~!7HMYGF3e%!JDVS2$xGAD<$ z0~e<(5VEoVi#Iop;d-@ssBk2Z!N8sccM2T+k7`i8_L zQ5PA{AOX|>vbTz+-j|Yn9+4Y+FAk~WhE~1#<9T)WCyy&zFp1Zr4@W5}fKtQh?SxP9 zqmsR0>IveR>P zGY52py7|#T1z^v#5w5F4-)##0U%m%-;ygY{;@lwiQu9e_)7*(_vNIqPk8Q1;torX| zbBUK;?BPXEjlsD9afI>=ZLsr48yNH*hTax_I`J53s{(c)DGJ`94cb>;EP0#Ttfe`3wUpq;TT0~B&a^mpm{ zR;BB@$|&_)6GQufJo1f@P+Ne-LUF3ISH zl!*Uh*-+UKSl!;cA#@VQO!TyVgTDDFq6tmREAc?Mz_5X1J z3}uV(LvOS&#EXVDLsI%nhXJx{zxtRuWV~Izru_1c{FXAN`$mwOH^%L%!kQu9$2KF1 z^U6%}x^4?F2yR?tGTO$cIWwIwb;#N)-0QKtTX8KTywQ+ScJpPNQ0rP;8DSlQy@}94 zmC@WvPl2nI#(WJOf)Ntm4x+h)N5q=(!#2JW?(qH<{Y1>^ zKn~l8OV721|Ja#K?q^oz;A2vNY4WvXoixNM)(+n#avkCDZ#|t*wK%Fb&WR$Ay>q*E z^P~tISxCT(j(HK%*hy%gH*#mHA&218iOuuSP>84UCf_WwRql@m%9F_UA$|@#N1c-)I821|iRO!|P*12BlebbQR<^EL#x_;-J2TxVz zX|jlZCsp?-uY7b;O!3NpfNS%h(qa!m`0@|KL{z*HAH%;&YSc9xt)}N0N0!Vb(Kx)- z9_JN9qbvcSUV^+BfJs{2+fPwzJj7^^6&ggP;qz7gBTm}Tu!7}4wMRN1 zTU%pwO;z#NmL{s#yIANRSbC($yh)DPB|n`z^%Fl@cTiI-3fp-Kc(cxCz82u#%WHlk ze1zUV;A2P^?^|Jud?mIO#Sv*=Lof-~ZlDPRkVF>B^5blb3eDEUoObPC|72N~i)4#9 zQi&=X`Rwy7YJ)In`^p`OzC7-4Z}jRN3yR$S^_HKC)jSbz&B4Z31as< z>#$jeBr>a`ldXFhah-3>l@x#19~kkX^pj^sXu7`PCBe72JAq?OV$ViabSwlxS%rig zIAk>;kY*WJvd#d*+~n|!GQ$(Z^o| zr@t?*yQ(I{NipP{Cb;+?ZZNi#b8h?gmZfRJ8nZ*dh8gGckyz6JM~TEy^~@-G3lYHg z72vUd2;R0r$b`7{LDZ6~etFy9@x548V8_4QSl$yDy{?ZV21tqrrKdyL2_pF={ta*{ z*%Rdfb72H+x4Kr^c!LgK2K*WnB68AfBmKw*AX|=|NtvghfflPJ%#nmWCO*+(6L8)rGwH*^v_-Gx52WPi?jziP+|w#L6GA4ycBFOxVC*yS7rHyK zhUM>QOBMj>r-M7)GbNJMfnu!$xBnn+Nu90YwS6smv`_8^=O6NKmxi#=?IEv?zha@Z z_;gGMO}k_>N=TyKrWP^kC5cJi#(SRMV&U!sWKxU_kkHj;v7!BKI3K>Xwdst*;P0Y{ zEoMN`Z-uZ(pynn%Bwkh_BLk90c&|qCm5LyeS|K3!jTX31GgZ%`LCQ3d3zm?dm2=!Yy7}L-!X)Ox~^>Zbq5>BKZ$Q{sO9) zu+2dgLi)8f#f)#h$%UeXig7}Qu~IsC2ZltJo{!Bst3iIUqw3AK1^C#Y!&~I>)<>{! zr+R2Vlc}SEHNSe$M78;n^&N-d@YuG2B z=9BL&Q8WnYMf9cQMLyk9U^$Oit z1O;fIdVW)Wsgnip#sU(x;YIlSO-oeJMqU)D&?cL_fuD8u%E2rB6Z)Nme-dA`Y>h1S zZ(X?;!G(8PYyTPPgMt_&AJe_8EQN5g18O%=DQ?dT1ERxMWW0a`p}QAd7L_UW*y{Eh zTmD2U_|+*EMv-GvpOXSosrnpG1H9asLf{G$RA+E9`_^~DBoQwKpsD43(#WKGJ<5wz z?;Igxow3H)1;u*y%aUSq1qKvZCL3{#;PvMMCY@9#{N~2rPc;kP=TFsziZl;L()5`8 z5hG4&a0KUa4?MXWstmdryLrj`z13tugRVFKk{qOKRd+ZxG{$nq^kHMfvknAmI#&^Hqaf5d&fV6sIV!@;-`o=5w|P1i8STFm`7po)v(?QWo?S+}SW#7?C=J)(dx zn|b^P3eNs+a7*G>;QtzxosaqJF`wqWx;G7B_mU_*)ZGf(rej+YDWgZc9f|kfeQw_t zoWJWAeV2wBt87v8BQgS0X;2j;E-`WOd4z4M&yEgJUNp|&P;W+wb}3OQSo2KQXH#^I z0vTUM-)om2fJOAu`kVd9wq;?t0W}h^eP7r6=h)2}JoloK0Ws-(#YujQf4u5xPFzde z{sR0^Y*`ZjlK!ne2Lry3D^0nrg%6VXgJX3Rx(pVyq%Cr{5y zD?|48>pI{(l#=SanH}s|-c=R?vQo=ZVkqL_ws)WnLZ(kxHs>$T-o^x8X+H`yOP@bB zQc6ukM7(h-WImFfuvK~cE#?l>=|@Kf_0UbYoh=@hEN^2|Np_Fs2?c55KV0N*yXYF( z&|Li*!t$kRakGGUC<28TBli|vCwV~zE)Wxeq=*jxe0I2rD|$ZZ-8yvXi-Jz}sILID zx8xrYLz6dGjOvMiSuJiX+fAr3!$l_{{CZ;FxdZ6|~ zo~APVG%*()KuHc2II*~|kbETu;PGlQJfN^p4Xigb2#pgb$JKM#0Kj^8&h#ThCxNp1 zuh(=Qz}!@+F`APSA2c0noe1U~UpSsTQ`|betNrzR1h!sPWwH(6o-GnaV72=*A&(b|zP zbHCvSJD4p~iz8cX=mv`Ih7-aj8bzrvgO|qxN3TWkL9%DZgxW$OmeWrurR#AUzh2p& z*Y}9+avMHT{PW)F(NIU)n%5@8E_whE_b$*TCXhpa>ShCv!JZeItD6G$CQECeTiQhu zJ2XrEZvN{=)SquLI_6K?{w69pBrf5T#V20Ir@;(up(R& zx3PQB8*q#T-`I21(D9$KUh6@d?U=y-byq(5RBshiU;>tSwu+4hdkn+xP1__pQ&U_# zUM>wC-~Q+TSPNfIKoqsk*rtYCRjT`nh0!m5H-2i2=zlQt9P`-oi2`mF{$3Hj?|Jdj z;t1uG9L^Rsfzr!$j;GsAZ#TvU?kJ~2R@*~_NM__^WiWjq6(C_ST$Hoq4d06V#}xgN zBVp~Q?jsg-taWRF4_E+KXkpOS%P31Atg0Ak5Xhg8pylOwBCY7|?a*2Vz=Yts^yHGYOpQ~vX;fuZPc`%W@ zj|;;Q5hRakrk-(%u1?zNMP_*&dd9Q|lkcff?m|JG40pCAPNf2m5tgCVQoS0o%o+vg z{<3`e$J`;NGqU`5bFPR#?j=aJy&pCJ@y{E&HDQU3?RJjhA&jFo^?MXaCSOeDkDn05 zqKesHCR#-9sYXb7=mu)Y~qAqMiF=5`a4&6QI+la&D`yl(1W@3jCwIHxemW@>8^H>k){; zkQmIK!lo4FlEtXNUn_<3M6O;it6RNnG{_36)8{U`%9@TYH8_rKzk1LL4L)C2n zL_k;rJE<(;Mn5Jhp}?fa!;`m9$&~JXDT6cnhW#7&y|>2?p(yIFbFqCz5?gHIK+_a| zJ`K3B%l*jp*jCPB(jYoXNkQvx&s@)6-&g7(yzvAl?RvlT z@83DsL(C2@69aeq8wr4Q8QZ%lrr<;zCHffA`i48n7-}A_A$O$rr{>(hOzPfa9tyJQ zoZRWIJe6$$YzBXtEP|-~*Blr%6AiU!A}AzrVS6-rbU6QUU-+jrQGO>GHlkQZ58KXc z3-hIBicW+pQrgIUrLKdg4k|-lWKv^rmPO4mD+h+6NMyct@K-3sGe>2y26_6d#Zq1! z=UV00!Yv-GKbYTlkplvRSR)$MHV(I~RAxu?-51Q!duybSR1MYfEs+NQ^z=dM88BIJ zqhX5u1D+9sAiz__0d|Z*l{Sw0kz~GDf~$Y|_8-cRb7~?L#`f1q7K`0!ibPs;Rsy#R zJ7BfT3$QVWxr889gy28t4wh9s6^jO_J7SQ-naHj07y{g+HUIswt(rSe^*)w*R8iz# zHr*|@lIQ3wLbz|AhG-(xY1aEA1lc-)_8&$k45bW}IM?cQ6j1EKQ)VlfvA8JB;jfhKw2yG<;c6`tR`Bq~JB&N$#Q%l{^<&h1au9=-Mh z$2x~e`xN(p@_9gG)5}6acHDU-js~Ti*~ev4hW?S`74weZ)FVt{;ETG)x8-=!%Kv44 zyDHR}LEJ{H$%ps4bl)>VNFF)Zjcxr9@`7dIB?BBtMkR#tjKw};LM`6u?i=|Steyg{ zh*1-f?_%H&21>2Ij?>yyV8+bLc4v4YtR#$tL7<^(?bmxrZsM$Y!fz-5rlR|NGPcd5 z9mUS=ubj!CH|9^ia+J^zGe&XxKe;|l*n2F)xn1~4_pP^1+oLh%a$1w3qmtOLNZaC^ zxDPAx^oxYdT z9`{W6F9{xcUe?ZogrIqz4x@dJrEI20=r)$$ zvUAIS&|!9)BN{<{XMwl*JSHgR z;>n>BYjletvSaz6VI{)s{EASC>PuJJ49C}8(yz1J;w0SCZ?>OX=267nEz^b2f8SjY zgX+`>Ii-~kx|sFQy{Q1B($_c_a~Q$y__BgJk6+$)2aHAZK@iDB0^9uN@e+0~`@|kK zxI%|YJ_Msj88Lr?xTg_~)wm@pOXplKu|aVu>J3tc7ITpb&qum&o*t)l-x4N&k1i7V z?6{eN-LrzwHkJV4{)e1694S-7eE$}!gl&Ra@F+}(dW>?(kH$oqy+p4EzO1oOan>zvH;itqfcgZ zKaXOUg6-dbEyGO@6U$2U@g?Xbnm9-3!%U6ACA`$l7jcQP8B!_v&;{_709`&)`YRh( z@MwUN&NWvofxWy^x8aI-`W#>Y!;td7Bf%uC8O|o1>s5hi$uis+#uo2AS zh*v6C1Bo`3EYIrZW`^H6v>Y@1i->zjKEgqfODZg>$6bADW9J8?i4{z`Z20>9-PwyZ z%im)Q140QWEBrPYabFpXoY{}>SPkt?KD9}im4-f$@bSw(FqUrBQkFfWs;RJ|IdmK% z-wA&*2Z&K`dwVeVzs>`F@1xThk$5Zt`h;&CP0ipnn=yn>CnGb$FlfRNz0|{#D{6l4 zoN@JRl;q_hy}M69&fxtkyk1>v+~#q(k6sd5GP>Dx&*i21N}`KJR^GRPOk4{@0<3=M z{vG%?=VM2x00krtD64esS2h_`Yw^R_C`a_#b+T){*8Y_~BR?TCZC>s;pyS3%{2_30 zh?5U#Oc734s&5oj&QL;`PHlB@e zy52LtNN|=jWypD;IZ=YNHc=`1PBS3%Q@BAO82|ffoxpCX66;S!jgJj-T5a4wgJ!BX z_AJb)d@`{|ki&;YuGa1k?{ZO2?c8zJ9C`Ct!jFo6#5e9<=S!?G);MI;nBR$TvDIK! z_}$=ozd^FdYY*7Tzpe(riEA_43h2z1;vB?DO}E=Nr(4l8Df>QlS!l!?}T|8t#zm>QFi zE*7I$uhXrTc`-4CkOF&FP{GXd8akbebF6He!A^t#uCr%qW6#~U|FnibXZ3xo{8gF^ zdRr%5+{$L``mI5nYus^+^Cvrt&|Stu#`v9C*&x^T=lgM34C>@a)AioH)>a58y~QTi z&j!ETWrVFS zLQXXY?R zlZ;~ht9_OD36(goTgvgf=#S12yBfhC_M%+ADGQ}%{(B+UuPyUs{wqsKW}|x^oX$8r z`3N80XI8pTQ3f_^q>(8G208de_6=tzs17MVByq18XPJ64yx%>Pt>5f4)7)z~yv;hG zmwZO7p&HX@>vNg2D&4;JDtLLl=3D?Q-i{IYO=g*>fk6D6G5mJ#)dbvL`GL(=>@f?` zkBI2-W9zAInK93yC(q20?z@f>b~aQ(gz-<(+kcrjzwPf);Jtn;VLqK=9y?wu5cc7@ zVyf)X(D_;G6A*4L;e>gIUS~iWYpBI|bwPm{ftv-eU(pDITRz4=jo=q0)><}toZ*Un zZ>l$pR@RlaihYuMkkiFm6&+%*sPwDFjJ|WNFOh+72pzevW&p;b)c831enZOri4-QD z=$KK{xitD|A1m&3Ki zvzDFbSru_Y+edWVk(?ly{k_A7TYpIA>B0xpkE7ZlIh_vlD+uFvL+{Nviw z)SgUYlbS%GX;qY80~S*K)evoclk4~Ljvoay zZ@N?jMgjFnS{Xfm<5Ga#TnQ>`b^98OzaGydGK4F>c=Y1{0F}g)U3!J)R%vZ0tH(G8 z1>=Ooau5?I)xHx#_ICB)*he}=O<(PIpr>A8(g;`)Nb%OX{)J4mb!B5iOylKUKJa<$ zz^nHt_rv3oer`3a zL2$D(p^DFw`HE+zpIA3$d#V$2i&Yzb%D`Zcs|xCF=b17ouO@xqt^APz#J{vnrx9J2 zy+TRiw*um?3vRz4rWx|tv)`T9$&tGXMM|hYDoZ1Thwqc_X87=YkZ_OXd+h?95ZLq!moch4+_xdsdF`y zCap@YsdH@~8kklChNFvLuo=I{de`%$W@-#RfE7f%&4AgygD32z7Hb&dYt5JLkruU! zQDN-Au{Cpek2GRu@mrPO12?d%eQ5g^xZ0LlCDKKjVyM#PJZ4PkHB7Faqr1(p`PqC& z|LvE)+iNA^WAUBTOnTl%8HnSzKKqV8r|yeN46CT&k}rz?kFB?kYVwcYhc~*rln!YD zQBq)p#D`W=Kt_rPlG3r!9fE+;NJ}?Jj2NJRloBcu0|e=j1IF%Wet*yRJm+`LbI$(W z*?qtFs_VL5*IV{EI}>QfUV{tthXeGfb5&?UTH6JgFrECvrG^(CHf{TYb3NyLIFXgV zQ(%nnGEH@SaF&qA$D(h`G_PNBi(MA9~hdYQCEoD7y4 zv^SuB=5Ui#{3WuTUi3g>ywwY=Q(#@JL;gCK)QgMB%}1Kg;^%tAYRo2-uE8~*nU%4N zQT|*$d`*H%eGf*N1Pz=bwf`~57B%U=cy4|Ao22*0quru!2)!tuHmAZcX!RcH#9)X) z)9h1Uw;!HX?{+bheS4ptM1N@;oMLn?#ViXNC7n0Z^*f}N_cweov{h*MP2(m}CH?Ay zdxN_h^(Sau^tK+$KtBGdVsDTTkqfaoT;v&IJRVkr-Qivj{DcR&xL9JT>!AuH@uj;( zs_Z@4an+Z%U`9(z z)$jCt-roUd9eF|Ih<|ScUe|xg^&=j1X~xx991MA?V5}nq-q!-usS_;vyvcm?hQ_8ch5rWY-l=!TKc%KRX4Eu022^j&9>;KP!NW zl3@LzB#aig8q8_pjpBd3*f@}O7(#v|(Y!!DFI8KVIS10jj=MKvp!?^Nv)Gq!W|K*a z52`bOsb-SI%7`soFIV0HjHhkJNa=PR>`;Up2De+YUscPNTJ~Z~)3%ffZr*l{N>e(Y zQAIl@^&NVD=eGaGSt7@Nx}-gVI0qYZ91!<{nS~|@#I8Z&t6~j`f~7%3itP==h*O!5 zvhz-jg5Prr6QX5k`%w$6oqV0DFU>z4JR(pm-z^O-wpxEPpl40e(hp_Cx;dus5ZHCS z!QnxE!xTJy^p9+T^KN3(Cf7_>u<~T8Mx4PrULC*e8yy>QV6Rc0q19BoIdg;)h&IPz zp~V@&7bY-F)54dIIvT6`=nQ(kd#2#zWc9ohWX*&VA%=illm@dA=&9JPwXh={HZLBW zavNbt6peCwW8ikg=uuw{g!Fl=bJVg&Qk(vV$bq zMu8DRg@J;m@7rVP?V;@-5mss{VRby`O;WLx<*L<1_mx}PZ>2}DJ;pw7Is_PsWC>=Q#SDGrvXTZ3U8;t?Y(~^dfZz&fk*}WnL)F~IJB&?`$4U&40kH9AxUxEn zDqEfr62W6iHczk=_r3QwWrUwcLSuLOtIVn!8fG7rqVYoNIgp7bHkLDAJvf)2ee zg;DK}uX3zLjz?yBB%Ezrs~f_Mq?PxdEy&MZ?6M9=2I79taI{N4qig$}cJB8w>EtT- zvEJEXFAE>&Pw;3PdEVIBoei6b{T}^39XtT^kWvN(HKxi&BCOX9u$M3_aq#QKM+?CC zjoW}8og?sks5k*c;r~%A9@b113n{V&oDcd0Zxrs{<8=x zl<^iJhLcsJ(pZA(rF1isB@_1g#cNupS*JQ#&fPq&G{FQDK8>aV8*Tb&P?^snv~qN< z6gwI#!Oh-M^%Zk3_I&4Z)UCvQ3!07jtK(NC15_FGhqr!C!mS2$XuF-kc+luq)AO4g zB!bgFJrOo&C8UixU4W!GUXX~>2eV1tx9!@Zfxo!X8%7xDT<7g}N$J-DMNjLp>ca1X zCFlE&-&Gv>xTZfNS*UJ3c2>&8rO^3tB4j{_i1BEn;W&sd&6fe=Ppny_+YKS<;V*JiEG#M0)fN z;6vi1Z9Yx&hgR=VKH}BauYEUp>o3)4y8bRWFiFq+l+ewkCf~>u)}WQq*>)H(A&WXw z0ha#FYO#XYuk$#LlK`(T$E~Fj;w`3-cjP=!n;$75x2`?pu%vmA z%%@7}#WO`9%>C$TdE@td_$8&87I}L8i;{ovH zHu*=`Obmh!7_6b3uiy_|`+^uq^Hk5 z-DYB*mua#Zq}c7&k}?|kK=#h3S84aQuuqSVD8yVq@Y8$Dmcc57Ejk}YVjt)i$*oMK z-nCH}g3TGl*$LwOl-qAeEGC%UCj^-YKVrKd{DCRlV^;!Es^sUkg8$6+I@G!4%L>?r zTl}n=!@{4eHnd4X-W^|MSQIBa7s?QfCD7`!9hz|&X{x9`i;_pqLTd<(aqsSCH!*KD z_mVt#R))`_U`qzzPcYzErb&s$tJ``~AlH*cCHzsZUf&>kdLu-d2N=*(!n~45$i}b} zl2A>*9xjIvy{Dof0nedN=`R8yC=np z#O5wXABk(_5~FJBJt=a)P!%L8LBbLOu2p${*|smXXhQKkEOez_$88Vx*TT`p)os`r zuV^_s82S+pqfs%JWkjvG^qsl(sPv>#2fNE#HZ<7u-nRw>$zZ?k)rwGk9B^rZ6`T}N z0A8j9qTU!J;dCLK`5eY_jUeM~6sS^DFuQZ+89%0<3@K}cM57=iAZwC)UR%|8MC>yv zUeV@<%|IO2)o;5SZPdyP{#$u*|1AUgI5|c@K-|k!$7e-S6fA)pH|?R&J+Nx(P}Ta* z6k!wFB7@%yU1;H%)T}(s6E}FQF3ddew>(zk-XC}Pov{bRY6em{ml-`iHb0fA_4D0d zib*PZ{k%>4FW_wQ3o)$fpp0$+nIeQvGXfZ8of^TVE=kM_dy`-{?QzQ%t16 ziR?KZa>fkGCA4O1SmR9751&jTnvt3jpA*qublAPbMd0a%W7jmRJ?IO%2yz$df`TsX zuyXB@b*|s*;S$BYvTGV3;SSxqm@W?nSbBe>K1cV!=y42yWE~(!ZF;z_f5*91t>;hb zLaeue!gSTG!MMH!Ok3IC?etEY^lhOBX__8mN+7p9P?mg3%?E$jD^z72NmY7SfGc(f z_Q{;B3O^zww9o+~@6K5twMY0y<9R(skinuBs%V`&Aejzen;=TmghgZG3ReX?28$A( zo_MrlZ3LxTbj^dVr zuMVQN$pGvgWc6AON64xE^C!J*AZn-PF$T|yJ(NzK8FmG0GdiZY{pSv#&$f=-u*tO%3)3tR%;$h$W3CS5@A_xReK;1SFB?=b;R?Pe$_ z8%yPao--~dURt{yxRYq2R4178~-B<&{46ieTfpg)xYsmx{3OvmVmm?MvPz6<9 zJo%M%;r7brsN1Tz;z54fd%wChCSk{mtr;yhXZVGEgP`Wuvz6&1iC7cLemr1kpNxd$ z#KWfg&{Ta#8>}BT$F}X!$T?OpBqk-C4?X20fK#AQHx>`QgEixk#v80PNigiXy(tU6 zOKk+@%a1r!;Ce_KdbynC!fHX(5tMASsDr(qx|*sSyMPY^E*AEmt2rwdzqY$5k%(mn zw=}O|TXH01XXQ2u|2efZ`!?R!+%ndd`^M__Nab+f;RRLqs(DtmH03JK$XJn9ie+;{ zSVED`!?+$$hHl9^mm}9Bx2O0;i$jUGNJKTJG64Gx3E@W~8o~&o$+fo|WjI5(yt*L? zt}rUu8LqZB7#Fe)^46qz2uv#d`*ZuwJsWnRdk;zt)!7_=u0g*iMXWgM}C0(QlWrR z5X=l{{#EN*lI(apJ|bHQmZ*P15LTjv^ffG}kT`gVH`$-DCFT0FB~%_RgO*%Um7?KE zMIwrjtq?Z%&QV8NfRAfQDW4ul{Ur7VvrsBFiyLIp{hu=-Tmz53Vt012SsSVwL*#xa z?`V=#M#UwsoK!64q*aK2>(5G|`OiaaiDOlXDE+63f%{szfAcx#&X=55`PmWGHQX_L zw#VFO5ThhQ{K$~$EsKJB-MFDvio&;y(8s?mriY4Ml9o>c{ZAA@qN|{3 z#U^c>s8FqAy687U2f*A9B4vJYG0&$wid zaSw)|*UN^CGz%9y%|jpQ(o0b9BxAGw2tYoD5x`#Y#I+W*{kOB;);?uSb1$8Z{p24B zf(IYEP%B9Rw*4zN7*BjoiXu2V&0<}&3%@&Jp{k^MD}VQ?vE|`E3M<>h40VZ>-3x=z-7lD(IB7p)Pvu zZLeq=cO?&ZC;J3+_|R%0SP=6cJy530MLwYW-m*MhGTn?|qdcRyhDgv23iY)Gum6E0 z067yd+O%rlBRbRylmFK!I^a(jtp`M55(#PV^gq18%w4OHJ&(Od(!1|n4#Dry5xFV8 zUg~W_B9wJ@eyM~#g4E=WLsN6A=+$V$w95BoINtgV0MI*N0tNCT{Z8@WRSsLw*wv&+ z^9kz@`G3jk(Tg?7qD?bO962EwKbxZ#_Im6DIv@wVVhLt*UvwB!4iDUP?te&^XMC_A zRqM!B@uERX&7KnYD7mqHdly;&Z3Ko^*Uq*A-Fa0f^>8Lg4PM+`=eV*baY``H7*e`` zs}?S*DDsPUGmPlUv zWp`m;XACoK|I=^4X|a=?XLf)cwEKETWIW$W!cJqTn;`edf*%)@Ja#t4Rxsp(IQz?k z6*0wj`@I%2#g=x?HP|=WpDM@bpCvH0DN+E!kKTcHDb&m@_nmLqNnTmAd8*V^(YFoAY>L|rp{khOMVQVtG$fX04!*uGy&7`S8nl~_k86&ZeJ5?9q|?{?1mC3XrLO(mvyTX^flsd&<(NAj*5q+pWb>-S*m;kBgrwUohYS4$84(ezVeVlAn^3ro0Qb%h%~%`qz~?-pR! zJ}c3~RZJxZnMNDiQKOCu5@4eZRCtQZX(qrLiz?-|q61R*=-dC`+Xt969iXd2uYL9= ziRn{}IMWMB%u}!Jr0-9O+Z$|dXk;%@`&cIm@`Y&_NIsAT*1Aa)V*ciD{(A8p%>~Er zDE$0wPiJC?y|2Bk547LG-Ie}}ve>oJ#MP;VfF&|%1FCwDuMZQaOohwzN%3n?g+T7Z z5B|6wdC4MGb-R6q#4MU7emu}k3x8HVb|rJYvYpP%*t%mL<_*hVVFoK&aoFJ zn>^{g^x`f9Fha-j`D;+_Hzd(cYv;9$^dH!r6v;YsX8XgJ0{!0xtBnbLH2I;tglO7( zhumZ*nTar5_E4bofX;UG!_e(FE2L5OiyvAzWq-5e9gyXD6ksm=L4-e|j!ZC2u_%<9aLO5zNcOE<=Qv?h^QI(!pCU%O6Gkp0xPUOQqB^dsV z6F15Js69UsscVB0Bm-W)=^SL_p(}Fw`1QlfJ#q0+iR|g5bPy_d&-9(z;H`l(T98RA zjywb7qH4#krP3o6VN-=+9Tc+0&WGUK_;6pDf%ZjYBCO)rND1UAgPxjmA8N@s3D1^K z@_8)Apc%3Q5;wLDFt6KqAi{X!<`8d|hCy z!aMgX?aNW4@!0Qn;>0*%A-eCMOi7DlKzQVcd{G;l>4SK(FKG#M=VLCuLPk6~qr{f% z-9j4eduD%w>1tt)x4tueQlSIlBR6+L;3U=c40fSTC!=SsP~tP63&>Znab_MR)t4^m z!=m-OE;6;R->FsdYt0-cC)E?@qy{mH;i!ocOYp&}kI#jgX*CuND7kuK{2Pi&p>~B? ze9cP62m#A@{VY|ECcQPDRWi>z4oYFGa&8kfu}{>vPwrKxYPfIzf;a4Th2#FixoRm^ za!Rr)d5c>ndW7yT0$o=BZ^i^-i{t7$#ac{{Mnc$k(3ta}z|Qv=?fd8{WTeV-fp^HO zqRjIiUCx?MfJGv(?K^wB`h${;L$R%F!6)GRYS+yjw9$*hCR9;^^$mks`;`n2<`>QS zZ+cE~**Du41KQ!VRD>ENVFI{U2Kaz&*S-E`*+T6))uk~7h;Q_^*cC0!-L6Uwxx?32 z=xViGEC=a)L9mni#Ea}sjr7xVv`mQb%WlBI}@(19cg{3C{)BZ$xaCtq7!YM7b3m|09IkXpjKRksp;zyJGJ z&(A2p`A??N^W}9ULVcd;=I4=>8R)R0C+}wb%11@!K^9w@O}$w{d;BTQDX~JzB@|X~ z@^muuXLGhiknml8W+k$ku82JM<}Wv*cTvy>|7enqkzLH#H&_e}2_gIIrL{R;dO(LB z(097=rC?8tlN^k{#O1#vCXx}l%Y;bmU|ohi*^3!0qLpGiJN1@99~S8NK74ivQ@Opr z4wp~=h#~C>^P{EI8YL}Ej?AHtjO;By^>0?_iNYM@H^Ik81H?uT{3(=r?Z#rcQ zvuppZD()NQNKN#D4B0K~PI7R3GTQyLsBj?$Ro zlsIKQ>WYIw%&{#^IAEHZZ6ZAZ#_=9}A%Q-8@N``Gp@7S)?ccb{PlztP=AQCHlZ zHSOmqdh(dunC5{j$s2PmK^bepH&%<=Y1q|g3X&kK_!6upX{XAk_t(cLSD;cUu#BQKa5@1 zX^>OFLySa*8}?tHWq(8sXz>rMN?pSuK*gr#{XHjeB(X6?#I7;ED=+Y`N-gwfqZ?!@ zfw4!?lYr+1Q7d)un-mee)Zp9tx3n9uxF)Kog(Xb%B4oq=Jw^x(gk)nZlD@x|9}l*2 zsh8QJ4{M8E`d)GrU?sWLn)jhoLF5CtrW`KR`fa!BUcWbvBE^7(kdbg`>eoxt)oG1K zst1^W3d*6+#-lf0hp(Ey2)?3^p?!;l?B9$AgdnW|$0rIT1NZwdhcF64JG(giZJP(O z9zWK}ZhQrB^o-&{pCNP;I&92nBMe9u#Ps1pk_^rU7K`#UEWdoH66HB7y5ZxY$HO%G zCJ`2_{b1xfp`n~;yTe0y&Xu1Bkq2t^#RwMdP` zmP2!zRQCtNy))WJJf!mMmo(qn$n781Vn|tcZy*)%u==6tGkIz48vI-YTKY8xP2fV6 znDz(gf|S0#t0~yOdUxhc`|Qa*tB<$omXxZq%TbQE!`#kHc7;sk+|N&UWn-zwGq-d{#SAvXe_-nP%1#4W_RvE2h_thw-` z(>R^CSBMD86GX2xh>G?ii*+I%Xz|-c*j}AqYm%yNqW{zE9}eyd4l(@wBR4MwlXEYE zXM`$((C#7uBS+xb+Zjh(j#r7RoPYLQQ@ehnkIj4u>-}qy0Q+&S|2_^fp8L z>0Y$ON|g19n~}ZX{dDFf<_`IQUAskp-Y{={VEdFab5d`O?c&48>YYN(V6g6oq8>>u zCcgoSCw6UzGBe)5W^js{hfcp>-jan6UU{e8PbQ37$y@<^d)g2Yg&e`Z1|rc8Ld*b_ zPCSfrD4LC_jUOej>C&0&ba1*Uz{dgC+t#!TDF5}Il|oci&V}&O%Y}ZA;On|260{~3l@S^F#7h#w6y66Swl=SvdY}Y* z*6hX0VA0}MQ+EG!jf!qq5TE!O#(pHg?h)~5!ot08Tri^mRxsF-%6-N=%|+5y^D%mg z-bP3COWDg3!E7A$J9}X7s9He}1+4WWS6+hihrqd&OS1_|A=sYhZ25fm(G4;7;~Tc^ zc#GZS2y5_q9#^WkKVH(0EJe_cvWz@JL0w4q^e3)aH(@LEKt`?Yd@f<}#`l)YeJg~g zqMd&tvwGhjI?2?hF$6l23!_Ep>$sl@o_P+glA6CX3I=_Imo?w%9k>ZE*YMqPoxXZR zog-wD^U;RGI<2Rk$nv|mv3F9yz`aGMOoL^p0xjB8-xc;|&}mh9ZL0605=0Q3IswdZ zt-r!U0Et~V7OFfl_}gMY;eGs?$UGegyf+^FA&v<_Cc|{jk(qO1>KS?&u9o#N!Ex*D zkLxs2vg*sn_+(N`E5(rjlM81_sg9shyuZrN$~7n?5h`tgWp__2pab;DZ&qMP1oNA< zmqJfKnYl|(V?boWHB$}Ieibi&kE0ZJ8hyt}#*;GrFnEurnu1QDk$HtK8)*T!;gf7%n zs_;lsGL&)3HiRT95cs|>)V=Uz%nQy-@NSJ`$np!T&bMqKP%<48nieFU%L(MZ*E18f zIBG#vw(9pZePByxqoU3@4Z8E4LN|MqpEo+5f(VaS#ZN<`r25t(?iE+k^!jZl4{xbl z3E4SFv3E||v-z{x;A`O$rvUCZs}EVE5vw$xW<12|DuqS?IefO@8kL{F%u;gy2IqFm zTXXC>!a0VK#wo23?oL%(3BGFfO5baMvu_~DH8lpzYIH`iuha=P(u0)?y0p!%ifq45 zz7e};c#Mhoy1wF+NgI#fi{}BkAt8``bj}JOt(eLTkn9`LR`p!^qx| z6}rBcG?*bH(|;3hV{l%{0?JTM7_<<9UG@r+e<%Qhs;od;>9;TqVmOgbUi`>HkeZwASpq?RSVsIVxgVB^5hClq`GAcG zd}_cKvN1zp!1tR{91y9IG`;n*q}+kX%)_~Ec_$9&b$M{~EbFRrD& zIJ2GCN;g48=W68UAA8Vr-$A0=wud1Mr6z9?=F`naUq6{nEC1N(qzHeSA4&;+QT__w zCkkGa1)eq5(*sH)kFY0r&S`TVhRTBg4^O4G8BPK0fwTU*{?Y_4$bH3e?|Or1NJcB9 ztopw=-Z_p^N+5qt;Q8i(%Q3gN?#0Q8Q>`1AX;AlXm9tA`kaS@X>74t2Acg7+qyByP zx*Hd#cxvrHDoN;Z(^RP=YXYob*e*c6SsUMu{SGt0ibXT-e0}qKcZ=K(-?Gg zwcJi7h+*0Yt3r=8A&=>wzlwhfx$}dM)`rv57mE+l!kk=;oEhxXdXXUR79L@_n1a(y zv&R*9!+)$BgiwK2X{2K96bTd(z2xZK)xnLdMlye}qYBB=3sstZe4Ag*grrAt>;nDB zZflHQ*}~Z!$)nmtmzWipZ2Tq9r2zHyEpo}JcQJ`7pB9_F~VZu_n3TGMti2BK8gRXQky0+fG^bI zyy6Ls6IODf(!3t~8Q;mEd3R`=c1dGW*!MKaNZenRyG2$}TEWZv@oZM6fB!}G<4<1% zxmob3RHSE*+>`Z5%^s)XG;GWY=IleRqh z_Ky;|Ce+H78w!d)(y&O}opbL0-d^yc8}3+%wc7-#Yb9AVSCl4Q$<@Av-LQ`bpdL_m z*^V^fZwsL5dV3}rSET&Bo`p5)b4Dcd+gd(yLMAO${(0kgaFWCd?+!e;Yhsr*wF7?UQMsqTg(YJ)uOcY+qoGFA!v;Q3GsLOY3!OB!*I& zFS0GUKIKWB+Lt@lnso;T`oDa3S_*t(U|hI^^A~K9^C%YV?Lapm)TD!$N7yWR%|1^ z=?;Y}t(_=ys2Zr2DrS%{$AUb;aXw6Q8DZoKzde$p-yyTB{<=nMNE^vHey{S_6a+t^ zHt(KRBuba15s^?|IvR1?aWo)#o9R~0_R+YXkG**E(e#Q~Botls(XU4k>|qbbx1&Ov zp?IATjSxza^s! zZ-d7@evk|hwIv+(maxK|o|W|J>G_Aw-`&>TVHcfkOCaW`!*Ji~@dzCLzNd9-Urt1zhgUoz{nDiqN5 zqHt_%{QBAWY>fgX*qJ)K*ItoylB*edFekn|K}unw(CFZHBjwG!+aUhzX=OTGlcG4= zd@aUZxRooHBm+P5PlrCW_C0;yJ3`378?JkOiWbFh zaOt?h?&DYQ-w^@Jp(?jcwEvBLLzudxBJlm8f3j%#9sk8H&RTD;J(WNtWZO$mV~9fX zk%}Ihs&lDX&16VMG6DLktK{T?kjG{KZSzp$_XsCOX`{LA@|X5unPZn6iT4bW9A2+3PuKga9Qv%Q` zqG?5KLg)P^)9G`vWHTZ=9-=Z9^6#E7#(BH)kJfaIAF z?4}7clCV@LiALGuT+WN*!{{Jhz_oC%_D*_2A%tom0R7H8G2Ti3!}bj+rB_y4*$?10 z@r&nC2@VZRx=EzQ{a=sUH7S&$UW^GO?^qX;3`W?UKMs25xVkSEVrlt$V!LXzlorO3 zc9vGz3SlgPbzHA905s$l4Z6a^2`njv@E>cnr{=MjH&}(;{vG*D`H}GDEOXx^E2VY) z2Xj{~dufcth}5-8SKIfG3#VQ0NDQ@75yubna|M|n>}V~Rfi|6nkKeylpW2LJr?8d# zL3Xuc%3>AaZ14G#y_fHSOfxln+<^KT6#=zX-BpZ3(%$I1!u02DiE)m}}*>43==nFvX zbiam^4Ll+7DJ?TcPh%;B6+AUZMTE zahT7>jYb>)CFTRY^qJTRV*)d@F%$7!-Xos z3#-fyfo^CdciIelTm;7|yyl!HQnS`@lxUe=f3%S)3!>F;;R++LBdN$J63+}$2e&pu zcV|h83;yH_xPZzoGDzg1ndjFs?+e=ApFl4E<1OWlB@jh%gFC&0BJXHd5&`;UoEM~(%R=}c0*^A0V1Q-l25Q^#Hp)EKQ**Q{ubvM5{2FRYFJ)*~CJv;wM^0k*8)0(XqNO|hhUPs}9#T92AH ztL?!FItH|af)K3{sh z@!k^&qW~LKtUaX*XRusxz{Asjyx0Jjij2Tgi}$V~QE)36+R_qRraCBVK#VlG%$fELS!LTbew?|A7OVkSKmuqOed}C66pK!@z2f6SFDjp`o zrUs6s)2CuLk(8A!TQu8u_t^+j9;o_Zv%PwKUk0s`mJvhf?AzdJsh;ACz*j zkrNq?xUBzry+N`+HgY7kgmC*{BD^b{3$|mcFEVE(m|VMJDAnQTPopb2Fi0O9OO{Qp z>@W!d-|!rZY=Ts~8is)umqJ@Nk^sub!m}XSXu9Jo<}^_JX85;< zW@yQcXIAWHr>D)NhawEXi~dy-Y>pl*BLRjNtANAsgVt@BgKAj%TiE?OeZ|Wb(6;az zY4-c!`s0Zno$;R!W3n$8^X+y=^37Ll-6u-BuEJ&3@)yBduZ>jE)bh2E$ zXrdhQNv|7$eCxDvXYazwE`Hc*KC4}GX_!nb+jc>X8puet-E^G&OivY1m$ z1OBT_g<9pw<($&jtIL>OFV*#r%tiTS0cH|H;1OO%0OQ}EVxes*z1TFM%7VDsX2cIp zQX~ETBvHSHze6;!nTs$3>~*nl_#3`aM7h}O-0lR!7X>83KP|}61S_M2royUsnZZ)` zufM6_9pqSPnrK*s8HyYHWd4?jPt2YwIy|Q6%#PX&rdPhP`muHRpi3(gM7sx-2Cx>;nY_PEB_rJ8i9qx+3gRIuDJ$UGLC1T(_0@2 z;`Ye}4N^T3Oexg^R#dN}p8+71lm0D7zekSKzuuh(3~(?bA(z)Y_FuMj}F3E(a_ZtjbdC37Qi{Gr!79gYLs?RXc`mU9ZY z4z?u$v&B%C@MIYBa3s*_{mBwbrab7W785U#oh-}zpWiXs@eDYq2~F|~qCl_&`!%^j zjjJrb`tmzE0zC9yLUk)Z7Nmb9r(D4f?oG1KoKy(CDusb!6GGvSuky&A zgHykq+HNz_%U1Sgv#knw_-stvd0XsqDGk_ExSvQIW%tB4u&Koh)AFA{-2LGjG2UE9 z_TgiaC0SY?1F7q8+nN6eplgx4s3KVc0zuGq{s|;FO$b=0lp;EHYFY<(e6kMwrA3u3 z5{#XcPA=WP$s@aod$izEETDMeV_vv(S{hf5gi0iIA5tia`+-;PUkRcQHMVXDzpiXN zc)-!`{x?RH!!`)>wGd>fHqNeCI~kkVh#6d!F9uA>?CxAV9{m|%R%|}(Fz>bMGzRC8J z=!+oPbHR_z|1|+^lLckV2sVP3G#p0n%g&ZMy)2`@mnVY%28kigEZiGd; z_ze~Op1BH%$FuiRjS0&TTFl~l(V%AV|5A0_Dk!MQMuoywwM6(K$t_&ExDI+tDf|!r z_h04gA&*bfKKtz2OkT61eyP^PYTO+g?T%i3kqx(ne+nhF{E-G zSG_m0WR7I1oFqTjUUb4&!dCGlJUEV z2owLb%7y7uAi2*S!h2?C=?pe*>clpJh+2omn#9TjcP+y9u86GTHOGZ{G>s-=sH+rX zgBzvkwOVdlRbXO4|AI;)_ed_}+IM1Q$Yl!ha<%GtA?)O7=x#YoJ?#^6n5dqZ#7W$O5i++6j9 zr(Y-Y+fW!S33DyX80-Jep^wmmIKO9e#=G`>OEGQ*ziwcMuXOos+a_Hr{2k*+yhIST zh=0T$_hLZYF7u65gOArNGLjeEO0HblAr%>*Ew6e zR4(m_t3Bs8Z{y0FrYBPW5|lo->i4`~l%X-5c8a?9iy%V(|BuHj4BeSp1yMOQm4ZWj zNj=xtcw)(^qy`iS6bXKOCYEvx?i1?g!M}ZeJ9SmE9)T-+!+pkC?)2vgO*Yy}Knu@% zg2RSM*}Iz4C|ak-M~EbnjHymFoA-ov%An`zq#iv|gAtBD1Lireg+TrPa zsn>*8cqn$P+nC<(H_pn??9}ag!>6b%LiCdY5@W1H8dfU3T50S-)a9GO{>)`Ub3DjZ zA$@7lKKw3nDn9S~#QemMFBZBmV$RQ_<3Jlfvug9_9i)~a2YmXc1D@auYWV*MzOYky z=_t^{DivV1Ot-($h^?zlH^HecliFkRB( zEe&JJ_#nJ>mDV7+Um%OQOLZ$j!#O_2;yVW0&~q)<6~@T4_Y8N?X{%C@8KpT6U_g9~54w~!%RY>LdoV43?~=yy1L)Fe*4t{ikE4O-+vlNMsBc@W zO~C*3DHqoGHqYh|!T{iz?c|5+2BG^uBuhyL-bA!0+qN_~QsGzT*d~WtA?HL7>0it) z!%VFh`7C$@WRHmY#g3sR6J}(^S=Zc8&_&O0*`fP0Ol%pk#~1NvKpa!a)01Y5 zvLm#U5B639%helh*ACE-MEPO1H4qV`m;89Lc@7u+t$_h}{Tna!%2=GOAm^?Gw=wptI{ zWuz&(S>uc(kuf{d!wVGXz06L4haV2h^V2X}^il+gc<&VMP;$9c&ysZ-!iSd4n3!(* zf>1uTq3yfco^r3EF9p5gB9pHd$7l(ss19s^{xD%yDh?^^(riMUs$HPGY!Uy`YI zdh&cp1?)%DGa4A=j1Ycy7&u=fM^C+-2I6utmE~s|#NsbeFOE5jzbBD+{;4wMy#+QZ z^3CSpmLizNwMEQiJZBq}oJoS4l6?lLk~u#_nnzIdHmRcj+NqV}**)Op!_q!CiG{y3 zO6&v&iB;TR=JYPIco%nW%Z*foMV06s6K z4blxWbDr<-ocDT9{R_`Ev-eti-Jg5OIt{gx{4P^9)JeooLjM&p;*t3xu8C6qI*_;S z?SE%1j{-3g6>P+WxOYtHLRbT1O5S~!6z5&alhT~mvCvE=Y^&Gw^&Lo%R*~8+4$Z_< z#nmD4v!V_7%6bJO>h1w^6ew5T-Z*CL_P6%NUCJ!T>9fHqR~P$MHQ*hclBY)xCxEH0 zO4C7vU#9Gj6tPCsADs>N(GC4SM~5+dfPOD=wF?X~(I$JWHqve8GR#AuaA#@x<6sRB z?dAPXWxG2uNBfZ5Da~BU`VN z_aGPpgg>!90Ud@F-|$M|bPX1!JmvDy_G$Ba?3WDQGkTYK+)94JaC0{lJ-x%rojp+- z%P4kJR0kf@$|I-p`v=mxP+LEUZArXTyI5GC+7p$3_ny>6jM1;hZ{ptNZqI;^eb>l= zl-+6lC9hAy<2J1_#Z>0`%QFc^Rv^XmV|RD7v$%(~%D1lXB!}0?xaKD#-!`EP=|q-o zH#uml)^q@SCNqiJoq^+qp|j5U$QW9Yb#Mo*zfM8lv)al2kgyJ@ze8l40ZSa zGdijDOBcpL4P857(7=t;CgM%HTmMOm=pj^MpjomJ>1)J=0XeO3&l70(@dz351bO_O z^uaaZ^9~U&j}QuEho%_DrDgWgL3n@2-Nj{aXQTU<-4e(u8cdW3bPWc}*5111!2W>! zR;m5sP4Y3o0BCtT*DXHGfdpyUt3_RqH>(J`Ob1OZgm*f8P9{;o7Bd_VM<0H+3hT(j{nt_b_`gjKbL5{M6HY_H=(;S3fE)h-YDjEjmv#7`8C-bG z(up66RdC)Q8{wPqhPU{T)UtP|R<3Rqk)o#DFzwH%CGW35mGbFGew}Jod&^LCmCcP~ zd8{kxo=5iiN2Ln{7DKD(*VtzhhvRVA#u23=nPvqAKjcURI~ zg?vyOUWgx?wuVE+`~xRhk7|SS{g zq=Tns_l6ReyL0=)#Vz;{FTA~ubrR{Ev6iFt>v3?&XsyicG_W?#fI z3ZMe0Fr5SE{C2G;O0)M(1T!FrrqxnxF@pJ|K1-Be!ifSxir8OqQ- z+v0tf29kTo+hS^MUgXRBpMUAteJqsdA%+nVay?`@*t#V={h`lF9AH6>gJ?ijaaL>w zk6sJ?jKbM-nQu?Km=Oa>+8v70Vg{hvG32aBI_R?bs(R6!1nd+^6ZYyoJ3fqSt}d+A zO%`}yCK` z&#S!WEYcT{kz4vQP@tAG$l$jpcfA=4iW8jl?XQh-9U{^l!8A|^b*JTTh6+6Fny(Dp zcXE`3^Q^|I*0z;K*uMz0hmcuF&zB7Gbm3^Jtj_-5k;~>;WmF#4>MS6NjOj*AN9bvYd->$jMco9jf;} z{XGT^_M4!ykt2fXdUIw4BJxZKE^+Tam}x_RNb`~hMhre(&{fO+3=j%;!@%|Jwn8UQ zQ|$7MfD^>-vzO~&#Hr1x5wqR-#21$9Zd^J5M%PX^;%(Du>7r@Wd$VHv04~~!XsmD% zIB;QPx2o+7iqgD(eZj4;Jc>e}JZed{X=@?<`hy&>%#DN$#Fm|!@I~Rm%hTPW%KyJt zxDL6hPUydDXQm>BCnn9i3tdZCp++N91e0)g>X815a*zXgpSzInV+*wxBE%p*9Bq7x zUlk$E>jnBkWK~7~6a}ETP8gL-U77QQFh(Tc^cO`+yG@NvROYHl2D{ua#WS@B{5MZb zt}Qn;9DMT>F<^x*;eSORdbIzoXPkGisD&o^JB_a^hO^l?CeVI5q#I0{*SQ#1_2=dl z$Sw;~zQk-6NMtNe9r;{yTb!>YjF5I-CZitqFQD z2aO1kKW|6<&fta!h@`0ZR{-J-U9yb35hJ43<$4hCtoBv%^4BVYeo!9NQ!erC@_(;$ zR-xr5cG|T(UwjAsuv@XZh)A=DAu5IzDlzZN=XFMCPM0T5gfcfn~=GtE(A2~ZMD+SiwHRC)-8l9nLL=wx8DjRjS&o&BQ#aCBu^fsFz zj|g5ZJMb?C*oa)~qP}oT@<2^NpMt2BML{B4(EA;74s8@}PcT72qf`QVaQDb|4nfo3 zE1($s_WznP5+JD|+Oq2&^MHk0RvTs>$jA13LEZzv6LFKdtSXzVbIm2eXNhPYSZ2`2Ze(<|AprsxdTptg20K zIS>>{F&4>fFb|anDKpo*AE8IKL?E38I%mN zJr7elb%rtEt03+)sVS90?Xr|hhTbr_QL=~MT94bo?h`gunl=Hnm$L0{`Zces!b>+2 z#J+0BpkzBl$^^j<0~Pi=8xMp6YIbKwdcg6XyUX_v0lcAW;+oB#Ji+mg4R4Q2(2* zVpae|047Y*k6FAn@nUJ8F`Q}o#6W7ix;iDZ9O_fLuQ=!OMjiMTL5->oY*sY01oP6R zJ`v^`(>dAWqknEWnAe%c8%B(iz8zeCOy;S#Fr)mS9j*Yghd{;GcU##3;<0jQ=w%40 zBpz_VIQWr|zg&D=i-n%5;eR+-sGg0_vf#QDp!lf|H*i3WrM>$3D<{o5s}oqX0gsoG zZ%2&*G=eIb>BmLM%3cr{<@ILS67N#edY4pW&VrR7uO?gdC}zWZ z#qK>0hRk6^A*cN&E!o=BYuL2V9O-N>st}J=`rNiee^)Ak0+rk-l{I*$N_vRo#A=Af zR{})sZvQ>K2nw+kVSJ$OM8>_V$CAZrQM;{Yp!Y}N>s~G~P=I}L*Lg^H`iCPg)vd>X zf%Zc@>RGM=tZkbUs4gVrj9G^jpy4K*ptEG1fiB((xG|_|v+!MPIwzCZ+hZ#aJHMsh z7-#)aQ(@V_wyEDeIj&5IP6&}yZs9LC6Aw^xNkbg%s~FH>P?-Pj=Y++s!nd8QL-rp7 zr?t$ccrz>8s#ZBYgNx}hG)O#)>Nfz!rc3_!=Bi@1h>v^5(Pv%aN__WWw7G4v0!gG} zJS*EmvKeao8_;e!x*&UFs7h1EzPhNbWJHj-Y>kkUq`HoKU_XT z${yYgRUJ>IScG5$JI`rww|TWAWQa7zoEt2H(TPxYpY_JeFKt8gQlj;c5kukdLgOvkY<`z&i;z*QH_X*mAe=BH zgo_+e2TK?4R=~<9kI|GyU|8|unu;7v{{VOXDfT<+?^4Gfqoh-R)kQo|lY{)MMK#*> z@-NQ#gNB9i$IE5^+4s#d15pG;z1zQ8X7ELMz3!_oIo;mFf3fL0O^_+n(uClfECdFuM(79|RAtSt{Y~16 zjon;adq3FC!UzCBr}g{)n~3hh=?ELTDMK9P*Q^(!mX8*`niXfh zGZ;@B`5?C@#$1Yvk}t+{s*powr>k?DPpkUyb5f=|{)6=;)@b{%@++Ii!2Dps5UqC7 zgHAU85ZOcul$lW|wg9tEIwgOifw2VT!jv@*_i{h$$|YvEqe_>-&3CM^;t=qAG>cnM zmOb|X&)8Sxtv=TO!HN50zeb%Wx=GwVH|q>QgNWe@k=ET8bmIQ^;EbBZf#kzvd7VL& zkksPVb7?)Q7WB+tcj(%u6RNOMEeD|m8McDnURVY5y~zW2TA;yk1YhsUZ@hLcN{dX* z(`01cw4^qlIV5AC&Dxq1`uXhkZ(orx1pW9dM-?gfKU5avLGOI(6=FMJ?ISXtw~;^w zgG<7`UTTldSoL8hLh~KWe?w|@(77t)^Wzr{5~eWYD5hUR3g65Gn4Z}re8hl-&(BMt zSV`&x%VtO8rL1Io4he!kV@GWphUnN#Y>z7GnTTKH*JBmkauZ@wp!w}XoZF{1;jL+w zx(_BBV`4GA;0u~CUiPC3gS$Yw>uoJ62o*UNG7Tks;@jwGmjC7lBQQdvoLsH_S zlZlkW&6{{^^H>@Iz;+Dxh z%KP}eApu=~BE0-dZ2(O_X7@3iY|XGx^0Q}F1@*&k)d+#eeYh9Q9(@hJE#Q3$lkfg- zYhIero!JU^`Ty?}_D{iY$R>DhGmvmy4mV1e$oW044h8;VylK$gbxkr{bEGHpJ$9$b zYk2Fbt0FH)RJG4Hdp>>Q@nRxaURQ%G$Y&!zl~(6>6fR@Y7jGxGH7(_EP#Q$@1&S6q z?Q;g(`9mVNHMU=J67fN7_y(Nkf~f0Ur>(WcDEvgReDvvI1`o^hm08XtMTm|O3|-)c zEnR7-zro03CJ=gtEOT2~pcgxZ<&`3ul}kU}A@YYDu!&z?-pAqFX!b_KKYu-Z{3R*#TPDYQx$puZ zcShcPkGc{ZAUl;vV@#bx^D;ZZe=}?byY!kn+GHE6_>t8W6{g!l zCn7Otu9z*6jX6k3{#K-qZ&5O)6f^W$n^^qJ^k@Iq@x&kH@^ZU{2k)SQ=G&)|T6TEb z*RxXb_hzz*c;SZl;Bm5gd35@TksV{~1LB<#Zt`%3Zgfa97#(-zYH1CtEf&8MV7?)t zTTN>T{nGsV&(7U=-*2vE-uP+EOuiccpKVsz-t;SbfhZqAAaYpwSAkl|kg%s#ua3f9 zLK#ZMg($7)*ArkiYMa+gPWW`*m{vQ1Fu24jRM6BrBbk)axMy8}KSFnp@78h^_4 z8(F*&jpRoqB~94`M<#atrNXw{=8CuE^gUm&@2y*WPl4w^^bZT*_tImI$4Y<5B}NJY zeF#Kq2^dy7GSOS1G){>kOK$IK=-}*P!2DNW{cC5Oa78*`sUlCt%um`0<<`~s{hVlD zI!J9EQfQhCLBrxrf8qk$a#fg8f%*0C;vq%Om?R$=>V@RvQ1;JJqc@q+{L3OCvhnQ? z!TJhpgt)5T<>v$GNBW z1?l22)VOfhe)vu+e$pa!I|>@cGhy`0o@&P~@&(lHjG?JvG-b@97cKWD+fA}^cn{qw zjtMAxU(6g()U@V7M9O3rv_^;U;0wXIrq+U=-&*E%3)gvu7%d_mojvuBdlV`}c+y4+(!?BChNz{-a_!Br?68jp}<>`_S|HHFEE=n=>lX4BA8m_hG?YVOLR5>OwNAO zMTA`K^zSzQ$iWCN;<{BT!*QPZ^!Bgdgyp+^C^wFV!$^;zMl1U*s|z$7JO8Ljur`NU z7PE>l)q-{C*{^QqTMz4NW_+2ynY_<85=J{2;iA{nK|Zgs2O8E-xGBe0O+~k_6Ji=NI&N;RBTXAH`Xg%spqCDyY&}+H{P?Im>p2nbY{HT#d2}XR z+^jOWoz6rhDwJ?Smv`M5rT|9iFUa|mu<({otPW43S2DO27_L6$B^s#^Aebf&@VXIc&f=MUUt#QY2M`PL;Tv-ZSehnEB5Wfi;&M=%& z;|mM;Erj9kUv7UnyAQirFJ+{{GSa7j%uWEYfi*~yv?!b2X-Arh{gel#vHZz&wUaFn zuf=II!$q`*wJQjpQJGKsb#%DqP_~5Zo5u@=9z6yFS{w!gs|aw5a-s{4>PrLL68va& z1H>|6tYP!j%U6_*9*`4D<4Xj9ePd}5K0C3{10XmukLIo>MZ-nW}k{nWCAQ007a z&GGEj5R&W~5#e;5Kz@tS2=$PUS>keB2a5+qveMrDH9DU%x%!%LjhFz2hc+L9gyj$B zR?fmJOCo%w*%&cq=0H7(Yj0wYo%2P8vvP!POvOWI2bL=ea5-0 z!M5sqd7znFVP>-OKMYJX&QNK~{r!Lh=1Y42#5qhliSR;RG=fvs#I48?O{{R#CEmOy zUOVe->*3-(`~~Vh;GsXRujkAK^BuE{2(T`=Tj+iT`WXu#*5waO z@b@mH`gC78MjITKKW`7i6hcP??=4=TpF8&+qOzARS(Y38%uRf4gqboo@;z zo3?IL+OHa7rM>_AP6U|0{@?Ssz^^l_)ZYH<`jvqQwb6=5>jl{jG%1qo$2)SgD04<1 zbdzFCq;#IMJU~e7SM&B{)ap4G)o|r!I3RJES8NICE=sD67e0Bd-HJJAxMV6B2Nbr z9-zo2l;K8jaCaTh=@}iH#jJX_`JY<{2j9tWsv6<`#g`K<0yglfy@L7UILuAXBLwzgeUPs2Jl9H`T z0}0v6S15KF%YU*G=xwX$Sfa|FSH)1AR8G}qJe)~oph&6TXAwJ!R!ySm2kXFz9`?Sl zayN9I<2-H7(`_u&3A;aTs~>B+u5VA`CC;gn^N!nX^&f%1N)DTlAIX$QP|$||fm6~- zJcuM}PSA>6pe#}M9piDCig<}@h?Fb2>6G!cxnP3WdQoyk+M-ZTcz{qE5ZeAN^A~c~ zXw^-OIFE8$j}i`z$62x77ZW)=HxHr!gLFXxCANxgB7txzbcTS^;iW<&$tzTRa6x6x zzU3AAhXzlg!U2-(tZfaL@9KL=2ObenlBv+X^5WE1oG6k=#~#%lwY1R;6NZNw_KYHz zUw^8LNaoS#+)XBUHT03mt7dJ|hx|LO((b#Ay9y#0u$f#_XI4>{8E<+-?DHe@;3V6f zyP(S{)dRlKp@P+4M_2ZMzrP#mXNdg_{+Z?58e?gl+9in_=EJt1cqZxb#^?AKHs-NL zYp6A9i=P@NZ6}6h4ek!d`ZgN8{}&4o)ih0scqBo2$Brc3=Bd-Yt+GFi)MiIW_9J;k zEy$-%1{Mo?E_p&Q41u_%uxn>HzK{ZZv(F8SK3%Hf2OD`f!4T#n@$s#){#qOMnWj4p zy6Y|B@r;snid0z4ifz%a@Okp+R@SAIp*Gu~s}p>5ajQMwv+|*Baa4S?CxY=D?#I#ck-Et5WOsoGI{tO53{!JAyQPKz5=<>u@ntP@V=zubBpvC0%Q zF^_{7GZUxPNtvF2Ari|h8oUW|8DKV8$?`-h`^NsW2VY(s0cGX69DPid7)$i4Yjrud?P zJao+Q6;z5Wax^_WkyA#r9c&Ak$^~#HE6)XO3}o{2(~c&~uiu~Ik&IO;Vi7~iTLXrL zJpw$TI6Rh*M6Rm=#OSaW2{`-Rc2?@81boImk|O5E+dgXGRghE+@$OMmmk);br9L5& z&@H{s{ovc#`oNk3S)kfnq-0X#3F^=m4fP+};|gNjw=wyDz=M6CFcn5aeQNBGBT9sI>|wK?wn5Z}Kr)|Uv2D+KYm+u|wj5{;O+3&8uyM5Zr*FY!`e7WUC$lU!fxKJQ?FjQ3hitHP~6o%Pl+oc~h zkBJAPUh&yOc;0TWv+{YOK1vbprk2*;%Q1!>%Ph?#X6b-$nybH*(Ft~JIs)NjcNKKf zabpdVNy+4-q)K1qFp}fxa5Z=(?z5%2@P47F&rF=-GxU$R;RXR~&G^*Xcd@~Zy17NH z(E>romQc!X4U#E;%hq}!WC-!Aw=n4(-V@fuzp-%d@Q`mqQWSg;36yJ0i5T!UWxuV~ zE{cm{cPA%6m;4J``A6Wt@i28>H@4QFYIRs3_SD~r=}*9j($YhJjliPUJWzZQWqNVo z1=|I&+2<(~g90g7w`6v`OZZW~P#-?4Oew=bfLeepGNQb0WU?sevh{ZkC}DuU<3>Ft z;g>b95I;stXayj#Onw zP8Ejf8?AP)WDfYX5k3QdB?>a-T;`29CEZk4Wb?fT5Lr8dvdyV>Vcl7r&spOE(|Wr_ zNN<2eC6DKrch)JFCwyDyB$P|$ovwpELiggTWD%O^ZzIL7vi?te@q9`XiB=GFzh))Z zO(g0)*xMBL0zC+0cVBX)n5s=^1+^UuT$NvqMU<<)0irK!DPK8s#U?)`bnTiTPnk>F z#uIxxO`_T&cFfm#^UAdg!S$fT$jEz!kMQpmNzM*tCV6n|-&Z_7j>Hei9ddX^p>^>; zKkxKQLS*SKbo+oD09E16s_6KuB8en2 z(6e$_Lq=zi77e@TdBDGakc^84G5Ac|ECggk4?5GrgGQ24(X+Srps7hwUZ;83g0x~I z&Bta`zlYc-K1Y7X7e72(kBT`v>id6iE{ab-5_HnV7Y&pHS>HWAA0B^PPOkH@SC6(F zLm{lXtq^q^@Z*1JOiJk1Mx@_Vc<*z9ro}n;9j!bQ5`X)u;m-$IbW(@ta{CWtBlEL= zB@LL5JsRDI|2|{5{}tJld9h|&$bf_qfrnCY2I&(+Q=s?fhUZra&=?PiL6{vpJ2NK?0e z9($zV0$#KTNeU4)_qQnr*K}JqR8w2~GiyC1Ki0wsXjd`RLVHO*$7ip5FyudWLqOft zRFy!{=JSW8#ki}i^~I-v-fxx<0G&AK)i(U0^<;Qs?cWp_B>(X+cc|ccvjJu1 z@+@<$_$lC?XK8T0Ty4$}-i`j1o~dJ1kwNmyDk4^+Jxn3O+5|w28?H*6 zoFh`$g3k@e7}zO!h{JrlA>zFX_)A@A9oNgW*(}r;5wz&}In(!8-+;I52D9Mj1m+KP z(+u>~L3iv6w86F4L3pQCBLKK?eo_v$J%bn6kqxH zNgHeGKa6<}_c)qo#rx6EBR1!lTN-k))P^mPAAhodK6bq9Lzh5tv&eTxO{n(*N^d4I z>ExK>K0q1+m^>zN)d52_F+%7R`7$niR^X~3ej|?h;-V#FUG)VwX4|Sv%@@U9RoXOi z61{`JX6)YtNdb34TCamH#blaTQj zH=X^BbNyRlTM?6i#F7ZE_1O(8?6~57`b&V&JQJ}|qVdgFrnsfiIDRkTVA>Y>Z@LNa6U$u56UK!#UGX^VQ@>t$*2_F~ zk za6Q#ozg$;}LMIeJB!OUnCcu~6uc6IQdMl{=Z}+F|?hY8{Nj>&yA*-tEc3f(x8lGor ze)axHf>LQp4Os?Fn+l&4EA&9b#)dhk3RILVm_V16nrnN;k?TY{=Ih-?J?vg-I$NI}H?nErHcw(g?<@*W_ z8<)?sr?G@w5#=ODnq+C^IX=|5p6SopX?v8$7Bq`lf`BvWX zYIpY&M{j*QyHU05L(E9`lp%C68Fs~k{Qc=BRNd0q?H+7&&@BxKZM-b80{+8qjdM5DQ`T7+Y%8?VR_U*$l8JnmPt>Mo&{Eh|M--7su1lb?l%s)oKgMw+A4;Kvd|T zu*6Hxm@vQncils@2({WaM_jZ2O-jWi-QChA{LDk2d94CK{OE5l0(|bt^zTu*Wy?6I zirw1(eoAgizOQlbb*ja7qDfi7Yn;D3rN{g#_S`vk8}_b2+D%t-%7)&bqf1=>3Qa6Q zV~X|igpSe@9^NE{Y#vS_M7%mCKsDm+C_EEJo<>G-!utp!-T=)tamj?6zBoROYp##cesIAGB%=y5FL9 zNb3+Io%&jT#zfsYMBd%Pc~C^+28t_VhOjIsna-!&nGqC2jVwYB4CQ&f&arw8_$V0k z+5G7520^^M$`+j$;icI0DNaANC_sE&g%>Q>S4DW}8KR%GVG^7|viSFsZxJc04!o#| zw00DGQ#r{JLOy!fuoccqF0El)wf+1|Vp(tb)Xu%#_2{cvxPR&;5$p82 zWHu%NG|}TR2?j*hk6BZ!Py)9$ETOd4P~t4whr&%k(VRZ|{kfW}5{<5un3#yM)ji;I zcm2nVb<^hFDOpMQTw9)xA4w-lJO`B6NM31-;0sSI_vr3SVADlpEN$wIDOMN?8{s?L zrmz&XXSix&hz5Di&4rGhDq<_uduKErbI?wEB@U!4p>$CqcfTT_z_O0-Kv_wA#nS|J9ByR*oiGLpir{gQxO@O*~qJ4k217N-YlWs_iXr$!VCO z1#K9g)iz9q*8kDILkPi58*l|hmvCQc+wK`SlNmC%Fd#-1296UZU}l?rYe|TFqO}Zq zP<6L!v`q*T>-gOn_U)E<9e~c9uEpu+p9oBLAPoIZu%>-OyTQIn<(`-b3eW_LOn3Ma zD-GzySDwop--WA>EelUdbl()gu+(14ruNLG4VxiC1iSv%IHViy+&-8^=RV+fj!GglN-1SRxT_hp zM$d@AB8}>_n&D%-f zqdcPKE28MvXCbzDHbfEQraJ9yhY!=Nwl{!+%q z?p`{r>@@NjijiYCuYRC>ZBj&j%bL>$%R?uAw=0||MM<*_Ss~i8?RxFM^f+ijqGiLjwg8C9dB>wz7RBjzBJ9>;Qt2t74v@N^9!*Ka9r;=Gq5ZKf z%Kmzj?oQ)Q)Z{5|rP}Bs^RZVOotM}il0kT?N@(LJp*C2zgQd8c?)<&A|`S7 z{{fjMqxr{Ey#eXDKESd*1z6VKk0MVJex}_|;#_vLPiIgPNpm|iNTIXK!FCNNQpD#m zE9-unu#96KZ}vNNt2UI<5<(O{IvV3}vUX}cS+iV?3PlmQvk6v|J(>1M)=TjW-b@mO z<^1D$!Y*7#=8C+SpWS43DtQ>vtVcladT`cc94M3qH&F0Igub2UwMWB1s;Fa;le`bf zyyVfgI1z-;c@-+9r;p)Y-`{d&*(I(e@TNiq=)zTgq=hhO{EGYC%aHF-6pJnbleE5I zphTwz8kQ%qjd@P6u;vXr-GfkXzM;pz)`pE@C8Xii94vi{`1uKz4CtkI(wg$f{~RS^ z=Wg|Pw}l=j*HLMKx5$>;w^Pl`Ljij{jQlOtljaXMot-GmzNvC=O52hs6mp`OCf+H& zrOZh>@Sxpu9N&FS@Z`4ZXtAq+DoUCDCYGKwzKA}J5G#I(^4&4smG5nkX8tli<8BWV zqhv|*q~{FiJJrx1SHt}3+VSD>VfWi9X+O8v}nK~$NKX$_?}OoA5@(e4YO9b=q^rZ|53rDuMF%far<=_ zgx_L6rkLjhR&OHo3T7xlE3P;3UT)HO6f+Txi98sP;kbBOP___bf>?7j_O-kq-Tat$ z)w#nNd^3h5z>8*D^w{1dmss<@rDsRx2m-7_UxzZhi+cn@=q87)PR_s^=B+xmb-pfk z7T1}p%qb;o!d_(mJy@uG#rZ6*n3_0ae((+5hs5wN*+Enu4Dm#STvI=X-1m~YM@C4$ zzalfdZS#B7Y`Z^34v+IJxqdsy?rZ+BZxevM*`L($3MEwH|I(#&lblbX_R%5C84;}l zyW_DF$#jE)Uq-||No$K+GR@94ai?RkNU6Bnmwa+NWJpN5s{^JJnhuW8}s!+n7ic^t5qBi)2RqeAq+C3(M|FrGO@3l>1PADEU(8CZ8yZ~ zvqhC!YJ&X{2}aHylQV)eO8Hky@61%4|EO-RX{uJgb6QZ=G*MC1+&R8Ja%vmU)`^zo z!=2fHmAeF&2&E?7{^;y#)bDI$D;3%HuM{of zWJ$&qLF=;2%uMS!d)&7zMAM~rJvZQ6Hzc2?-?UKy-Ydl zZ+*-5nl@7J75354aStn(DiwGH?jc`=QKSndvRy6jQep#r?st~`YJgxmwPZ)?p!hRF ziuw@|LWZ{3+DljMMc}PGN3I4DL$auTko(6K>#Nm1)K6siqwBvb$1-Z=_p3b+se!1WCVDm$84_+OUCJNWZP-U8+W4+IWQOg?$7R^ zx=-@!#el&CGb;+eN%%8)HXp`eJ-%}GzuILzpF$FtT`%tVbJ{i41_>Mu82l~#p1xj2 zbhZ%7@}y3L^en{g$>-ujMf4dWWN`K8rgJifPQWQZe!`Z^{OFyDekHbj(dg?Q@(e6y zHiIu%U@MuZ3zY-D2w{zfmwU*?v(Wj!1mw@L_`id-ZY~7xlQ%0} zkqQ!=dOwB9yEQcxVyLM~R5_$`GgtE>vSiw5fjIhr{eAL z+zN>3BsQkK&1U(4^_9>PP?Fz6dj$(LhA6UUKMZFCGtC9(R#Rs`*EN^D)0t#-D1+x~ zJhcR0d_rah9SOHDk7wslnEyG8^0!Q7vfbB7if{MP`H)5e-nXvIwIz`kZKEqUJP)DZ zdUiX72@AAH*O?6U!Gh?;OHQ2w^0*_OB_au$snUTKJuWpbk?+55D&orK8&s#eUI?D_ zPOlLxsXYnc?EcG3StXB!wzy_;ASN8m{gj^`9ph%r$p_Vty3@N}s>@1sXDw=ssH5&| zwt29NTf2CIirWvgpEroA3UM=pT3DAkrgS*nZ#v;KzK8O%IwCz4Yt;OhPNJuHBbjDg z$RDY=FLWsH9by&_#06x)MRs ztYTdzMHvG`U+4G+>>S=yg2N&>YyPHnmIAl8c~YZM)y3Bumii@>g(A?lQJX%1xJR5< zTzdpKL8=_^1}SKzmxxS_`+tW8TM8+`Nj+yUDl=GXvYd&2@ z{kX7tL@z|16!LoE3H;%AHnH$%AnvFDn=ri(rLtAY9yVb=+43UL(-+e`3ivaGI;GiI zR9+f+PL2&$e+8nXvwed`%>WYINO8g%pXpN*qYgtl{*g+`7cp_mZ{@eGV{l;_@{?Hn zliY5~>m^Xoj(ENMmX8s^or~u6jqMr{CE~(kjVz2VHKk$FQGtIE4R|9w|0u90*F`{q zQL?2z5`g>hRnOSlw;VY1KiUlV=xkdz99boNbWEx==~3?=m3N?QUy96^}MQT-aKGgk`j z6}iLq>J|Fx9WLVc+6MX*xG|pd!Y-CUB_HA zqjH+E&3F0?rJ|eOsy%t(m@Fm%B12U712_`3G}l#;(Mrbit5c^TCsHoQiA+!Q^~8a| zjuyFmHzH3!;MC6zvBpNnjAz|QO2hk?P0MnE6S=dR<^;0>smq5`~1sA!%&)X zJVXApX*YP7S!U#xj&u$-MEJXW6x!SdX1FlqK{C{(w;hc8=Cuvg5=~VJM7I+UdPl?Y zfMsRzAu9KfotCGcD^NkAU`epcb?&NCw~+wjL3;-j$VXLKIg}KEqWdvYfAx@;PB@-2 z)jzjcpRec<9fJP|Vf>aCi*Y?zc)s4gwu^oWwraJIi)_?V4UuC&2TY?DPr%i9Iidi_ zc_|uEq)mT@-gaH={6R)>z8@l@@mjpFzm)up@rT@mi5;3m-0Nhkf5%oDq*LHG;62(H zYO6=N#)}M=MZGx$u{B8Cf-B`d8S6Vk`zq1&4Iep);;wGlkX=T9Jic%bxHriM^QWd? z2+N|qtFx-Lrr6g6_FzW`uplJioM;bjpE8ZbvjRc?q|Q%gACXuI_~3VfA3l}uOn54q z71&Xxb;ZtV{;`jej>|UulRTJ{g7v6z2Hx&HRBy38jVMi-ADTJ9QYa^J&2)k-RHf?@ zLQhoeh;yc$B4oP`6DkX@5*-#XWoS6H9btU~ZA*}h=g6zQXSn;smzL{L?ZpP^;2gt~ zkSK*ev$NB$GTkmfN;c}>bk@4wyD;GGWT%7ASj-Pi?mx|R=Ho)x@7Bujp7tqm)5w(4 z6Dlgvru^BRoh19M+|y`NS=v{ME#>4$hi`ZnhPKO9(%^U3D@qtXgkgb+Iyz5 zmLVXnUq!jrwab{5f3Pem!FyRPanJ%_hg}>GQmRF77{I!Y>W`~9Qm{wTUyEtN8HHU<0W5|e@J+USz zFHRHlzPz+bBT5x==c~{)b&2JIt{=Gct@wme6;im%hmC9J-fJe2Fp(|_590szl(KW0O#)SW z1M5>hM(#jUTB0eEbN(55jB^B1G8O2=upt*jmgOIam!z-N9Zc@WY7ks0NgJVh#LZ{m zi|A|oWA`fOSKWc>&2LcgVqdB#%38c(@~2Y`2`6ViW$zn!diS4o6i4njA3 zyt)RGZXn0L;_XQJ>^KUa=ZcCl=KOcFm?EYxHJ<)32-NqWjXC^$jOl29U@$bTx?L0W zLgdns>~|>xBPPXIsJaICvp34<*61kd1B5Qjdk6wfGd!x%1UEG&G_(BdjJnQ(JI6Zq zb^DXW<-BHt-P1H8t##;g4ytOSGz-Yrq`TDdD*30eAun>uPrX#PC_cfBex&9Xm z&^JDwlM|g%8V^l+uV1q!+5*6V>QBNSpT6rMq+Nf@-CcgEC4^~9<~^0Lv;AC1P{yc<2%BV-GJ1nHq-p)CfIA0OBo*m7|LUGqbzmJ%=VrMTZsTohVoWSDnZNU-Gme$CE1@=5_hTlC_`&laZO9T5iIBvk@+>r60ljN)S>|M4jfeAYT4Kb~{DG5?%8ptt~(jyv_L? zX<^J%T!Wd~3p$WOWqPv^J5ji+vHOYS6lg1YHLFN7|F)3(<0M1aNM0E=dI??ujqPaff6XBB+0lT{xqWnrCd+$t!I?PL}p^$T) zp(p2kq^AZlvUFBC3bk@KDW#X`H)p(X#Zp*e{c#a+Czvj2UHxbA z=cAbOYcFt$3QXfSq#rm6_3*x#UjO9SG1V~P4Cri-TTsFKM7>GeZVWI?*1tpEuuLky4saaycg(&(5JNkiUI7uO@s^WNQ9>5tmdtF{8`i zq5P|m=r{%}#J1>(-mQjw;Z<{`5Y6zvxB@8Pd#YEObWHTz znsEEOG=uNAu6_s_eot=yw2`+Fgpk*wwS7m$Zv5BCQD;Z%9Ctc*|HuO}(F3W_8f>9p*f)Qc$w*oT%39ir>y1FSak1LDn`BRDam z`bXz!RQ7__0KsXBAwWoLoJjZvs8w70CUVCwcyh&~5x+Os^I@@Rt zozW<}X>Qf~K4E|vesiUCjqVg<^v~8U+!@0pg-><%Xd&?k@k$q13>~nr%0#&Kt4A|z z>wR6@(b{3x^<#C=`fF4xmExoGUj|cXFVs*OF&HzJ$7Oz$ zGOV8!=>rew$sknFD+7eJ1N@=48bhZgM&w#=~l6|0)%#Kc^CFILY1Y zoyU|>8+4gA1{wULPw6_O-98MX;Xte9rn1jT(Ww8bF}qfx2BWkPjRdL1eeK2lEU$Pr zo^A{&=BjJ|tAx-)OrAmeFL(RgW+~H~1Mmp8UVcCl&gy+!_$jJqB9fHEdt&Mx9znsa z#UytI!sdOUF(8D4UDOWNHAXp(LA}VPSD9m=YH}>QWkK>pnZJ1hBi^d~$2HYS4*Ut| zs%_dqeu(6tspB?Lq|ZZW7R%Ay&zunhGh3$hX74j2U~R=RCSl8aLr;V)=F_=hsHMuo zEU()x*970~7O6A;`P6JF?d{;MF;ANDBRaO>m+>WPB#$wO-9U;1EJ6P1B__5Ose%1* zCKpKmfaiAb3{S_hh%e~Ke+1o1%uGv2@3LO+(VOV&@x*H~``WKf{hr2pXR;j1_yNFfJY zp)WjtS7>Wgi;%oCy;cN$rUn&z<$M&tq+x;PzfQ0da=gI9iBp#z-f8GbANSJ7MKbIs zq;%@BL>Ko$X%iM_^ik5WhbZWg`}T6hZD+UA%#Kv^H(<4p8MGjF_J+BwEMhl#qVrGq zxiXH`s1ZLbcPN}2L$H6C+j~HTFZ#K8!@4^yO**khE-d19Mwfr3+hITs) zAi3P=pQ#KPy|pRDztpZLbeMBf3yZm9+)nzqNpm(G`L4+JD)K0B_A*rewYV{0^r5i! ziwYUwFj}(!(E2byRi*c8zNIU*?ekY9;Dsx0?fAhz4E8bZeC<(?IU=a2;Un&rFxdEc zo|L)BrWY@QlsCdKnEAen@A!?a!I@ssKN|)b^N8=pIs+H?R3OJFn#EF24GXkFa`GTq z{Z866czh!c^Su;m@I8K3J*~4+OaQL|W=PFP#OQc~J^NLs<7J08^m!BxIX$0}o+)P| zcRycS{6m}M`1~^Rj|IL|sn)+Y*LNE?y#d=r0A5~>_gvjNW}nYv;PFQD+PaCqUr8lS zx?5~-K=A!OEZ+>lz@ITuKr1@KoWvGz0>^<1S{KuJRM?GGe<7GmjtQ)MRcc7|Dd3f^ z6!BTKqmimQ%4}DIT&=^O!U?flFy}K9-0E)A;(5B?VqIlYS!9Etzb5@$DKduWSQQWER9&>@mTr;nrt_PcV8hKKu@^8Vu2yhf+cPL221u*`* zJvEnMcb9m#s*^n1va;8Pabi$NV_WF74SzPjYETDHdqj?D9|^ZJ_8rp0=BdM^wwheQ znpv{YW)dtuU(x*+jo=$DwHv3RmyuoE$-z;XP{Z8oz0^Ie`;C?t>A@gOp{CF@nVWiz7JB&4F3r-iMI@@ z?%-mMw_v0gnvR7-V+m?qk!KX4&^!7EgMwYPtN^BD3Js;}6jRd}OkhzrF%o!0i`l)E zgW!lk1|pBjIzQI0^}SfvGeOdXMLA_l%u#RiK2M zz41}aa^57TpU>-Ia34Y&_zS*}NX*T;rj`sp)AOo)u`%A5eXa%UVHy-Lf8b&lM@qt1 z5m!J*Cy7x)6hW2=VwMV!?KYH0V#&vGDCl(uE@y= zKxrjkM|o8>#v1<)Pr)$dHwp^EN4rRO7;7Wpdo!dAZ+)-aJfuGPWqle|ImSxNYG;m&g&#J*sZ0lL z-j9+W`>8vC$?G54&zI#?uh_$y{48uu438h$hNZ$o&Jrc33qT<~ETE;bH`V4syqQOD zP;xSJzun!gXP`P3TaNIYJA6>U?E`&c=qe4bYF8=Pj6;i^7g3W6_+ZL@wMMFhrhG}l z8e>Anzm~t2>T9ZOkb7ZeQnv8G#PCWEkUng` zDqjd5EF;Xb$#6}!WgUJ23rf;Gw?Io9S{P?3TlK7Qa=slkksy9OdYSc;`o+}9Zz5R9 z8bwdl?-_X0qg3~W0?yW&npM5S<gT2NMsPc+fL$^t+K9`~Z*4AuHL|68RfsLw^2K?xpgInI-JdyrC(0JgTLdfj7^J9$w&9hy;^Q^ccQ9wtJ| z-=(JT^6OevGe2o~KLC^fsaUs&1MiJmW@{dwj6yOOZ)?_#88rm?OouEjP31UxbC)8t zA9;_+%;^h6oI2c&qj`Ri87chbS0J*nf7t5UIFgu4*r56AI#G4Qp0IoO3E?{;PW!*4 z^~F*;S~tU|?fbo+^j|H&cX8GCATf;D>chf2h5`URmmvn|Jt97Xp)DFYZ3loPQNXgO z{*NhRZ%D`v^hhKWGsnAoi)<5#bvvwby+(h15BjQHYZQF$-I)2YJeCGHuws(3B2N#N zS9~EqdWG*?7JsR`@f*wayI$wyjB7ben{r%fAD7LTsr-Wg(_y|pu8_t4UEiXCgB9_I z`1gnoR!>J3te2A16IvU4T!MYOiqb6e332tD-m+*=xnQ2MpEjVAv^p0Q8^tpne(7SJ zZT2=4*USme=&?U8)GGZ?9$aYdgi=+p{C5HHy8O>mQ`cC#{0>6$J-<4Mo1OAorOXL^ zvq9gzytytso39m6pyI)!UAL`U(9kemf$neTYCVLvRE^}3LlHrdI79VF^in;D_pa06 zlqunpl!4amzU%C@v5K7mUO2p~<6RAPe2My)|BBgfg@H6R057h~@JyGM?cRKB}XWkkCxar$I#;ywEqW5ZVvoUb+F|9!vcyk^tMqjc{zj2^)(bXNGG<*E^+HU(n=m^>}psA(_ zeYn3}H<=UN85~dJ)fWu?BVMnW}q@p8}ul zB@xEuUoe@G5~88)ZZDM3r@4u#^put#-|E-d*O^e@X_zrZRv#6&EwRc>9a`g=&A5$i z3hN>&{{oPq)cF8`qa5Ct$KDVuP10O}PnnS6&hYXC;EYD@I}a{;MB3CUs`S+$3KT*R z+RZviq)n2!=zJGrt>-MK17&vlObhA6`iq05Vz5hGqs;*s^Sz*Z@D+R~RL8$QEP(+v z3d-#Mrjnx@j-UKC{8YJj$)1j00^u+2L1&zD$zLvPv6A5LrDg?e|B15EXpd3~Wt_mr zp=G7e&VEXu&Cm`)EP!IsY|Y1X`$L^C2j+$f7K418`%aDcotvV}d+;dPVPyjXqiKsE z74)LU>Pd~BuE1tbIWkAV6LwgKf8#*93zN>1)o>6Fe$rFR16_1{2&uA zR#zr3XoSMhMb7LenFNlC{1Op}BYvTITm!Tha`13ua;}w?njJ^L1vQC|P1V7KHD0(I zS1M=G(eO+t!mV-mR9osc1S!#*(Jt8*UQ!2U@q;$wJD#Qwe_d@!M z>LKr4$-|V087W2A{pfR?qaFoghAl&0ZCUCn-NfX|E6mqIQ_=>f?Zz74rfMY7o zr+bLY_)VDGeOYtb`5Nk-7cC{{u0sp8Ta$qssZBP`b+vT2_cL*^;5}^IXZTWB(2f6p z&r5lV<1A3R84aow%D`RKRe z^Q4KcfSVt;vboYud#6gIx|J{2w`Bau*s6IatbNsLR2+}A=aTCYstH-aa+{Sud3Iw= zopoKh`l+$|h}UL1ElYimuAjEC4p)r+P`vT0RrrNiP$Q|*HvS%d@c&9ah8ALD#SEha zlK`DNootZ?BhWN|VF#gkNOaL7hqLnPvkQ3~ zmpOI_2l4A(CLN0)oFlcozr=M*vpqoSAvSn+>AXK6*Zo83(v-)E?N10%t}nFqXLl%a z^R3eRQl*dgdG~rJ;)@}9CYA~*&F*LNB5M%oTX2JA67E+%a3qhT3-iX$t5jO`uZT49 z-CuLjf5;1H{SN3dR53dcC4^0PDQs1xZ3nFX{pC9PFAFYR!fp-v$f6tIhg@qkKCMZ5 zbTww&t%LyfY4?=2-_iUXm^!TDhbqNj4Ma)#p!(!;LFDaIiM3IKR+&ZI3N43W+}WW? z4<8YGZ;O<3MmFnDrBB-Iq;Y(OSq=Dk6_z@p>((dUgJ8-}eCnslHhH6i>SaVA;$`a3 zwEo|R8e2_t3#d2t=JrE<(&F4q<7tHC_SydR-1UP#6w>|VOh6^9d5V3~u|P77H2TgQ zH9Hn^q_(-KU>Oi$N88~m3*ylWYJk#fx2y~@j~ z%tZj$8=Jt(eLinKa~R?;u^~TZjpu4C&NynozfiJgs)+O#SOv+xL<@>M-};ZNa*4m# z2%KfK39WhQ(pzQ5HgIV!E39%!-Q+Pb_p;OBCcD04+2=}Utu=hh%{z=hVlJpI1W*O# zt>S{u1)2S7uh@zPX57XWafk4SpGv(oCXvO8{w_xUnVO<&K(BD^rAn#xG*=paH}Nls zTq9>ctb@5%lot>7)?UfR+`CtlH1%V3>KbfCg)6DbeoVigV*5OBXv@uuxjHsLwYzq2 z$jSk#V(;JajaqKWA@E<0V)kEbFD6%`U{e5jM^D+63n4QqeR+TOuYpx4H|w+DnB>rW zEKUgV0qDGA3y{T$J&6M$=|~lp)6VqaRHySgI`B=>3=Z~eRt>|O&9_0Wh%EAUmP%Vb z>&WLK4eEIbup~7@B_d*c-%}B0lQu{&xjni`-1UvwAV}uj#CnOGwMgCYuErD-A`&L8 zaYZ4Tehd0SmDaq`fb1}iX6U*<}Jxsfv*041u8I^z=S)_ zbKq0i5v%qNPoM=ptRKGX@yAE;7nd}{hn)rg+#-Ll4FxA}LEn|CM}VTK(hA8Qyw|7r zBsX-_j@Oa|WIy^!>SB*-hI8-$2_%RkvjAdU_be8^)Db6tW3HBvW8O*H&vtuzrGGtf z`vqY*aaaQ9i`O?RSu24b;23-d`dHb|6+f37!Ohr}HqBcCaT%nXg8RV>C|d^bfsV{K zICsTH4@ez`oxTHc)SFMBIQ(=JX#hp&dzgf}9TN$0^V$>ue((A_jQ`!4O`o1fzvtMc zepAj!Nb)C?eEmy6S6c{nJ2`5-OmaZINNpuTH6YvJ_= zz21KOuijkofXt&eO_FAV@mP7NlSApPV$}qjdUcJ4I9wAn@~W&us2Ah-)8Di_N2b^jMnKEV1skaeQ8cxN+jG?sE&h_Aw;&R^LC!B5!5yJVXrgpd9r{T(XwHy!*Q zEJiKU+eou*JeS(VsS)Po%GX;!i;VDW@W|$)S|(xB^3-%|=JRO$fINWMSR$T+6$N*e z^i`vG(T-8OoR*8BDx;N>e|w!pGO>2zGivXZsE=`T7~V+I{D_JPeWO!NR0Z{EnwI>2 zq3Kl}uZX0yMuh*hdG{BW-qkUu;!Qa3g!5J}i|Jou2di^s=G9AOIpCXtjR}{=Gv-iF z?sD-7r++c}`D6OzxTn-7YY7_Q2wC6ab+xM|Ywq{QZqI%DFqR|>`&+{g9KCBUm6_BO z7{G(q*+U%eX+CEmzk1Y~EzDHNHUDUvjV*6ek$@KF$DdEd68Ob0BB?6rBr~)n8!DO` z0er&E2^grLP6qZ66shn^V*Nhg;IoK2k4KF&suEagu3aM=Nv|*##UX^j9G@PJh!~~L zh78nrVAQ0b#y4&tP9a62uR|$t9iTkQmsIFLQ+mJr@mkXU;vOKJeOeKwP$7gJv4%Sh zKmy^(8kgtskQ*n~oX}IvX8!pun^!XVAZRu_Ob3gxSOA;(%}|m8jv$`RNc-3uN)(RV zuP$7Ma;^WGNfUgC?Myjd+0@U;XGIcA1pr(oI00qGFhG7`;c#;Y=Fs~g7j>-fi5c2j zs@wF`ORMPgTkFPdvG=aj!ZI&5cHZ!dc3>!^{2av|JUfHhAK{skq~%~Ws83kpj15SB zV>r`U?6X)qp*Dm*=fpTYGE!_bdFVZ`EHTL04eSR+G7sfiNcq_%&j$Y^Cq;~6Z_x7< zcLn7!JE#|x=&4)V_`Y|to?G{Vg_W2qM;n=&ez8)0VUUa*lcqidnR&;EC@KPDXLxHm zO~NMwZ$wHpq_4JuO;KaUzlNe*P24jMgfJCL0Q(kIa?x*e3;X&fB9`P}0euaFu_f5YlP(0_VhDdT#{NyXK;V`yB)2xo^0ACv>!Iya`Z)IiI4rVK zlP!s0E%SOBfutMqK)jS633t+_u0P0kz8C;K=Y@9y5c?I>H z@Y04hd|&MJ$_wF2{i|%f6>@W`{-%qS@qMXiB<3G+1d_3G=ZL}$)Or7-Kj}Shhu9+k zOKAAD5w;2QStJC}414vE?^&})%UP0t897|y=;7DAi-_atf>bZWv*~>=1iqcV=k>qG zeU!o~wZCDsz=FO*^~}nx#iMUBL7K!)!$+gJyZw}zo9i?$PJE+v7i?ybkW188pmhQv zhOUkvyT}r!-G+~NIL|1aL1X_F|9eEE4M3J_&9$swDPm(OrVZuwSr``c-OZFsgPrDS zI~I$SPfS?}UoS~SKjZVex1~JTp6ed7(T0(^rDAjEr+7Fu$-h432Rc5j)~ZAV7H}^X z^9$qKlNgk=x3Kqdk|lJA#v=8;k9z;gyw*M<->PnKqxn{vAimRIdTRv)KA~BC7P`+p z^7Oi+b$hkHkV&M#mzD`oQyvyXM-P~Z@c(%)N(T+4%=JBl6>(y7E==)!y#J?{qT~5q z$K+7;Pwse{Y0d84tn+En_XbM1(MJl`NeW;*e)ornBQFl7yi)%IFM&&h@_K@tWkNI+ zL8|*np!g{BFnMeiI~0q7pxQGo1CH;^w8R~2w7=5hlQGoPa4}Db6m+?^#U1ya=f}DdDO?d!o))?S^%|B z14GT(>$H+a`GaOPfvcN*7Pr_Tre6kiJ5N2g7S@i5aW-R`v;M~LJ7ws3L(u;iwLQ#}#A(?;&lBL@pc=T=WU;Exs;F*p6@%+AB5&EN6 zsd`t4t;Wxkfo9^zNoBl!(Lbj5d%wiIe+%DN|1S&BpRt8RWW2Ex7)~t}a=}f4i~%=` zrV>F|-ghz+;ZUpEhhTf;>2!_hz=LMC8LF0Sd2J;o&NhiXY93K&k)AqJjm3^lsP==z zUTbN9YN0{a%Fm}N><7lZqJdC2_Rq-%^A|ceXXlLlUS6V<^2Unp{E?gQ)mn7tbNMEbO<>w~$-vR>cFnT`aUIN0^+-$Dt>ov`ghkc{hE>4%Y zbi#93`Z0B($v5~P^p2P9epJ9@WTW(pzS zW2UY$?!CY9<+=59tA0`Z+}1Spc{c&!H(mf{^cj(dKEGWo6d(I6uro2)PaheTDgf2o zyc=^tbT}mkub0<*xB-dYbpN2JEiL{hWoyTJrVIml0gMa^(JcOJ* znNo$i_2pYQ-~?)Mi(8AB!ijVj6EF)d$2}H9F=!nf4El0gytKC(ueeU}a9`NHiKR zbt>&JWxv~P60bN*O)kz~q0~1l6^XMO{iI|z2hKgYA@v(F>7;p%Q1M?4N885i9D9{J zO{m^FSb|^I{1r^Us@d_bIq=7b19WrGG>xAvj1A*nM_!^f5T(+_ zS8-r*SqnV1x!1)Fga&|wbD(@S3b0`e*lztpElgX7_Jzk`jE|z#46DPJ}Qa> zz^gf3&pEI+bZE2G^lZx|yDKD1H~5g~6XPU~My;{5G{Y-CkKq7xM2rLn!?FN6(~f|M&l5Dk!LKaIR)ZJaEKxW7fn1YfzUF~veZ;Oog! z&8}&a=}6evtAnG%?Lx=~TrM7QA1EK7aERA>Y-+++p;m*$k-`MAgj1*#U%U^`)-P_` z{N*hu^jvyJ>wTDgG~Dd5#fp*wfeKDcp?dA?8`MaUf4LLSY;4C`V2DBP44)tChd8K^bZdL zehzHu)1dkB#}*ri5L~SP<_v|0TAkBI+e!M!lS zR}2-DXH5PYzwE)ve>Yc7-EE3m)!}-y&uZ1#7Sq3c{ISFhI@{OherkeX3)|+ zDy{zzE{WxfX1kp-ZcsG#mn=4Vnl2sDYlfwOmiBN%_#(nl)pW$=pYK$4Wxv z#PzcU(PUzcV1xpmT@->)8;B3CB>eS)`|l0mG5_ycg)m%DB>l1`Cb{Onvvk)o^Pocy zYo|nu%zf46T$A~+^rmR|JfuyI5o_W)B+rJwD>XC9Uh%MJN>?O)?u+1D<5QI}?A*aE z>W4ri;9%mnoDOl=nGOGGkh1VDu{hx;B?Jly*dr(}F`Fo)w zHjw}n!9ndpjiMBAEtLQW{$57ETk0L`6of>=!qV;FXHC zrG^)3L!_)5`II&@5JI8vxP76eKbcgQmX$voebTLYIc$Pbx-U3z@WzE^PSt-jSXo0& zJ$faVn9*!ay0c*IQ*#nC_fn9+&mk#q|@ZG=s zmB*jp2U#3M=yme9bB{US@mT56-rB%|zxQ?n=f|D&K$+IM7Wv`!o?Y=Y#!c7wc8h44 z!qmG4g@5U?1$#rAd^+k79#qy(p+ImguqF^!2zGqa)$i@*G=K^1s3$>pI-~0dtca>o(1=01=Qv~@um|d43tR_W4wuw;mc#M z{vrhc!#hw~sels!#@^u##2ea;NKFgRGtWh`33MH$M)Ge?V{VVWXChA&`lP^-F;M(M zyw=?StGhgpUmv6G#SJocX@_0QcTrCUFg>6pt~>2vAM^5j^9~z&)Rt>%r1SRs_p04+ z=$ogFBBZ*8QHzvu*}cyuYMJJ@mPxmTe!5CiMn8){Yvv9D-%bPUB4^_B7YGnaei;PK-9d0}w}0*2Hd8^L>7)OKFZ`rA9WFY1!!?Z}xme933;3e9_^;+_M)m zSr&hpQOK9~L>;DuvHu(A?AeTcLM=($EXjjj4{OC;&|0wN<}wZ+VaJ$b6vt-1`1%Gk z(cJmi^Pm2u-XgMGq@ShExv!Z)rK<2s{0sOwnVS9zxw?jNE|^c$L<=_hNyiAEyC}co zFf=7bXu~@+$;17Ua`8S#L}}~SBP0kR0bR8qaPXbV{a@zlvb8FK$f0Kq3>3vdcwy`T zdK#$BAQul{TeX%scGV1l8(5{lNSXC9$TD-wg=n#k2r-;O_rKJMlTHG#%bShduO!Zd zpIAH2LkSap1kC;aGuF!+dM+C8f1>F08+5&plUE(}4p{XoaLqO?J*+A-IP^T^UH>q* zefG{1Z)~2%&0(t`s28EQ&{$xRWk>yAi;yJs=G{Yb2fxj51k~oD(aVCC>8_+*~nYxfr9Uv&4L@Mc=_&K3`3r8;q z7EseOCLD_P{iiV9$#@dgQgc+o+_+8F*OT$U&;^E!FY$lE)?Wr;K}@xgIf|=J^lQdp z^nevT(|!6jk(y}Q?#;;JB;C`K&!&4mAOw}+hPdkE2ME+&Zv4UT!2nQRu7TH+bcXu4#8p*37g%TQK!M}1K>VPf(S{47LjT=+73vAN@ zN#xv{XXRu0DSAAwF%ql$h%wU55L&7HDU>fZAesOM7Jw%nt@EDyMW))rh|P-?EB$mj ztvjP{gL1g$C+n5+V7(qqv3z^t!wL%!qGGhfd)-dT>bO2WTLviGcJZn~buwgivfD!m z+0)d%SG-(UjSMA;W+;ub;4B_-rZDJp*<7_EZtNe9x>4;9P)Z}X;*hO%vDE<4$b&Un zr8i6?c+8&>!ajAt+|$)DH>eQuqPMO52no;qiPERTy_T3Cx3Vx^bx3XzYUqo;jA&gy z3>>~H>ow2Bx!a@d4pAv7?vaWEuk@*)VOgcj`Z^m06EK{nD!+(IVxqiwVQTB#l%kJP zJZ)X`JyrL%zXnhvzj*bcy>}gnG@++?PY?dr)->8vS*Vz#VL4{RB8T)WL@ zZdj15S>w|w8b-sD)8Rm9?=8_wav*bMGm#V;NQhxz8Q-hi#~$y__&DO%!(qQz>ELn> zEm`cVBmrh(m&cO8C#c)TNVfiRQegfP&i@P2A388><-$dmxFX-dp32l;qT?LyI2J{Z zswTEA^EhZY3PeSk<~dG?e;hvCQD0>f3>`TTIM#BLE1w$6F(!oTqC2u7O_o?l@M??w zu(GS_z?V85;=Y{l(CpYuZ%JvSl6?m|?R(jcIPCcs|m-M^)Psw#u z#}Z!ql{mr5r^?r+<7BuhIbmQ=(}_^PO?ap1n8$;ajPP5v4v|ay0l`5ufFm9pW#Fp7D1X1kXp0p(#r(zkD#KiYhVz9Y6bUJ?)Z~cnphhxe$xfCzs2h{M zr)z8hQ42Q%1_p=CG-Pf;JVf~H2=gx(k5Hs*Ml?h@0fq69=2k*6werWu3?%Cgk%ze4V+T7|0=jHbuj2M4RQ%- zbEAfwri0*{#M}oZmg_Ifn~_7rb{VPpPuj5#)s>DPTk2LM-2gNvS|*kR=#y!>Tk8x& z{HYdkeP|v>{1Pi&8I&V_a4omy-`e)ntEkb~&>$M$xLI%$lkJQ0Jlf>1<5&3~xwb@T zC-C5@w|sZ^kJ0BoL2+T8Gw@j{^%Hf?wSHB69oIZZ6EjxAhF@xakxx-#SVRBtt1M=$ zL+vH<;?q2?S-_X-hAXx~uo5cPufjf`8J{2`ocI2?4jn}v61UF9u<^O5@CVgbtjHF7 zyWXNG|B>gM^gp9yvd7*}%g>r=^ks>Y0(+-)Uth^1{Q36`Bw(pgFv?F(85bHNf**H$ zram+h<5yyq@adAFRCV;AXL_9z#hNJ_Dg6YLsJh<(a4GQ8zKw<_eYX=+#3wuYll{RV z^s~6BP<@1X_`zCiCnhL)$NVHCokTt_|^zg2` z2V}4UOF0maB*h3BVuzF!!YW=czxzi#igN#$U3aCY?HHqy1~Wr7S)$~`x&j>oyEu-j==>J*28Y1KjVc;Ik5W1M?*70Uunypx!8lS$ZM zbM9NJqIETIwr&wFIZHP4B2E{a7liL+E~6Q6?q!`Gx(oZ1 z56cDdE3Uur)ovX5QfrTF`n1*v9mDh729u~@dF=}+gR=nTMCP6iuC&f7kfWt<%yzK?xqcbWx6PXvm?c$LXr(S#-EXJ+y zX3NFzBgOb}z(K$sf?z#=Jy<~Hq5P{dvNUKGs>v6UO-U)FK&|t_NZHe_R!oTyN$JLrRos^YIJVf4jzRr8nIM0y#96g9h zgVz5Hj)X;0@oBcbIp{a=E~~5V;m4iU6PAJ!yt{(i*)Q)7{mE}fSJlu?WPq<)2%7#g zF4qA|>5gzlF&=(I?b;cQ^4aW*o+wqClrr`8!l-OPi%Me-D_%Q)&j7gLun?~k{c1uj zJq%%(+`}aUFizAveODZ*NKAr!7K9T4 zuQ7w_Z@UU9ycqq69t^16twk-wU}~EU7@C=@4Xk8@sKfkX#IE+%!T#$&l9^Q-{@9ni zqEEpdmi`X-2sGh?B;Z2XRA>3F>4+;O*3*DpV*Qr^Avp0Pzy4UDTJRz#x=nxjQGe~9 z86o|NB;iUwlSe)6BA6U|3Q3{9NKCoHNX=QMiz4P$`6oj8;%(1P%V4w=n+c!;+5E9x zI;XS_`@N_{`=y}nwQ6fins)K;p@jZ9_4s&stGw3A8e2cVV>$T1!Vw-YEIVYdWPuep z(Rt&fO(Cr~usW*AJFvQB58~NvcMyyK}DFmm)Y>9ntry) z2iAfC9AZa9JTJA!w3RPg3&yog7XrmhmHIqNnAC`*n2VzHKF4p%kv-NcB)vBGi$zp8 zcni$Hh51(V?yccB`j%^%AE4Z!54;W*{|O~+rRbcYVfSLRTUzy;$t#!hO2?nCUv8(N z3=!gXV2gN2t=c_SKv^PHB=i&UWtzaI*JLyUemU+t8-ht*diO?%#c%t&2!B0>9w zfdvPJUYQ%OiYacgXUIFE1`|yuu6AEn1bIrN zZo#VFwC8n40GAmF-kIB8lz6`}VjrL0e=Fxj&CusZqx9iE8eM|+RZkp<{%`6s95CKL z+c=vv;moo@L#$sIA7r0=%aFpQ4KVq6uY3jN46`W0cK0BnB{Bb|{W>1A{i@-YoplEv z-+k(XGDY~40h`sK=Z^dm!Y9ly=0hg^lDqORM-maYpI_%*YLw30iMG`oY)ZH5qw^pm zQPBEtU2}h!gkhkWW7{&eQaF#ggXO(OX`mZ}fw&U`2rtP0tPqCtGe6+=Wyauj-E(g_ zTs!Q?TV+;uYvGEoIv(q{!w(2ej;4&o6^t1Fk!Q) zc`6uiLg<`rv8O#PMmwu<nEa;l1d2;}!-I*0wQtD6bp?$#YusYlJlygmD-UOhX3X=?{Fc=zT_&C(Lj7 zF|N6VGJlW2nJCWW)~~kUms7r%5u&(kP6Oo*kMwu_2b1?C9`c-%GF&Y?7rOATyf03A z)f&jv-giYaJi1?wsNURi=@Xtt;1JlHk<_7ml$?+NRP1uG-Bi$x5$~HR4yR$V_bR&> z<>?Y6B@tL!f+C4XA~la6a{XRmJHs#hN-rtN>oqv!AcPZcarjHH1G~c4wC*~K!Pt-o z!8l^FZ;QLH1M`DS0NHn60{S3B5K4Vm&i55Ywutu9b8dWY78vU?UK*JWvt3p^zfsI= z@?jKyLM@_Wo)Ti zuTdwM3>n((U-UFMlp3#34h-LVfon32wnJ~=31XM$b!@LxZBS6Q3&nrqJkKT3BpYKa z7n?L!saBxz&a!z?j=+9d&CP=ByFhx=VRU--aHM!iO0B>4iVPQB{IS zXYMKP&LK}9;PG**OYC{*!td_jr2I|P;(Bm@Jw;2L=gc3=_=m2S)S@ke8KSNA)iKP= z>b+N8aoD*&|Dajf?bn<-vPVCHMX0RLtk6H}H;ziayov}y^@P;h8biV__uzOT(fu!@I)UD0a<+8puBWn_1f<=R2_(>Kt^>R0N~wi3-- z9i=Di=9W915@QA(guDI4u`D2J8}daqR^Y~JpT$5!Yprd@Q5LmZ>ix*n z_Hm^7*Sg)oISd*1Nh5@$dF36w;m|e|f1qs}lN`uR<_O<@Mng0qDq(;!QVy5SQVpIl z&Fd++F_+!?c7W{ReiGCoOcSIOIR_zoUaPl)U5HuZL$)`yu`z@!r~dw4-nVA5N( zkCO~&0?wzy-jBqP%Qn`%2}goP*4Hq_RmYF38^p^xNQRjS0jF>)%~+MizOW#}XZ8?w z4ulOX`i%?p*!D9CerTy3pBZ^7(U3axro_MmTRIw_HIolGhP%kW>pcYk+1$+u9QiRU z!7x5T9Xm#bkk%cE*M9jYWz7wXn_nyhhJ1Fr&KS~V-vpm$)aZWHvX5tq4Jd1U6=E{jT(^p7!s&yr?vINm5P3{F|-#?-zHbuGa>bT7~2THPIFAES})HP~?k;y3abHNdQ zZR*bCUlM?AszA?lss}(Ej&!t9SRXU}#%E}s1JowYe8TBxLcmvDmsx{d;#LcIo9Y2V zn5V6|D~rT~J1q&4S@g((PQz%W=POni&y%fg7@`|^VkeV+I9I?U6Xwu_lJP?P4Hc2pIrSm&<>+jl2g3!(E!9=i$A<YPr>q?I6m+|e; zg^Ioz25L4oq=tI~2&ayQ0(1=+i1QRfe-)|a@hd>V_;Y;ye%eL`UK0S^&koN zEZ(YBlgeXEGp+t%)gyk7%ZK!#xs2`RnMy&_2+5-l{Q}H^VC?ssm4rI7#XkVskiM>E z^Kid130rA1pwi$^$GzFvd8ZUwpvWyQ-x8~qeVo>rlIE>Ek5NyG-60L%XRHd?Os+*A z+&1;TS5*Fd!3VnQ&$MQsrDFY)bLlpd=%+_UnC(**F5=_kea1qzT`r&|!9Ad|EQR z?nl3-F;~UeSsMAskIbobQe{fv0dAKLxCyrblAZ}SCiuO5CyYan1I|%Vi|Q1;Xh~t7 zedZdtZ+~L1i3eB;+x45&72^Qa5#v!|D!FE+c_N1rPO%gvxyz}7j#*x!t_qsr1ntXv zOz&@dL_MHA{>!H3{AT~l0-#x@1F-{1Q+U37pu2v=hJV)Qw$=70;I|G_)T|I}JOAE+ z<$b_`MDICWrXn6g-Z>!w-X-WggCR*gJbRYjus$G{;F--Xu!QSo_7$?BUHS+NzY%l; zBS|53v(l?B^EJNl9mZjwzV|~zW!UK_8&$GKc}Up0;F;#R7`;YgGHf%VT&U>zdHM?3 z?mm+rLRNQ!xB5v79=`J$ruU>=09eMG%z>&W<_116NzuLES|5Q-42v31 zpJv#@sQwX>ik+FmNpqZLVg}^eO@8lVa&O7F#wgc^2 z7tlx1_iM0@FB8iI0zU=#EyPH4a8 z>j19^Jhaas8a(WlHOaARm#LofA<& zd<$hqyjIUU*&j*{%g|X69gSR<3j;51Jn{QOeDTARu-8Hcf8$p029Eu<*e?oHk&ywj zZ_+T)kORd&{w|>dI|qfMds@8m_*Z;Ees|gR`XN8KjGxD~9ZTar2@3R#GDk3!C5kU* zur#NqDL(bdn_Y5=-JHivfh*ay{~WiJ7egqIpAB}QPtm=2htHImb76`9;L|}aJnctc z`aCJ5`g?yF^df(DGxMcdq7%toM-s*^son@qsOQd@%XVdV`6?Hy3ewOGE$34vT6XC1SGVeesL`JU_bZr-wLn<`Rw6L z*Y)FmF21hSnaig&9r=nTnDPlBbxfkT4JRZ279K?M%jC*~d7o|xHQ)h-Fmj_MZEPV1HBipjgS z)!oIkH+8^a)`00ixq%<+W~kSVL@qbANN6E_?x!9wi94)8ynq!sAVMfMHM-uTh^DKC7^+z_vK zp>w3Nj$Q-gM_fOLy3V+<1_~wzEd8)_)rE;t3Hn2kS|jEm7$$*)Z?l|o{A|Qqug=Kn zvBRo54T}Wkk9FEd+S$Lpl#_jf?;qc*f^lA+Q_^{D0p9b)&7@*YT88G5QCFL6%J=0` zH!(~nj5I~Z@3JCoNCXaO0Z5&5U1cm7q=8s(m*Ow~#uz{AYV8+%$!I8^#iuQb_Q61l z@tF`peC$dH2ILa)-6Qp$0ICOAlgl1&Y;rf#r47t@sTQ$O@9g8rxY&5li!pKnI^D6_ zevC|oBjLP8VCMkRf&nm}=B3?iPlS2^;=3W;^SZxc_hNB>v}bKTgyNaPzB>X~9kHiF zG+LPGTFXM2x?t#dhC-mp^CKn#=W%M6--LK#0bAp0H5P^z_quVZCw9uPY|%}fREQ@M z!rAFrXpmUy$$%XIu=|L(1!-2=dy$CG9Rde`eaWnTMe%>CoT%f-d03eJ2v-wZAjooG zSQLp>VPutGjD9hE1scU6IUoC@p~;80zhxQp7EgZ;R*?HAX~-zd&6$3(Lt-B_K|{lc zbcGly74qiqrF5lGDc;!}{yudcFzaWeKOhHJq6XS9MYw2*b(hnFlksit`|sju>~n2@ ztV%9z@}pw&xyr+%h6$|6G1;wkEx&->5C7^+XY~ibt#P4j5=bSD(U-8O8T}gtV>CmA z?den6CaeNi5F)MrArq9e?6x91QQ(4Aa0}NJw;I357f6 z`w605p{N4dMp9Lx$OydxlmQ`gqaRRH(1JdY6g^?&u^*SzynRFQ21_;K1W?rFX%GHz zz}4?7bgP7{A!)|to*VMYS3`|@7Mprtd0GdPwUIZ31uO0z)E?w zfDo$ut^6o!;mei?4LJ&IeT#jJluigR;7S=luYK7c(j%I8AC&i8ZF5eOxh=?kwEDQc zG2-^GTo@*l)S5k5dJaU2YVRnoJ^gpDkd$DdAkgrEmdVJ6T>eD{OX`t|fu}*!*$NMe zTX%r=3gLC zWrqX%5|`p-rTrngt7m|G5fjP7V{lRbt4(%E&tq9w)lO}*y|jX;-u;9xNP0(&4*<4t zWJMuw@A}81XTd!9M8Mp!i&D$Hkv8LA?^;{83y!Wn{AVBVDvSQzAo%qW&YDDmBQKio z?u_s=z#&6=F%$s1j)KeX)VCeGeg4zQ*jUg<2|RJW)?T9%^fnv=)0D18qAHb)QoCdYj+7l!^>4$>bD{v=v>S| zqtv$1Eg&-mDkv*IvW%6l?f6?^^?)8z#v8yKzH+mpcSZMoYwsnh^Qdu3VJVu`@I5{v zM#>n;NEL*?#Ht!xbREu5G!$k7F(4#Kd3q;Y!^H0Ty3xWnnS=<`L zv1&T((TQxEBf&YB=WLA8@*`3FP-Xe4ldylSWQ)Ye)qVi(t9M-OlfZ^r z*8t6lau47I1{z9H5IHv$)N@P!UUFs2QCpMprrjR@a< z1ybhuvD%`t5kVja^*w}?O`|&MxAS<)`ij#?ioVy9>wiW7Qe=?R_f`^PHWha|9uj| zP^5lypNnyCD&{1U*35*-WQ^Y>9Q-H|q3FD+ZQ^n&Fo>Mew&i??N?WEUGNe0O*^z#= zE7YuywHibeiWb`v2SC3=y^bMMSMm^0*GAxvDiT8GQ%o8&t6Yk_drB~Z z!cZt6e3lXY{^mPr2|>KW!%&~{j?eiy9#b6t4XObh=EszdtYjIN^}}eVKgo<<<%;!h zjAtoHl`(|kL5P`Zzq;88l`%C6%wj3QK9{*oXzCVB=j*UgZ;V09Q(z$NLRw%q$+>o` z`wkrrauoZ;(DTJHOK^}Jsf5#$?*KGvKp#5K_%?j-rp?G*&q0=@XOglvIoNSd#COdo@4a)J!0)F@6aav>y> zOm9?9UKr+cAGZ>=2-T5$=xT9L&Cj29*} zprCGX&O82<^~vB&9qsUl8>L~iM16~=3xg{P_YiMBD$MITis^71WdXhCVYo!H8(;YS z3nchhP>1BIsVe#{{i|Dv=&ZgtApE~BIzp2 zbao$IaHU(t@7I()T+u;e$XX1^s95WpQ0(3D5aR&ibun7zGk8^~O!eod!*C=G77pf= zHD;7}`g+4Ocl&eCq?#s1*NqI6Bz8;WAwI2~;1OiBD_Bs278F`Hy)ImpTOo{m$!r7N zJw%6>^sK|7q*OX}{Wr+>>j29{h1ABQd#p<6uaUw@NOGpY!jJb#^iihQ@LzOXui@Dk zjTDs-l!I;(vSxE*d+x-@PHoT`F0;M`#cjuJ<+z;Omsk(R_ikZV$Dsk? z6lS@s?WP32`V+JVu)KpJuH3M`U8a?l+I&$^U+`OjAZ$3(q+AoZ#d4ha7& znoTP@ZTkAtQT+F9O#fn6m7Cb3fD+m!-D)&}A_(|#uV&jHcV*n-nts6jiu;b7p8M|w z$2Yu_nnA*12#>r78h($tP6PY0b@ENTTme^GOYT4ENOggBKzH>?){dA~XFJjbTgSpgc}W;Horno%q58pNqFvCOkduW|s0i!Ag4Y z^^xyLY?9U9ZrWwUxZA~877MZVw))pZ9B-$x*9!&+X#&lK7&_VRI&kpc(W=>1M?b&@ z+!8}b{~dwbqJKhVEeM3Nli%W1Tu0ws=?|&$9QORG z=nZ^2-WCFJBvY!vL)b%TZNWMzFc7nPnp8lINs)J}*sXpGvcguBkBoV!y zUN`GCwXP%s&bv;0wQCoP4!ozBB=?aWbQsb;LipoRupNI?^jXx)73!}1svf|&JW1Ah z#2Ju$Ew5x}yK}(%t3y6-!zG5I2*VbeKs@Pf`(YCZUUu_OW_xG;3vtxmGK0J z`U%~(QPEEVp`?9${b3Wr8IlbT zoJG+4h_*bt9<&>WO>Kq4jDy>R1e_PRDt%4|abupY;s)J#w7E~Xf&dj{dis@ftDSI; ze(H5A6gUe09l_(5%0%aWNhCW}-blUQstx1H42*}B`??${ItRMmW{pe~dq*mD=D3X- zjd}zwx=Y`?RTX1ALAggBG%4@+n6__;pGi`y=TurY=LdZ=0q}Li8KE}d>Fwp>lcTQ5 zdbv{}R03ecbiPOtB8eCNEN8wY@0=tywyC(po*`!_O3h>Gv_O0}F)!Ewj`vG{+p+aY z{Gdapn8fd>K#{W=*tsU%`j^xDKqLi+Oj-VVF1~pa+5#TE2bFtJa)8Y6CuqwTPNLSU zjWbG>fsy zDSqV3-xWm2PQj`a8~i7v@?~sgaY(=kTL)3hhp*bFe}IWZ2*Q`oHCI_O0hzyd&I$sU z#L@-$>NV=$pi5RzU`z0yBWw3?_4u08=F-i1M@3oks*aN!XU5BdhT}l?Q+7mcxtIw} z7?4WMnUa0{EQ7Kj;tbcv!MSO6ZIT0zxFbS!x{p1nfXrEFDy9|o36>sjO&}Y<7*?Hp zhik-*+3-LK-V}REjLo@T42`-XsqJwxAr@$|EYfaY@dN*)jms+|o*$3V>=3|(PH?2H z7Wm8OSC4KGwFXYaeaG_s=c)E|ARr%s;__qg`!gG++7l&FLimG=G)}H*cZ;Zi=hESD zi_xD3jq{?%nlCRuPtpWv9^we-He|?oxqI#$4kc1)u@e>Gfzznbl;F^I04Rtc=atHK z_L4Q=B5>1X)we``7a)+|zg1sqM^Sjqo5f&LxrWrsDc3ACuBCg@XZZQBM^rlD&)+ zNZObyOr<+3HLYIgwLb#?y;k>a_e6|Tr|MRfC?dnla7OFx zb{^$w^ii}R&ThGzj(*gh6FGo>c@b+r5-_FA9(j-Yu|s;zssMOW>a!&T7- zNq$8pp+!i^#_4@FieN}t@pytIoJKfYN_%(%?g%X%b>Brjn~G5PsuXoXQ{ijLdfFPt zjPNz(m-Z{2|ng!zJp z*BKLYemWsFp!W1^$b-;t-kTZpo)4(h%jcsM% zRPbQG8sXn{aJgS|Qw%}e;hVwgFcLjIGQ@I#w{x1mo)m>**?buvY2X{UT|-d+k0kcZS?3^EzWQrcbf6W(_m3T7GMAbbBch!ItvC~13MWDj$dY@^amF@_sqG+Db7acu(bl~N zNJpMk-)CXLA1(g-0}tvpmKX=Akg~-Ndp{H^sqnAI?d^Qte`!r4k_(I0MWWus#l`S& zI`=7!cZX$+_GfD#AibIvQcQ#8Pb$B53=b-+D|^WQQS7^5^0aN0G|8gRw|O z-laS}`*&jjx>pE$0UgZJ{(0+j9+7YeafiZq9?>VhZ_avkZ5X@`HY|~FFVnbiST7j8 z%}_kYmKu(>9|44iFWJtdPV*p|uDAZcK9b0qnGCU4b&Q;~F7o|hRWA8itqjZCqCh5R z2c`ZuWvcHwj+AX15FJj#+(&8a;7fM^@ZGK)ZFYX}^4~YSU}V0(lMZdwTkCyxvdi|e0t5vP<-)@||*PBm$W^?b<<~g`Q-V}@GBdyzcbazBF3AeoeWR!VF~U>B|1`$`6g}qRE9a! zFuI_fmEv?$uog=!b#fT9TgoxwZcviF_bY=0(8SP!zZ}o$)>x({94{~Uk;uSiIaL^# z0RUZ^SXON>`M!ny*_6g(h8^N`aA+e!ZgC^I?@hgm+t?r9VhZ5+Xhn27v`Okn+Sv_- z6b1IgWTe0flOamdIfnhUUEU@`UOkc_5TkQDM^29)fbSe<_`wGLM9}c3@txq4;3&4! z^|GBBk63#|!-=*F9EzRjI?1OrJIRmW+YfS4{Kb6md^_!T6w1S17t)#t3Tr+07z$?S zOlvcm1VS!1x&m69+Z*M2)9H9A<+xX@oq<-(P(cG1;S~K%=MM9moEb&c)Jm_`c9>uF zVm3(<6P!{$ZUrP&Msy=fWK#Ai6ANyqRCOKd{#*=|``kzWNBlG}C0PiLO*E$o^$n?LRcYTdZ2#hHA`7lQ2WGra!{cm*-a%LtXxl8&IYT;XZ zI`G5)!VYN>07||Nt#D=0X;#k`7!wb=b$W6|3Ea*;x%4jlPzg?M)no}AwUUj(VA)^V zmE1!!fXA1v4%HB1%#r6q0$RfF`MDHNb}aB6>|84OVsFwJhwOh`4HydzlDLFF#nOM~ z4fx3$DSE#!@su>yb=7Jl%|4}tVb~yT^Ei7o^PtJV2p2c4A#c|BIgZ?K6C5i-N;ODV zg2DLsn!*=2Sa`Z~k6)}31P@wzVLFiE4b?t1OC_SD#X#5+SlRp(^##GML{%aB!e&)7 z=sr~h%!i1Ta{Z=T1Su4?mV$EYs5eyWY1vAtd$#|5POL$vj}Eky`j$a-r5pj6n1|{n zg{yN&8#|z<-D)_T9mosK851P^A;n z8HD?N1^y%I)GOShFrEbu!VEQQ_O#Ek5WGc!kDbV`>?I!L`%;ZN>L;)zRHYUz8q!2$ zHqn+FBrbH*O6%KAVF-qkC8|TW{~23-sr5)*PeP8E09kI zHWF+ISxNB4cH%!@T=cca^j&}5cf5SwZ?G zq?s72%Jesy%X^gV?D0ZTD2+XoQh!b6R9b#U7VCr1F;U7=!k5=xxKWjyK|G8rhRbsd zWtfBay(tARU<4qO(CD;~&8iBK5za%$969L#7tZt>lr z8NM^p=Ki)eO=xt@OZS#l1#K`;P_xx|6e?18QEs{YY!{E`UrG6^*`{HyZu}*E;bVtA zd!;WmeBy^6j;JT+3$zqcjHkNQL_a^LeQUAbXs?>NZmHypyjszAsh|k~@H6SUY0G2h zmX=v&14gADxB_=@p=1PSvjO{UCcXEy7X6<@{t2Cvu}(PGrmh8;TV){uJ#IOt1;3M# z5r)sqni(tJvqi=DDB#4&cDxqS^7s61wG#XD8^S~Ch)b*Vk?TZbwPrU9V6ay;Lkdp_ zN56-5%_mjW-rT3Vr@#onH{zZ;27vUuc2UM1_?#StYF+g`Ai*_nfn5V>&~4tQR{|(I zf#RrMjdevt6@+Jl7!j-VJy%Lz6=Q~g5QWkfA{*6G?#NVv1Rkqb-va7yX8*KFRKCAT z)WnVlCV8(zjkQ?tS1-*r`)E9!}bQ{?@H7 z@DL!4<*Ec~k2l!6PaMUaEvfC26q+eAZ8(#!OEdU@;V(D#fK}hcf;=P)d~eaa_8OhisO-k2e4mIsBqG{$DM? zE1=R~2nU%6#K{l@IKt53z_fZ$r0@mDe@M7W6GG)_>l#%&L`U9bh`824eU;>-mH=;> zK?uR+$(1&jT5SWqpjbq0rbA(NZ05EInVhgSr%jhGvmKVD6=9?2ks6o94)GGfwzj-^ zrhpo#fcz6-pk1RUWFbS^?#pGd4V*kfa(YzYn;If{P*a0lnI#+$cerLorbZhT< z5kr#95QW;*)wqU-KBV#Og<#9xcgY}V@ z_Ca)DN_D?R7umPc$28xCd^*ber^?rjt)#ze;pnwOvW9!XEa6;x00s#x4g>78oWq4} z8sBndIuF6K^_Ki;Q<)lz-P)>#DvW1 zRPvwvRc`2Xo3XtNgmA16Z5!gX>3M?HcCQR>yI6LOro0O6yyFeY z*Y6kV=g*={Z=Nlg90D~GT9Ye@KDE2uso(ZZ!rKQF$<-3;-!B^+X9CDZ|I&{*M@Zo+ zo_emqu1b;-{P~nYw~#nvF10~-ZA_Ufu4-a4eW*c~TgLi$J1E+V+x&HV-OJeS!TyCT z<;<(q7avy$__FT2sPEBXVdguq9*Hb%<*z@2FuL50Yz6?%?j-smBKzmK8ppKxMGsP? z4A1zdWj0MIX+)Kv0>__C4s;Q`k>=QfgN_V9$b|knV16u6<5e~Qubi%Ylhb+Gm8(&8 z)gM(_xESRdqfmjFac>&t!_>g!{eo7+GU2iGQ*x)6&up;}xsH7TxX3AM@2-?Bmog2ZR_W0iEowa%s3V2$t&Xc-uNUQ4%olZfEL=fii0SG4ztSQwUH7=~#00VR+~$G@LN9a9VtK*yJkARwY5 zZaK0NCD^p=%V3ES1btHInq%b(YF6Q4$i)(^26IPG`12H%_;}7-N>(LSR$asaIL-uN7Mry_abGsg7pJ5c0%MX@UHO zDqhw}ov>mm^g7nP$WIft2WotI_irK+9(}d*BybS1%e1S{@$7Br;MGpbjEm`;wVlzO zG7{M2^P~9aj#WPBsHcz%+)Ay?5KL_Jq?~L!cX0 z3Bl?XSXUI2QAN51nsT#=q*#;{h~^O=aAEyLv0|Z7aWIR7_31%^D5Jdio8QPr`T0a& zSA&6awaboClVkN|(O&kTM8R*RX>Q1Zo=5pR^*GE7WlJK%e5S~_!d+B zcNq6MUFE7qzOSp=VG>rx)tucHHi|KMAGQb5S8e%E`@7=rUx9aJRy zXEKklg*Jk+w_Av^p(yAmF>HmE441mQIf+!4$~WfAc2u^kXBTiv14Y=s z!3eya3*6@w2DhC*dc9D0{e|LsINSy-ij1MLGX1eBP3bXF4ZU{OFPh$pPEbxl#YN6a zI+Rb%uD)G87=hnHNj&*xqJO2Yzt0frQ?HlxNbac+nYfNrtcy;AqGS`^zyZHq!6D5= zc2=@WWH!_nl4Cmb1W76QY$7v0!k3}Nu2+*0t9Gb0s}nxkc@9Wvo@=5Sf3r$YV3$c~ zz65>ftei(dwtj^dFGqc_uMJ2Q`$25Ybfyl7en{c6Ekp_7SW9Q=%C1g_bCZm^{>m>a z2S2ghV=jTOmeS8R$0}b?xqW>X>^?V@j;H9~eKt3{s=_<1r@k;J`C5@09q7FVZOF9v zNNf0Gl8Y(*&l90rmvqUhcJbioGoVPs5U-wZI)OH0qh8kSI#;WyT}3-DlQwM-LR#=| z{UMJzILJ@Hq*eIwJ-ZPNKpOGldM<_nvN@^xJ9W~34+<|d9p}@}bmsfC2BBzh5A|Fk zmo|Wz>z}l>kV1Z=RyxFWdzbUX2bYmn{h>l@H=jfLLBujcoAN#?xiq==T6HpwH8^Al zLuENI;bm`R!zS-vUt2+KP<9Vc>%An0nOmu#k$}!ig9~bGHlPQYzC@KT_Fmn!O+!`L zn@Rf!@Lu-kuJxrKXxhpx^_MPkUJzl0q;sxUoMHQvO9-Vnrp9*L$DW+p*FnG5EVvqE z(vLw0GpH3pt&;+g9N8Q;SF^wh70=I+bba)PU6@VfM_(lLpW09+|FOQr1Zt*41!f)L zTq!46O;$r2m`50{y;-~w^{QWMPJk`PgqNEUC(~DRs=jTt&pO3H2R6+o?#em?P72@T z`UA6mPIe&o$Uk+?Kc_Q?!(ERAa|cX)ZwE-lp=1JbNVmiUMcz8QhGnv$55nwI&jLUs z-b?uxpu@iuIz$0JQop)fL(!R)AL|1%-69@>0!9nu#niYzm2I9yTTA*!^HFoYdor&C z^5lpW|FX;sZt+!-S1|9=VWZFacc{S=`~h*m-&7VXmKw^8WDv^`Jp%8{^-+NwV}=?uLF_n`IS?wOcPu1(s=JH@$?RW@Fy!McY@} zZKGHyjC{7@XW;j~Fd2Tpah!<9lN-cMVE7eHXZpuQ@~eDc;+fH1z@GD*n*M_OlLR5H zLPf*l0e8l005%H9Md2e7uv5n=s~DH3k^UHbc1yWANAM>6)%#+@T=14k%Bi>e>@n&I zknj(WPsWk#CBU3lz$0{T(;fg^cX1stVcHW9x{?Iz0F}0SfvAoQ5kD{0S{~lrd@|`> zdVAjkQainBD_wQ{Dkf+CkUE2^`AZTxUOb2@yX(3M?!5@bx_3W$>HD{HjK^)!fzU|# zG8KvqIHZ9i*jeaJ`%4{o-oLwAv8e@>Q8-jnY4VsPF*x1$A6dc12R}by)M3oHyN@v- z>G`RaE>#9Go+qxNvV@nl!ceQ3DV#i4dk^z|3K{JZs$9vl1O2+<-O<-E5FA-jet<@6g@=9k{sx zki$iCX^#R6(O6R0>X}1r%~TtTG3j5sAL6A1s5SO4IjirY#vXb|8!_+!?#3^<@K+c= z5q~#B^L$p_2rZGb$_7B!a-{5K;!rgijJj*CM}M=kt)?KMeDuN~oE_zFI_Rqlo5?9U zKwxh(4!0Bj5lcg(ECc3X@CCh78BGkrKRXDP!0^FdMfpVZ7NDZShL)E5D>^Bg@9RJhh}PAt@@*U1FiJa{L*Fo{xr(Y9{nS^pad&cGcdG zqMGR@A;~#+QB(}jVu_jbf(OX<+KPOt6zZOFQ`e_?6jluTadj>Mu?!JM)%M#js`4g5 zux`sTMGF6@>IVW+_$-t>ZlwjpuOaLEZq{$OIO&U&8tvA#DbAu$V5TZME}hHV%TR)! zP#t_SIK0}>fbg3K9tm@A)i`os#gP{CqsVVxoURm#vK+NP5U@mr7vS`?z6h$?G-Zra zV6S8c1<~Ce<*ue+1X1qZDIW+y6iP^Xk-N`o&mj2o3L-IWS5>E4=}@{0*K~0;w<{PbG z>cURn;a(tCer&?F=@zHs2_xB~)`(xO+}@b7LiM66NRlB(HU6!nkV_{`WmYY4`>jYM z`41GBghN#;iC!5tJU``R+-AVJ{eLVBmHKhh%bRZVr@T{!-zHFu6>haEj*^rL&ERjN!qGM7=i1`JtM!lUy| zTt*XtiX;lZHQeJp)(_L~lA>HCvMW=!f_oJc*T$KGiq$o-jRXek+-rVCc^wG`KVa*= z;m0<)n$~R!#%``bg5eJ(g#n^J5~OUhz*&A($-4%9JXH#kz2XM9e6u-dKOt;NCNy6P zKQNa6(!5eEEwj;-nXNn;%kXTXP?!(Y=dN7FGi=H)Yx@sM#!}u6r7+6pJ5MRR&EHk~ zJdT9{A7;da66>TbX3eZw3fy2Av)tPCYDC5uhPR3Wt8prVva0TuXzfU{N8 z^WLDWY_-!=gZmjYY_sucj^2U7ID@wjmgcxDDgC7V4X zf^=H!m7j5v3vF3lh@7Oj;G|5&$wN7B{j84id}XaLL-Vp-zk5}Z&!`UF*9#u# z1Gb$LTjNAZsrFd*5*6l${-R8ZUp*-Q_*95JgVyGqRe38fB45iT1W6^e2k5<_ge2k;53x;Pb=&7Br<<`WfO8Fhj3zA=#{x{!^D2qE}8oa z)D%MG0MrAWm+i7_PDLloLEqIgL;bqmtEZvr*{hp3E8vRk5A7K4Fz#NDZj0BI8wzx0 zIoZWp+ax2YP^}vYWaB{6hk|E4?eqAsb)DIXaB5SzG@TIP?O{1=^AjpDI1isfKH$NNwk&|9$(P ziqWdgxVs>(RQyS?ExBbm*jaR6`Ebs@|?rNh5`g8i2m z?zkrBc!5};=TM8k&7`M8Z=1p>Z~-5?-woy()T{W9&I(_Dbs~7yRXNrk>N71uM#OP7 z;tU*cGto7$fNXpbw;D{2)Xz}ki-0RF{+5Ng7u2MEKJc8>eWV^K7uF~MqT+nV0x~?L zK~D42VGaJ?HDMyoh|2Xz4Nsk&avA}RoKVO4Q{Nr;R*SP5ZI`30yO<&i+NCQo;Frzu zQ6lLPa17b@U<%1vQ9_;jpJwmcKI{U<^s)*scZGwZc8!gqLc#B!u=ka@C(m#c`Ff?h z-6J);1XWmV@oKMdQ&TEs9R6%1{k!b^RssbJ{8@DNenAdlNYGO>=Dv-9O8&@y;^o&L z?Vl$1a3cs&YV{tQT*z$yG`KWO1WZss4+%sdnxe7-CjVb3B%c#CSNR^>6lM7 zviqLUj)oO9F<${7HjfLuj1et*Au}{GI$zL#&PJeo!lW7!uM)S5bImwg$ScFJYcjQ% zul)UYbw0rd_LQOmnPych?xP+LR_2dx>>)XO@~!7PR26_!a|VRV_qC$#e%d-|BuAXo zvvVUXM8*tXyhTwt5*m~puWnze620ok;rp<#@g}fWypY9!9)xL&{6p!_9HOEl0QSQ7tXPnpU+M6Es5dKq+~diq@ts+qN4ZWK{={Zp#Ukx6d@pfTk_Q&HN$E|u(~b5n=MOeOd$kJ3$<`WdIo?pU9;aA0eeszThfzUleirZ zzE;r?1c>Kf>-(e`UxP@4AMJvIbh$eaw7)KYgu}=5>+cF4{(wmZ2&J1GXLIBB7xl^N zdz$==oBlu(CoV%JAb4VDD;h?e%ol_%y1$jcg^vB7F6$_LhxK@nfT#I=~I97FSrW#&7rYHGkf^`{>f z5p>$bW0tMWpCvbDUPPRd(JnNLGWY-wcOwQxQ8mOWdw?$UIl2BUUqTm#LW++vk(BVA zxO)PWViQ!9;z9D1V8OG$`|kptn~b*a#|f5Vk$8IJ&sqxp5ct_c+^*s6ePt-bz{Sn6 z9_FDqxaFZgE0#N83BErSFN*!bBSnT5X`@#)Ai!r@Z) zH)6?y59JfqDvHhawt_MPbM3vVnAN>E4-s@wwE@z}$f*bUs01DZ;@J7y@@R~;5UX02 z7V16p>5sRgI^i6|nU*h~6zpKM(h+yXoS9HL#qW1et-Fkrqf!lttRl#h!BQ@uW!C$A6Xvg16qj5@N8VK_KtC1ODj^R_rq4 zJuf}YSYXM%L2`>%1ONq3#KD{PARuw=NRh46&Y?JXZ0V;nD1S(;OWPUFl5#0VhBHvw z9Uq5YxT_xPn$YJNz2OnRTTCs{JciWrCcNKyQsr>$ zA3NdYjF6Zcr@;c>#nwH^rdlB+3pB&S%b(n!;3^SaG=Q1uz`cUAaM*i{*qeA=>HZ0n zU{q}5IycL4<*w`RLRUkP5`4ykdo5B+u*!Tb{7j4OL%otvo#U4|j_<~!d{$rhTH?V8WepLwM(!oY|>;c35B z*g))+LTa6NuZMT&`B-$`Pq5!lnp2vv_u&*HD7AE|e4vl1FSN-knaW4 z%B-=B3&nKQ6HuQ{N3(4(?eGbX;DOoymyKYy3zzKg4J%M)$2eyCzXmTx;fO9|Zt@R% zW)hu#3~pq-J5Od5-?yFJ|7=$-Azdd6;?!Bq%Vty47qLZAM03fd{&6>5DQX!MTs&qS zhV7rB)?{zP4B;5x=xJ9$np3p+7PcaHbw;~uo?maaf&vC79GnZbQ|@m50pUq~1|0~O zwicIvbW#W-gG=6Y_vk)=gcO9V6TD7RN-KNr+C=d&3c%9p1(?9hIKMmyEwm>zT#=zv9B{ zsSm+NLU_D6%)805St(Y)=(f*o z(vxQhT}ITB09*gA_ME9#aM&;W@ObLO&o;W%KtK*hJs&`RxP|q!hPACFr5MxOvCJDL ztI!qA>1yx=sR!vQ(2ASFs4GpHCR(OoTZTcvD>4mE~R$tERK)*Yc|+mgf~7b+E_u5!tQRtu;J;Wn{>kr z6S2Ga%KBD(0CgdRXt}tcLi<0Ag1nm9`=#fGN$AA&2mI~frzvKpw+ixc+Jx3yMwQ3x z2N|pN%Vb0yfn?mkR#|PBZ8qmUm8Sx1e>o^t6saBj+2ybtzS`K=(n;~*9*#wUl9Eny zTwE+JPYMBhlLAFl@if8jQgjb8`T9ZMb93EfaDwdc+oau@Lgg2P33eUAKQts~Fz^c# zDTizdH_DnZv5zzsF*BW9-mb$`F11nQO2_=_Y&GkAQoHY>NyP(=^&2`0Oj$!Ov+?}D zT7YwkPp^cItR^W%atSzuW%YBK_&tQCUSa8@Ux_cT9~qb^-{hRAYc$2MZx##+!kR85 z@L5+OpNmDqu+U{B*nu9;{w~hW^!5fw~2P+Yq zcWT=GFx=U-?~!UUs<2TG34tE7LiJzoE_}ga7e}u@IiOF0g_9>OYd4|Us6e5i7kZiB zufN_!?GB>!56sHRUGy%!vq zaQl!H38~k3NqcJ(QEZ+LFQwj-2Vy|jUs3L# zhEcNcv4Jj06x4aH=c;i=7`F9=nWitrMcmwm?&1~#S|=qoy5muS6}D1VS{mg_F!}h_ zHM5nsf@J&R`Qa$a4}n{ab|ZPJFy56CuHwEl`QwHn@lT72GVXxNMex?}vW!~H)&bdw zf210JngQX$yJ)wuGOPw~tST!P?7M*S?Sg?*?!wPvO?mTdu_iP8 zkq0HQBJrn!2a$d`WVthN0c$Y<&OVz3-bgMC!Z%T7Htb(_O)AD-REE?PR~8p3yes9y zp39$3a90|u9;t)_W=idC=5+&ItFlIu0_$j%@}ARkD`rc8;#figrcc zY6lIF%61EGZ=%;_*jzo!)BJ+WM-xQ*17B05lx3W1YARQ)BZwH86UBBgavfsx)qirs zlvn5cPS9iC3b}o?z0b^DDj`S7OECTvGgX1v-|^@Z`tmfVH>jOyDR6|*RnJ$!b?m)E z(&^I={N`yi^Y%-EPe+9+XpQYPjg0gGG|?24 z&8&x`Lk>#!E@>}>&uU$F@4 z=oR|I?cZn{4)Gpa;Urs)BZhGkzX~%6!I|Xk!i_Dncwyj+i=Ad8Xa-l@o<0|Z)D@5w zT$6@RE6#Uyt8RT)u;V(P$sNd;XsrCr?LK$qb-?td*@mwU@%T@0j44(?2c~#~5z^QA zQ3%QMuQbLJ+)PEA3?n|A;mrNlw6uX_j?FlMb>jvLds`^eaYPhK_PhsxK4}HI^q&Z6 z0t_7pC4UU-CrpJb#{NV@Mqc*o&rtf9J*Ybe=`vuok6cOY;1@F zZ)nPI^eNR(Rg)0^b27}>xUQ;;vJJG?cSdKPo21oPmm0xcb$BW2q_G8bT~=eTGEPWLjUN4tPh-gdwvpm@@PR%sylLZFC6k9 zNeyM^WW+mKWtI4Xq(y=S+8B>NYe~$y#;UvFL;MIotgwL*LU{%gxZ|KO?pI|*b z4H>kZEA$QyBOV5vPp(6#zGz;NK=%X9UHQb|HocT@sGFW}jMl&TRTd-Fwg5AU4v7`x zOq{Y6tRB5(Z{{?RZ|21iG^p$-a)s!8eqF6mSW{Y%`_J}KX5+?}h@Xmp0eLdxM0lrkqz$6N7bSga0NTUpK@vY4~{ zIvvUa>2$TWD0DQ!W1^5id68YHE=yM}z{{=Ms2&sxtvFl&A1?6c24pMCA?1Z%wucYsTeubB$L zy~=^3oScg>UYCHuk*rM;`;YpvvByA91Z5E}|KO(`Y(+X>}1nb|cy|Lw6iV zNF6;plYMZRt6`Afo;!XCCD0NX$BZul$bAV?R%Cne0*@If(DO{h0=Q5w_5!QeQs%E4 zKImxa5t`+>dA$0yuhqBj69DMu9&!D(9x!_y)3q~unS__Lh!Spb)~rawODIJa)Y!uP zrB(gmL>*-gYcrl9wfAhgcO-DBSxAaE+~Nb?^3g6JQ|CQvyB6;nJ| zN%%Hxa7`6MR<@Ba?v74_Nq3!%wZY!_4Z{%SrIJd%B5D2WWLp}pO1pAZMuz+u`$%8g z)>F9bY`-x6Pu%T-xW(_`1&UU7{Rh3%Is)NKF;RJ_Sv6sd&F{E*IHOrRba?KG|y;e&!)75jV zmnS=)MMJvaosOIOTqOVDgPvwyF=fS+Wl8I$z1Cls9)`ogp~72REt9(T5AioF;sA?H zzobx>J=o?JJxhsbt-%*0tF=)?l;9T1N$DAv$e$Q8YHbj^600U{@1}BwWP#VQn^$9W zp}xK}5r+&FpxUN4eG|xp34Fy%OKLHIBHEW|&rs+@BEeMP^#c`;$gmYEL{`7VL8h5R;BrZFa^OAzYKD z9~J-YC*NN!kJnwSM?PNJAAXb{S-w@6lo-ad%c(6aFG0I zvX;JS4<}@kH*Z8aT0e(le*1u1 zW1yY!R-#6MH!f$3$QPbNS-R~`Co^+Rz5-3I!x;be^QIBm5@TQ!DlCBa(xMutuE1ti` zc>ny7R2(ovNxSJ^F2hN%{t!hS+H$Zqu-QOqPP@7Us&Cfv_s{zq%n& z6FK3LSFD_O;>n?S&5mkC_UC)IN{B$Xqr*Y^r%1DZu@|qmAXv2Uah?kxSW)}R2Z~le zl4sN@BXC*|)fJ^}b|98kulr6jnRz9zi1KKv9CEm~%KtWyeZlg%eO6vA6)gfm&8aUc z?D}(sOnsX8Y0Ma7^Lnw%#7ws&q_>98woC3#BI*_FQ*lXzz_&YtnG%$o_w+xHC3br< z;paH-uO41*wnCZ3^y+F*cev<5XM99yafe2-{t9~fOHQcYU;ua~+DokX+d^f5E`)5Y zT0wi?MyQ3!i3&=~LXN3E>aZhYjyjd%N; zr_x#=Lv9WjG`>kA4svY7lXMX?ybxlC-}F;CNuU(MIgZw(gLVthx4pMFJ@>yGocEgb)s#M{3LOOqH4?G3UNd9i&pE&s^W5ErO= z0oYhm|k>-(3L12-5(uTlw0?OaMmX{p(ezalw4Waa7si-BJpg zMknjPy+ai2TSie!IXH4OX4lilnfV&%lcMS_7S*zyrdZv%;W#8-c}aPHT!ppgKcVJ@0sA=bls`K&CIK`7C?#>r znqc)YAy@|pVow^;KlvH!6Vhkz9ro7G*m)KV9XokVHj2_%Vn8dXC`CamfHWxpcInvy zAkqvG2uP8Vx&RN6T=K(BM$oXx5Rn<|SNC(uyB)w6`7;bvr=Jca8KC)RZA6OPem!qA zvHt;${^q~u;l86TL3@eSc&C|V@$U!pT<|fXmo<_{om?_7ud+>pLA4rLXIj9=wm4qV zSrhA|3H`r1ktNcEi>9G8vfZ-k+2jQ!P1V-H_GNp8B*y2}kxlB)Vnmk}H}M^7&5?s; z@9XY^aL${PCgl2g37lf#^lW{<3O~Q8IAUL}O{d3rN#b%thH4d!wn9FDKW1Y$jpeEg zvlyXtzwR4mHa1yO2*qI*&dffjl+^L;ku3=5g(BWCsZ5QLb@47jEWj}YcKJfM)QeHRflNavrJERki*O0}a)QxNEtxEj&@St{7f1+fDO`!c9kacQv|NeB+xH$Hq% zY=CKAR+Gj&_iD*2uaxDHwWJuZPSTYY(F<$Z3eYP1qQ&9%E0$@$z3jQxVMMv!`1}7A zBlXdi{Z8gR*Ch*g_EQX=-zB1Vk>d|pB?8-pj-NE(oquv;aj8;M72A)a<^j495@bx5Kz#%Pyn4{^iX%QOWM!D;oj| z*Y%ijE$_9fyW#>CWBWPNxoc{0)bv`q+1KnSInhvjxq$QmXa!2-meh0ktyM&f)*36j zYnzDWbBEO!55AF(+u5uNHO>GI_nA-pa75xedU5qZSH>9xD4whGFOZh#3DIQ}h-!Sb zer~VjbLkR!|6u*&`qs-1*F|&uecOV)1@orgf08mTgEiM92l$q7l`Ub>-?gK|}g%@TN zrw1ta#eTx8SkG6FzR)V;FQ|)A&n?5|ON=-YPN8Q{1X%;l@%Gk5j;v)7(|#BDxYTf2 zH7Q*5ZmL|1cfqo6SSHz+Qn07nU#+MgwB_lrb_Lsg+2=27?4Y{*e{~r51>wY2`Ei5o z{h@;$GDqZoY_02O{4kusHJ-?&BOPKnCIZCJ7;A990_NxEke2RcVZvO87Sxg3+(^N2 z&HW7PKA79bVMQg^U?rnXUkpM+><6fo|RhSEYnurNHf3V)$+ zG=@sXzAAI&e3f|mWrxukH|-C;#d=dEznW3MJ5G4q{ClRS(d+nV??+Sf^o2o*D}1=n z6Dvvzp*J`qacw`DO7(P-b};h zIU>!Cv9Bpv6GAT2EkJ=qXchE$BlTF_K#>iC)NNx+z&)LFnQVQ-n`j$CZX4`V++Qvj ziunz*bKmMLQTVi;elduDfaxp3B5TfHx4F5+@8C6?7+YX?3RX}osi^t8T)ZAN@#|=7 zH{N!|brr(7|7V#;gH@aQIPJPfeCzA;NKn9(`QlF{^YBpe8Gv0E5HSWNty8z95ZVTf z@pH`)Uh=edrk~e#2b*2Tkp}eIN+ZSd_?4y7_!QZJM06{&tFwf=PUdd`6v*(=N66C6 z2FvJyCiDAzUrT$8e! zzYFKSzH`EXC848F#s1>;bSN7e3*p~7KW5pm&bwAqV*u;e5Mrp1f^o$eR+IqMf|3uv zPkLsaag);YctBh*5&yop zF~d?|^Z=D{_=SgaA*c1z2<+Yqa^kV0sbh4E-(vn@Hf!VV7$4WJ9_yYM2hn>kc-s-* zVX0|gzPP~IH`1C&;1R@s{81xprugzn(3VX2vwpezQti-eBziW4=S5^(_9>c$&-W5i zxE8k#d8v`<)%@ky^x^l*&ziS>+4*Fr(?!jnAt`mls#&{>%9RgBx2uwWlKn1>wmdKN zX?{1J)u;D3O(3+b-f*-=6j{)puDl?-t}QgcBh2Q{^U=bR8ZCT6UQk1 zP{eSZnN?n0c<1pb@fB%CvuI_!x@z@7S$%jV2jDJ|#eb(iwGYgq^HxoUvr}Gtkad4aZA`)tMssyGLn70{3@eUVZ z4xxQI?!5Ter3?Rn81+Gdf4RNpg8o@vPKbH+@>A`{RcdSam!Mb;d1W3mQqSid(i-W{ z6IGsi_bMbSI*fUO*j}GF2;)+!jE)@bH2==__q$tFhx;WQ8!3d^kiw&VSMqhcg(O>l zVBe(wH2OGUvQ|)uT=bPxPzHtCnfXV&%iC2Gtku68D(U&fv)+9FuP@)x+QbBdnq6FI zS^8}ge)1yW2&YVQPi0T;Gh|!`5Qh@|7}Zsvp@UVxdSSa! zbRygma#$onN@22T2}(U*xFPx$sRhrHMuRW&=sn7(z;CzBTX2K`(ZOZ8m`Use`2s4` zO>i`2kF?MUrXgwEDvTa|`%-#AG2=5zmk!gg6^>&uh}bKtz(w8z{EG(#_kk7-;1m7> zI%a(lFPabWRdPr>@JZknsEL#Ar{`K3)I0O6?#O`(@7j0X+9;lsfJN&yni*rNK+8*t znF>df4$OfXZjTASBMI|-^=9$41nz7{3|G6iC)Nq?ob_c$#g&}nCn>N_Fx8f0q1>rI ztDykzGX|r)deO%%t|yO^`tFpJnIhM$f_>kN<)*eR68bItZh!mO3JL%Rc%xfpq~_RM zzQBS`Dh76hnviZ(>X>BzZeR9EWU4q1ALp-iP|!X42hx97cqXQ|IxcvLP~$4O;aMFy zab4Q^Fn-qZCz97qBO610J`<044sMvuv(*%?C|A=CeTVT@CG`7$ZxQVy%(KSU4|6I{ z6ynPHbDGw>hM)d^PpXjOShf!KFIaXgXBSFCn(TibBaPa6_9~>Nq=?h{J&*Mf=CH@5 z9G}ziez46rI<2n9w9O08OCI9$>l{Kb*t&w&oy7KqYP+Vet=HTR=3DC{0IG_lWPY+z zLBzZ`0?_|%JzKv&NP8k=K$|K07X&%c_MUinku@)=ngWLjFUcLrsKUPDy3+`=x=C>- z&;W8LN8s%48$<3rHRTc(oS;TUSWU(&+)Rd|M?OR2x_bCqzepPmT{K3YiaLBteLxaD z`M-&gZ!eXmQxbI8+V_1F*Yqwz(yU=2nXTd#``0OpvD_7Gh<1{zdMJP&)a@(=co)aZTCntJ!41Sq<;-#$#S+{AqMh45bKytfUoqy(^N(UhiNnG z{OidS^q}B(%$kx))}K~#rgsHyJ0=sL0CsF(03Hms1FO0!4hD=W7~a3~+llmxu)JEx zj6o%YoJSW4=Tne>P2$QavlA~yIb?aM->#8c!AixKP2uN z5#l_|H1Nyb$8WKbh^-~vrhF~vz(Xl?5&B=o!%Va>0}ACS>^bqu-B8{Mli?Nl)^m2H zev+9tM7F*EvV=bi)goo&@}d%4@lT*Tp{!Lgd@`)Us@b}qmi?tYKTD~-EH|ylGxV8s zsdbznGl|qshzl&_DS{z#n+iRdh|JoyXg@3BS!A%Qf}-k(PK7&~)pIf~;sF^1kCPik zL%4sM4FvYO3LHhp^9W-YVZjN6qq_u2MK1FWv+%`MfgrWJA<33WfqCJ8l-9qdYxS3v zr0INWFcJ&M2<{5~EG4T67A%n-WD_Kj-@K_1C@FXKfath7Sb*=hi2*Po7iR;?_0*XxUK6OX!#iRx)ZGR=RfLjX)UfbMR98XQEMA%p>oilM%11y zSUoE|ea${TVazuSYXz+)}g$Td=-2H6C~;-Kr=cYui)3FahtaB;ep`_3Sf z?#%%C6KDVg1@2FZ2_;@Q6XR(gBRYieUHj~Wj6;$=I$Dyt9x0nmM%yH9{`&*e%?C{2 zxi{tN1{jGQhJr!-W4(smU-L~FlWc?p4KxGY9>V(T;(=V~# z#&)cj?W6Oxzko-l@i$)BjfD#nU+_q5_QNM@LGQmw=t)c4U8rcVfzt;G$FPA^uj|11 zcyLYv$)Qod&C#(#qt!k4!4D0qS4R;FTY28QrwR$#863cRH~)QDg9OqXg5XWi2P~mp zV8)C}7MxUJ_S?jVr+VY6WR~qcSo2-#dOq@kT=qwe23ad+e{PBM4=W!6M0?Zu7$L7i zE{A7U3L>J|oEXiHHT#pb?H+`MjspE=4@~5i{yZ@ z?_X~@Chd_4ZJ+s%xNA4f%%068bLc#1{qiTeuO_*h&6iC_(%1g>EtB@?EEyG8Tmyxx zX+|Vl=|RebWRuByM{I}?)hfzA16@3IIKcf&K!GCsqX;HiD1`EgW_^44*7<L^xy#2A}VXwaCHzMdz=G|?+VSaX%MX+}?gK1zvB;h*8jCM%Q zaijUD$t)p(WG5mA^hni&=H-^n>jQlJ0<_PI&XoJ1dq4w-N_M!B14?Tbmo5bBMcqE& z-+$f9^lfC4ZC^SmZ2){Vx@cQ%jMD^&TUhOy8g`M(8D&{Ap&#B=ID6z|QwJve(p$-{ z0t_hzh#X6s*k1$|9BX4$G#ub#9fT$$k-zVSsi`Ce5+|wDI-d!;sm-SLwOrnW!lNKq z@tPL{9NeBU1WI8_Jw?peT`(>nGKuu?SaU{mmg-=>X98K+z`0YE06kYpe8o}LZZ$N z5MV<8nV*PT&Xye-ELUQsOi=hepvIHY3tQdKC>>K*4m~S7)i;tx{Q>{>Hahc*XBag5 z8agHeXO)pMwR!ynlDwYgI_u0+b9Q;|!L{J9e!Sz+ttRH_Qw2_6;qxqittcZEb_1;v zpbTgJws@Kwc{Txn$dXZf`|{?e_1U`BS;Ga)x>V|U5q$?%5M~3|2^ru8xpe#lQ$8%! z;n2vES(@Tn1$4gxdNeO5u730P6HQC;n)PW+dAuvPm^mibe}nu7dJ;JF%d_~=skswo z%cozA^!VN-&sKKZbXEP(=KG?y2tDq;6${;NPdSGajXM}vm`@|Gr{Rw%!;x|`j*b|3 z7kH6=Ya@+Q4J$ydcHaDRt^M6KRUS|rVFQB`;I~TB86(C%(MiGmE_BJF}2}RPlMG$jFl_B?!I_OMtGZMbUAS(gAWIU^1RedSeyy2y%nF`+_L3rspY= z(H|PIy2Zz`u`hdHE5Db+dl8q7&!mrdGVaMVslLhzCQa|q+o__ROay1K9LwSNI-0m1 z+=;uohxgs9gC6I=_suX)joB>nKk||vXP4z^u=Ko0(q@s>+q`8VUDgh*+I|+6go4X2 zSFAa)v~tlSa1ehl6*q~_DpC&BNYlSV*m5vM4zsW3>WO00-7a^n4CgB%1gxb01Pu(v z(Nk)6*lCJMsDI6Zj&1uTLRTW7a3T;0xQ^kwh2ky>MmMi!isJVjdTQGlZvC@-cX^Ra zKZD`c$HKvI0)I{Uu0af8Q$3w}TiW>wfJ z6#>L!GF3r%TW-Ca-Cgr9eE!U0((?puTx%oRPv=cuC)fDRR9AbI-Z|oij9gijEQ>d% zpZ^*C=>MJQDV94XF8j2PU}`Jn;+g}$s%lAK|3^O&tE?&Y zTEjSI;B*jxO1V53Zf>_^f+|=IwnBksRP9g0l07Vj4=8Ii7P~&{BUQIvyeR|11XYC9grUr`79M52E%SXN(oqNr8*iH261<#0OpIBf?EfmB$3a z`^(^aV*fJ!2m2_$JOVHW?ZGBbk~gnT4`y1Bk;Ts*bavVK+G+nJF0FUcrf0D(CcA?_ zfGv!mPIFwiIr$(}sXl=IfLd2QT`w4vk5pk zAV5=e%V2#FWQ`y6QSVC&Jg`7WW{iE| zMG+Y3>;x>!=nLWDi@!L{#fUnWh*t|Qr%eg#_v&*~ak3xUHni+Ve?ICg@=U%Z`(wDH zT1G8b@_B;KI2CCPHQc<`dG%56!o}Xuj~*&JAG&0YgCJ4jE@PY5D>gvWgT-B;4iMXkhv!q3I}D#F)QMSg=<}0b z0Z{bqp?td&A9oQk>%%(S=7?)BYNtr&7wY@JyBH!dCh6mb_Su^5OSgXs=!jiwJ@keT zqdm`-KqnML!$zg6JJUAnpKGi|5H!!DKC@h<&dOXO^PU^SC;(a zpd(U@0sNj56Edpj@V1z9*~>hS0HX{7v~?mU1c51c{s-l%5L5{HrQ31Ifg;IM9DN#v z@=lKWB5Usx?}uPPp$jUykD#L?P(7W)K3aW|A{LP5^=UF%&j6`s2AeTaF3Yb6Tf1>t zfqGENUXxOyQA&P-a`xX8H-K`4T)y*)clT@x3?8M@?X+~+G~ zd`s5Amo-HG)bw9z?kwjT9$$0ps6XD#i`?>ZEVAxhDx9^C_oZh~5q_Qg0xcOGN(g4; zQSa!gMD$T~bk-61A9Y224J}BQq1A5ipC-vEps`*4wjM7cV;pS0a(yT_RtSM5qPgV} zaEnKyMCx)dZe1vxQxZgIvoWmp5a*tfa#ASV)FjnXv6*NsBVfl02JrTJE?_n znx=hT>{^ANtE}Jq&oO~ix4XV*hDQV}7$58T%Sg1gwaMXXeniuoni*jSNO?;s1_*fV z==W_-xE_5SoY$}7>myJftKD2}YsGrw!mI#Hk1UwqL6g?Qvg9A+SBBfL9L$>RkQ2(o zJk~XzKt{d~Mr&In4QGO4!UoP{G50r{$?iNjKm{69`bU{NIa4H{h8@zoz3o_uzGRTm z5}|c-i(k;C-CQv!3cOmKSs!- zdl$=jc9huf%dKBwA*t{1Tw;WY%ebuIxEXRYQ-=05-||FZd%>RA^KERoQ;IYfkQAdm z4yeSoDT($vYn`g9g*zC@1WYexldS2IWc*}U3nL!M=>_ypC3%WY{zAx3)fKSg)tJ(y*`xbxVww8foU_?M^8{puHNLdQx5Wy4S()|$4 zw%vsiRzvd|EXMotOu6M1<4niEc1?#{BGh3>1|D0PGM}eR>OZD%S&A*hz41uF&VvKu zhgQg}GyGOQe3>#~ok9=I7pc;sp;v{_vBES+Yg=agfAN0?_M!~zw^z3oV>+%VRp$Q| zZNqu(3#0rD>QQvX(^tuQl3;}b9cQMYc=Le+ek0(Y`T1(?aaym#2kve~eL(ijjb`e! zn0)=OiiEC_P~GFr_kkD97`=LHimf)-^*rBgeUu?&KVEqzwDuCkbtW4p1m*52Tl|fU zmN@zUxf>&a4;vVZ+As*N=667F{(baGDTdCWhhI%qO{mv;sl_{RCZxt=13voKsjQAqTZ?TlR&0Nb zc2v*h#SW#8=qH}9b55%w4!!2uvLKu&2`6W=IRf!eU*j2<1?o09FKU_HsG3(1Iqm;L zs)CFwvu{+y&3=+fOup>lI1XPwrtunYne;A%U7690TC3BG;zgQLIC zyXs?vCg0_U4;s{j(A<|LOwY*&YF@ofh?Uo@%kjYxMEneQP812U*PwO@F};E@d=y)8 zmM0(tvAcf23LDrl2!hi*?u?!!8r{?jWv=`&I$t_X`sXl6k!-ZWKz3&eX%LsUlOY0^ zzeaHx8Z*-_G%y*t8r?^9MJ^rRN0~5Ox)EwyLJZnF`p7w>-_JTrt;zg5HcqGk8 z>5^okf^csWzqS(zSejXNrF7RV`xyF zx8srB&`nKZZ{>zCm}ty?y@$I=^$8BdexS|F_gLt>`aC6 z;+ZxX+oaN`t1;#n0qkr8vO;sKK`Gg9-Ds2LaKZ(mm)y%!9Kw1(cPz z`ZII$Q}9t-b=+z5w}(^HnpMjtPV?y5z@RvxP;mdLfD;*-n}mZnvogG|Xa^oQbTC4$ zDjf(Uc3~9z1xY={04@(0rLWzUj7JpED$SHPkZKqh>I0+>y55_lv*XhF5nz~R!Tm5G zcY*8QJ8ole1S^icJVj5sQGq_8rOxPEr|->N2=g#u;?f3~J@{WCo)J${%lI7Og2x z?DGi7dWJapp5Q;|7K#0tf4Dy*;$oTpwv@Ds6RRmU#I_$`1Xaa`8a1=(J(gP90sE=a zbpaQ_L=i=dg)dLlG?RL@mkYT{+e)zfd-5?f^qJrMQ8-a%ek=mO>@GSAnCJjHBp@&a zH}@Eiv12rJ1-Ub5e_AW}XeM91E<)Yd*1Xz!3X!b3Ba!qxO~Imky>SF%U&&Ik?NjX*khP0;4Z$ z%JLQ&Tt&G|97ds!1N#dvyMK73v4d+;(0a@wFAYY}r)YFJyB~_!cqZ=D*^ZUbPUf+p z+nc}M;!InzBHp2MlG4iYrY2x!?7!Arg_Bcmn8E_{Ok3R%H$J7#dpeP`?`uhFfh z@s)lbL;Yh+$tfgjp>>3ex)sbwx8fePu=Tg2zQcK+!3^t0NJfRWBW|wA5oK>AxS$>r4li`P8Uk?OKtVDJfpNj1z^> zu6j;K4w&V>yXG-FNR7vMm_kmc82l{JDL^WNw^ZbnvT3#w!|5ld{B+$HMxFyzZ+^$# zDFq0J0$)6+ZzYg|VW}gr75)H8a7})&3#sdWHx=vIIC!(3Co2**xlXCPKsg_ z>{fDVW>cw-4<-6Oxj#ce)aB~^EAq>%SC6%NPyaPVTjKa#z^QT8i5C^wyIJASb>4?; zrsKyeP!aD3u9d{}g18KsKsWe|gO5XrZV25}p~7H`9I*{9H~HG5W|2 zeD~Trn0?!gRR=iAY~yO$SscTzb^E3Kk<5@Tnq0o(5046{Z56INeICKwOF)VuZYG?i z$-K#pb1k4x1cG+4`yw^ohPU(}(-6Hm75YxC+} zJb4~7YF4VW>w8BP>zYj2gmYCs{rK^Oh6HU-p2COrrw#2ty;Prbukh!e4aPOrviMhW z>Od(!2$V??fAF#s*Ec1;9dVv$?1PQozi6+sg&8U1{EvWse2(%m!4{jyG=={Wra8((=;>&(1?`O6>Wz&*_W8Z;wA1(%lNvAi;1=g- zXod$S@{bo6jjRj(j`I9?2$}MP2S8n#({NoOI(Azkdq2FqdMENTncDpgglGPWJ9|vZ z0{DKXoygE=Hkyxf7jV(pfB&cf{8z2-XEn>u3EK6V3j$Cl*;^$2u8^q3ezf)r9T`ef@^`0X2R}N9zES_%rH@9Ic{|82R3=tc%viMx9EA9O(Pa;*Y9y z(h!F{C*7Fk0o$%otlnc$tnaydZ89;5ap`c-`e?B-x-T*=h0dJ1*RsOKB3KP&Fd3NT+q`TWgmpPXGm~%8LRSL< z7f%mYBXuMKonzXRe>|-(GT<+wlO0v;TrdCE8uTO4RfXt~>C#gXY8L=ApXuMUV#JI- z6PL0#JT_bZGq&B-ljjtT!3Ue72_`Pqza?zxM3Y@6RnM39XG3$~3n#d+z>s!Ny`Z#O zee3hEb+@e8Zwd)IW0iqREWhGbdn3fN>LgFQvVg*B=c8wA?N?;Tt>Z_Iet3tboc^bH;h@*ki%Kc+uY;-^ijr>}kY>(iM~QI+^ko zhx0Zv;i!K2;(rUwxhwJMo%)rnkf%h^oMGh;Yb%1%)%75xg~WmS{UNaCN6f5BZ$OOm zytDJWpF+Izulfr+tTd)!_vATJ1R7{KCCC92sHaB0+V}9!%HW##Tz)!)@yQK6l%1!# z+Ym2PQ)c30S6bGCpvPtK=*jitxX~)#l4BI4)^A*V%fj&CmEZQF(dOc0KaZ4gY_&@`|f?IDvoks*??Zo49DU}*bIozxr+Z305#*wAGGeXzsz zz-Dx_!pxr9Bm}2z*Eh+aA%*X05f#XNV$Z2hi@k=7U3)ItH?Bj6FWSe?r3c5xD0d8)q5ARK%*#%E+Y?>0*_! zcXFb}5rFAuL9u4jafhY{I&q8>WH8n}+?^u)n3v;&%Xu&Msfr^7;gvB!n z>QswqpJFM%v!&ti@2!rJckFP>PdXzXl1@LfFCO%ZZz%q78?XCdR8TopYdm~0o?r}! z>O@noum4+Dy+!>rT=>)G+fF(ZN-;2%jOhYU8QHT4r#uByIv{rvy&uY+qKqCx{rs07 zGoGk&S$@QidAS;lQp8F`@+KKIBk|f??t(PAzZQW>_(r&|R-$quLzaL=jKlv%piTh< ziPz)YairNo(ef|A4NbeKt?aq(UU@t>UcK*ibrlduapQXJXoqz4w3%PA*b&Ah6Pq`1 z8aHAdmgIfnM`R7H{+GKO=}Z{U49jwkD7M;n+G2^Htm@lYZ8P>?f0Vye+AG!A4{8*> zr&PKz(zZ&z7U4Z1FL|FZOn*Myp79~-F8$l&h_R-s^_lW2?D?wBA7n}?@vj(}f$z=Z z^1;(VaH--_fO6qc+=EIIv6?IFCfk$kW5;EE177C1f+=7*{@$5ce1=YPC&TxYUPn!pCjHJSVj)AsF2IbEBQUtn zV06Xvs@%-t3KiuB*I`C(_T%U8MAjWfDk)t5+sxO=`d^dvFUW8|n?AC>G&OxpTSkFn zfikwR&8VM^S@63W94U+b47t*uaqb!T}oi zen2Uhwp=>mi0%qQPl+WjoLlbql99dkkNj=O!>8!$HLCiK>iU>gQByONsH|I?rZtEc z4a42*o_U$#cf%Gw*~BB@Gx(o>G@(U8mt>q);`-0^t#Ss>L}_uKFnCI1 zYel3~hN3(>AR$`ooCb)s#Tgdz_z*VmU8b2HH(jZuTJ==LH=Q3BZY@F^Et+<-&n}lk zq8|1%B_4&2Hr8!&i(VgqyEGsEJ9$&YLG@zJGA|-%b^o{wN{g$u01V${Sx`X`I_hF6 z=6E$fevmj%_4}^ow-^1j2y3Z|W~lc%{!VIeS8KA$m^7yw?T-Y6Y<Ztka_*KZR` z-s5=Rhd$~0UjOq;v-e(4cHh1dBwvVcM!DlHji=$y2z#zOd`1~?=TIbMjWkX~ngqot z_tI@X1cE^aI~CI_J3JYiB3JjSPZ%LIIgv{1ZqpM4;;-x1x%0avLXQv~hr0{vYxn4f z?Y;Mx-nKR4)p8zb2jpvZ;Xx%Jxnk|2zuQsmVf3Yfxr0U)vv4L#>A_U(RMziEDetE; z`tp*$CZWL=dl!$Mvtpk@7P473GOM&Y8TFEKnm7KB7GU}DPwRM}ny(X!6vz9Wx=o@+ z;pu$+UCV3#BfnyTq>H{XYg+rvs`1dsq#!`H6FQcZIu4jMXw1YBEDyr(Qk=hKnX=$+ zxgB2~Eunev(Qm(dI$Jz`z*Z}7?>IgB*y8M&O!r)bT~u(*p!571cX!See+SltePbv#$qH+RqUVo~)~si0T33rUF@K!^Wm$(}#ah)u+Wx0d4&?jpEeNI zZi4pR{Jy7^ESt9=NmnnLyUQ86sis97@X0uIx?r@fA=b_V%}2!U5;4(A-xkBw*Vw0Z zJZDz*N9|y|LI`=P`jj#ns9chfmgJ}Q43CQw}=c}4l*}J1`$sK|3InRfLEDpxd7Af7c z{UWzwK9B;$`!N&vLlV_^ri*Z&58Y>JiWE`EI~Em)T`bVu&DoSe;QhI5Pi@L=S|-mE}) zxNnKwibQ+Y<0U*rXt)LJ-E7!03kl+Q}_S@!%8nRe*pB;#}UE5OnK3AIsIs0U_DJnItix<6i=4VC~$P7ZXW0Bj zNu8?j#)f!$O9eg;GS+CwOgs@Qs`2?&Yucf`JTa*Q$ZxTHlp7d9JN7r$5PBbsIY#k> zW%`O;=q>%Z8TVqD$Li%W=~Ty9`s_}isxl!c&H*_8JUiH5v9uJOI_vj&_kj?+`{pf# znZ!iudCaV_bALQK8MA}oa;4~79E&fsH;$-l;5;@i2Zi4@A!RkjW78=N+j*+ zYU%GEbd>@C@aGxnY%>l0epmmt|Jl&=;ZlO&hQ_pO#{eq^^}5(^sX>Cs z1e-#r2P>KB@JAebbGtlJ94R2}%hsB=4j9(7h=Q@ug+B?wxp)sBiNNOE1hrtxVIy|z z30E(VcV}e#N<=|S8$&ON(W?N2oib&fR&p;Vgy09q&nm6AuxHspqc~-&pc;F9Lr#+$ z%@pG*tp01o`FGq9$nGTJsV=kv*;H(xX>KhIpOsSyvXt4A<{LPO?MWG0Iqd$+WSYZ& zx=c1-kGw;DSg~$e&%F_Q^Yb0IttM;`&F7w6gMK?g7PqD?te7DIt(JD{KPSSEGkSoF zV_hnz=skjswmpfT))sj))}%Ix5s$LX&!!Rp(RvWfGxi{dmi4V4DlEIMn%Q^LhO?JzH%OrE6A%BQ~(P zQ#Y-7XnmL${Al@bQe#BIwB%?NH*i?njH=Jr@i**uMdQf*475B|NhQp|YZtSYc@`1a z=SJ=VY==fXw-x5M1e!hocdRpszfujlBydkTj{S&TBJpnoR=&l^FD6`nm^j58c_`cV zTub+9=@KWTKiXsP9nYPO7MzOxeKv;gYCATMIq~W#sD@97qKHD%UnoJ1Wn+civ|3y} zYSM^2{)S3V+IRpvI|!iISLbrxSnzfGbq7Yr*XE)yACtc%q**fHXj7!H`7?!~TzJa| zlTF}6C!z)t>z|#9tm!$`m|vQA;)(up%OMn61e~y|89xT<(thkDV*O0RKN+frWxSgH zix=={E0Vkb8j0#2V_o)m{RiE_iewEtr`?#`=}Xg z3^kH@@e&{Ml>`hD0Q1dL4` z*L5HPC&!!Tb3;F#65w%3YkIz4$^ecASa2n1g$oCTxFqVLd-ibwB()V9nW+k=g)4hxgkbSK&m^R z_-@wxx4oyfC!5yB`^nb{YhNDg@=rLw+orZl6rN*#!3~h@9$o+jJIRUQR@7etRJ=^^ z(bzKvvPgx>6h*8jVyIW6WinEfw4f)og3dJyfVkvNbY*mFK<7DSI%V}F)EUt55f3}VX9fbUDka3U)Bw>?($JiY0t&}rR;hF#1x zTd=WoI6o6cQU?sA0s<-fExww3TI>7NyHAp*ihQeuS(QX%5kAM2^BnCmpJX*Rtx1mw zqw0%#$6wwrfjtz6EUcpA_exnlCpwaArtp;xk1)=U(C3Acce0IW`Pa&UODZKa7*;&M z+j2D_U^px#C@{_z%Qj`i=Zox&%C$1>%1ev}?|=9A6ylLzRn2T_eOZ5}wrH=k$qR)c zy&EUc7*6-neuTc4%(%6?psX)J29PlTkW2_=S_Bg(%syMebBgjQ6r!;oX!FOK z)U)U-SvXcq7z@?~P#A>bCP-%gVw#cIO$^R<`FOy_@8HI7w>u`re-~Zp6}M z(3Upmiz{GB2vCx!Sp!dPrqvGMn{U#Ry%%6to+|z61WE?dkQh;n5$;;eQ_DDOmu zk`w@NCIrYN3FBX}0K{`!K5g`2p(QbJ$}G4{?e2r^)l=O|&))XjZS*{m=4vg-FRU>C zto29Pj7tgKA*qhX@BD3c!w4>g$DJf1&VVrn6HRevJ1E)zEQtvxW?a9YiTRsy4UKt! zjrW~9ueUR3t?zQp=>)EaURfU@TP@nSyQJgR0*aY&4vRWgh_J3_}>WJ)Lj^5Pk!9`X;{nI3WZu9urQ)a3uOJ zF@0wVBuqgP0`fpZX82YZemikbW5(Sq10QzULhm;E@X`BGi(3$2b8MCQll|Ed@r0Lk zJf0xja{S%@mCi)0gYmX2e58k@ej9_w&^4iP!X9lmA@GFW3qapZ z7KoAvFI&Y=Olk6M)(t#HDaeUBGWM-l0JJO8s?RV7Arvj#tj^Fy9a1Lw_ zjF#iADa&`4x7jZ1{zw7FLu39|E^4hP3*D#cwQljYyzF-)+5V*dA_!#OOvcSExd4pJ zV4&ow>vYINnLioxf;(H7e@mVmqv-2n#VDYey^xuXvmh)0L}Lh9l&b0;bNyXLK}!7s zj*w(P0kA0>n~i%sH@FD?7#ZIFAMmgJ8A9v`-eWlttiyX1^1&`oT)^5s^{}!Aw8Fr` zDAwX3H`-{UjW$}QmWxg&^5+WUCu9w_VgC5+EeJ$T-2KL1fbawcLuU=w-x*5Py=eRb z05oReGcalXU24v{qzG3$P~isdb9b+~#U;|9N(3YT1d9a_b zOgE%Be``4S3y!C2+z>gPzQgYAzr@We|3;PyN4&pFAk;x>j<^`X^@I??x~bV>5wN9f zK2Z;#5)L-nXrqlbI!{Y#W^6unx$N7I8tES?iG8viJVZ|nBgcR8KXdx-Ur(TRXBe+- z;xU=$8Dp@)B>rKuh8j(-CWiwyNc^g&3NNsPX=CMBd%h^q+23Ae|06SeC~1GocQRl0 zlM6u4nDEh+?knEb@Ul~E;tR6`B%Km>K%oWAUq>6vnEwh7ZBf$N6=8vsH}oz}mKeE7 zKH?(9ZerO3w|F0L4&vSa!~T1JjoTOhFd;-NBOymT#1L`8Lmx1Btm!bZBbW|s+KLq{ znAXg{#O&{pq&C`Uqm4Gobf5OG%LM8KKT<}RzwELZh>V=x`#aqHqyL(&Cn5h0Tt69# zX5CwZHF=j!TPFb$lGra<_eDV!KfPF_TUt_f*S;?eyFD);z~XKfP@qJ{WeY%MObIFM z-y%@}=PcCCebCI`H|7VZ8TAqdK-VkUzjhUP?RpTv;mWL3F_;~k#)Jisq(Fixz)U`x zj>tYf=GMPUw2^QE%U@*}USYiaDj@_cV=^8EpJP%z#*aid;JPGs7%Tw?7_4MfKynA@ z4sYiFjW*h7qxVN=6sOo||CLK%OvgfE`^3cj{gYqi?(hE>bo~i13?1BjdzTn^Ww+=K*L=)yI{^pqhx*DzI>$Cjr7>ntY1AOKg~&Lv7Zd zXcE}30ve=`E?V~CF;~ZTvj7uu@BSs+_-$?;|3U%+fi+mk4j~O~w9!TzZS+rpH1k+<0cZ5T#WKJTMclsXm+X=S=^)ti}}-fuZ4dF6S~6g#wAm>0nQizEy+|;jj4~hr82qPZ19SIYixlBdcr@&B8*F&ZJB?*JDDq2*_R7Xrqlb+UWbHS+sQ_Z7S9l0Ac=V{f?YCe&fI8_8Wf%*QLr_ zZ-_S?SMOPJ%y(h@ov~P(tN;@FC%*s6=O0Yb&Rgca1nOHt`Y)pX72k74h5s$gO|bxk zR8|BOJ4HMu-Wiu|DP=hAUI67dRLs9<^f?(%+o`pTW@SQJOhD`NSAw1f4tk$iC4-7(5Xd{vL8pLZPufrzgbb_=P2* zq8hNNqXP6qv>}5&ris=-Lz{B|mz2RH*e<6BHk5Ec!2)=ngaAUSud@S}yvXhG-IPlJ zL*#ADJAaYglfO=U@sDxbe;z{G?cJJF3QdeBhz94AbZBiPhL|57*&E>fWW3sFqm4G& zsE{P!nV?*W@{dpuZa(|^1ODpQIR4|m%<1j_1J}pG%v}#R-`jD#m7sm-GvLo*an9n5 zRpDs~B)A9!bn_fI)|i@?nK|RHtlkz$4=W>=b%Xv9*j5Xk7roQ||>JT0P6mzLaTQJ&oH(58k%IKnGz=e>wKATx%@^ z$s%ZyJHX`|)cKBm_Yya!_b|s1_Gz+g_$KCi|0M_e5@G)qasL+K(HC+@VyeeQVpMq% zgC*LH@o%)zMjL&=B_^&w@2vvtTOlA*1@@Eg z*~)-ey*s7^MHvPm>42BR_>-w1SQQ0OWdqD$i0Zl~8(=lSF=bH_#@tFMEw$3m%pK4O z4Gu~!fpl|XH5tXPVhv%8DKE2j2}HmcPN!_;h`GX;0eiV}V=NcpMPfX1d-nv_CvYSl zqInzRe~Iq;ZxFw`$Jke4_X5_w1pP}GyPM`ION!YTuuSbZ)$g_I+T&;IU!j6gTxt7U zdr#A6{rjQgS?%wVFnHhfXHd8I^|5?ks6+3|eQ$ry^y{D6oUC2*fP4B>eQ54AJZuiy zviG%luI>jt?+67^S*i>LNoqF z%$EK@JiS90-^QQb#^1dS{++2?zF^16O z-Pa25spWN|=;lvxdFyC^DjP29ceB>DRf`Ki;|6+wocyO4E%Ky!5eBNRMF_FrD!R2a z_U-*~u6|H7X@);z{^u6scA*u9uZ~qH$(W?9#fSh8WFer>mH@oL=G*|z8LXL@ddZrQ z12$cDR~WP75C;6|mgDg%&9I5E&zHG7h~L27d?SGgZ}PacEk#*R7RRbT7s{;J4}vPQ z^!p+7G`5Mb)<@>YSNn!~W}5nDeOYL)>O<4^;`pCpKB@$m4Fj0bxp{BTJ!RVuIexCp z>^_~D1Ksxr^|=CE)-XLGmktjz9>s`K2OuhmhF zVeQ#`iaM9tUmDlh=ZE?}F5T1Fd)q!EPuKmr9hN+-C0(an_3|!xK2qq8% z)+Ml?vlfHF8H?*O;19+8CEvdy0nE47=OrqdJ&M7{NuiZ~&j5fc=Vqz)QOa)aNZ&Qj zh(Zs>sBXgK0inJd64I`I5|T6##>p69JDQDnR!DA|{!;^}Fa<0~LJP-jyDO8P7$sWT z4uYEPkbM$X9u#BJlPZ##%^N=$gNp|a-7D;Q0c&^oBXA4eN8$+n2;nxl`C}vnYhI~7 zH(?sYeC()x-%|WU)p$=$S*dSq>NmNpSMBSvqx!AuYWpYcADe6M@;P1CiS-4wJudfK zUn`#`_gl`H+@74zFKemudZCR`)hWN*@@r)w&A-eexlOq*Ey=Z4O!KbWlxt+ZG#OP^ z*QeWV%b(Mb+HuGnxAP1NkgCd@Nb}RSF*cw2xEz!D_3E5&&YhZT^0@3$WR71ML8pTGNr5P3Icj9z_eh2f?d>)2c` zZ3$gB$8?`fzD}-{x$#WvNL!cmN55yX_O$(v%`wzX zyVcA;F1@g#^`8;WE9{)yXAuwzvZ3jI}GV{+g5qF8&AY zkFaD7dxZoi&}#FG8IY8eG;6<=lvG_QyML@O3G%(|n>7g(n8+y5$RtbzT?tt*2^JVI zzNh065l?t>L+k@F1bm;KEPI6bI2j6f7jm+}^eK*(D<%?w0pM*WW;js4@RpkK?egz| z#4Lqj%gq^qC;8f*NtnCNeGtZbk*^E>RjYo4fn%X6Z~LT+mS-T_|B{Srx0 z1c}Q$C;5RbOmmm(+vPgOT%R1fQ}y5EvC6{rSk*t|Ig~|uQ0K^XJ;}$j$EGYaaz9SF zpL^=XY?tf3&EvYu-(4=BBVx)TwPRu)1(K`g;|6#{dRC+_vanvLc{i$Uo7+1v`Te9m ztNNhFe9iPMN;@)FL#|iPwbXabOqray_zs?7$oi@hUy2r#s7d?C_@q z2oFrL`;yb&M3c>8tsjzxtK$4ugH!b)M7PhGSmX25Yg=(m7XrBz>+m zNmOR)fK5P)l69zz!%Sv<8JHU6Y-d231kuR#E_u6=>aj5W_hfwjM#Rv9bVcctTAt0Wo>B~dQ>NeW<=f3h!)2FlTJ;hx7 z|3#~F-@bS1_gcT<%)9hH#(J)AZCh*od%yEJ?PPcVm>yE*VU2SC19CKjT}P-!bQB+7;aHiKEN5 zd=5x{d?f&2GcS{k07?UCo-l?n2A=Q^U3Y=o2khlPzq6$mB~)O#u4s_5*^inJ>!!vn ziOFgu+L8uUGX4Iy8jD_yS5U~1fhGzvqWjrkKF&2=TVm{6w@`cKCFywX@1rg zNT1Ki*k|`owW05&Q|F4b?-t6V?y)i*E?wL9scrv0S}fbng=5Cl{%KQrSE%Q{nwy2^ zQ}=7>e))0U_iEnO7^Iuu*K>U@b&YlXu>6d?Utvmer<}W9&aDYUrJjf0$JSNbu8D5? ztBx0+e7-ho^dXoGHZWM5efk;H=c2{ouqLhEolD29&0&6IL4b+*Ta#7x1?F!TZeqmD z&HK5V0yAreUo9hw=V;!xShlH0Gk@EqI)W|$)5B8wp&HCqvQA9mPOI)xHe~-nKBujJ zLONA&8zNYCCDK}b{`A7kT7QU=io~oXnzj=Fg-nzJ1nRz3W*cqpcL@^^q_Qjor19i> zjLF+6PW6`SG1<;94@|c3DhaJFU(bu)yr`PTm=LKt+yhdfz8P4s=NFXOxk%Zqv7MvV zebVAVtq72<0N9^G|55+|AOJ~3K~yv${chuPZCJjxg`5QHcB}}WO?#C98t464oLe=P zK$;6(zuw+jo<~dSi}cIcc^hSnZM&^SiTa+jb53pp%k2KQ^QZf5rO}w4kr37Qrrpvf z)p##{LEDU1_iJs8)b~)z_O^Z&%6IMe)b+LFQ_X6s<}BA$^;NAGAy-rho>psiep`U}b{yp%Ve*Txnu=sqp{al$x zec#ufn`*w|bd4_Sqf{pC#iZGn0|uYh;L?xIrN?OO?TX0C-+Rv$BciZx)m-r$H8%C%wwnOo=yvyLP|${-_I)6XUpn8Y`u z8GOWIs>LcjQ>L_CEo6=9CN!U=Kh5_wM!QsgjS05rmg-z9E9IA7+G@dFXfHWwCyjIUmMd}-MUP*9R&0FSXz+RgriyA3uP7;sD(c1va{!{S;W@r zulix8v9{;icB%z_rJYhQbE@t~T)3~Q{-u5|9=oUP8m&Dm+H$JP+$T8c{+|`Fn(}L9 z+WT?eJS@Izap4&AXJxIwW5gPxmL;R|_Lxa{ z2vb;~b!M9ToLHdZ5ps>VwiWnKGuFjz?sL6*GrURwTyYC5xyto*=bnwVXM33@H$gWm z-S{n93S=Q5Q2X3r_TpEaX*GV;tW)2Y{2-IYzc3C%+W1FR*gj}Bn51d=SebHc#wdgg zCWPt62OkSK(&kuD3*>!&V1(dm>w|nRq@>i6v_hvP2J~s91=S1~SNNek;iQ?LvDD}7PptKJ1YF7=E{c|GT$xhJUlDhuO6o$@@d34(gQLOYK}UDNbI z#v#|u)&liz1wF57zREo5vBKy>Cw;DMw_-?YK4-#0^9<-Q&DQGX z`}C}t+4F+UK63T28o9_Bm99Q<>C`9FaMfvYg*Sw#lsrH6T{HfY@lVa!%z_Q;Nq)2f z%yj*d5Kx|#1@k~jeka$Eep{D#6~4aLr7<9-0zSv>g|e1kCdJguALgGjxC)P;kDJLw zP)S6QYVuHZ%i=2@D!W3ct@LFpUV|VGgssAEw#c6LLYjT-}$?J!}Pnv%jcg{PY znRC0i=*6@whH+lD)l#aq8o{7qSXyvcU_4bDT60-?6b#kEx|C3)jI&*^b^UqlrTunn zs$4bJSf4NB(=Ufyx3J(W&WmO;G-HbMIi$H(_p$0jGX|}Bmp-&}r`uj&x=26Ub~STZ z5P~$#G{S|smUg7>nO1s+nU(vcp5<1+&~>#8rwBEszMrMf?eo0Qf7{+GRgZo@t7l6y zO{P9ng18!wo{MsC8$nU(uGR#pPYWlFKo{CJWS-Rhtc11Z{gb+MpQJCk5Bk|}*NuuX zn`sS`wymLLk=5;1Les)~tt~}bxGdK;_j~s2w2ZKf*G$Ud2)+X+u={MHx0%_5yylm8 z#F%Lv%!*zZ6OU-0({}Ckm?_zgQM}B|_;X?OXUmZK<;|q%7VMmQ?-ot^G4o>7y=wqK zGYgR1*t4mC*GO)jV*YESvmt3INH76Q!U1ZcD$QS)H;phLZRCPWW-TFW)5T>{Tz*IZ ze3mlp6dh1ZL5gvfw|8XTmN8fzl7%YMkn|iAGSQ~??Umt7l*X)^T`YpdTleEoyP!@dc z=UnN-;xnY$m_1Ko)$`49=5se|Pwu^b26W$<&yTLRont*en(36X<3b$^V{YF~-G}OV zUs#VMG}5YfCXCJfV;b-5og7(wX5_qn7u)Bqy>@MUI!wJX)2q*5pqI5zq3>!T+|1r- z0kc~r;jXQpr4HsAy-=}on~8zDwtTD^JGwr`PTgyYIc&D6$oU5h!7UBJoGC zi`cMY5h0c+ut8$Q1_`l4Vw1?b2>1_B0$U6gK?*{kBnn{Jf<%r8wj=yQ&(H7Od(Qbz zH;d`&`BZgP&wS6l4}-Za-Fv>%(_O#1x~jUmXGXUl_#t?a@~TPCFii+puF#G~_$D~E zPWZJ3TiwSliy>$*|18}Ax{-_=Plv~jgbZG9XL#%BGlxH1G)^8T#yw0<{!6k^ZCUP@ zv9Hk8ozm5`Wq5WBO+D)j!xcONkNXeh5(J$NUZuNCpA80>MyBUF^4Fc2DOMl)7+aZ| zc@_nq#kBYIjmz-P5qqEDG7cLQdVzj4j|Gm%gyE|dZG#W~dvR!V0%iytwg(p@x2rFI zW$);>nrBx{Hy1UxFQbeh{c>A=FKKi7%l0&Yf>}~O-9NmgwmA;>44lOUfE|H6;U-@T z7cxLPUZH|6TDg{X>(DP#S*h=AR8F{&` zV}O!_iPeqSm5hcMx4?mWE0tFxf2FN-W)X<8_UaJL!BUob+iYU2A&Ew z9oxb@GJd9^%iYXl6>xfU+N)z8VDyl5urzyFnj_E4W?OULd)RGhKg0*@seQG*(iU+G z@wpJ*1(3(Qy*WPGpQLcDM~+;A0?q))qSj;u)|UPUc7NAl7?}-=l`Hf)rz`gCREF9@ zNG*GjNogm;)sWQP@mgi~(lKG7{ifA8bR|HOzF?=nm_Ebl%F6Wy5d76^2EdzoQ3`HT zJ|ZRu5mGX@DDBb@AZ9vbQW{Ppz#?R|)2F$%wi*5LJmmIw!5FX!`wdY6kfp!PC$gf{ zy}myJn#ssi^e;(uz5t~Ff=kgAMTi!!z|6XfI&@n18--}ira$jLb_Rr|LS$u;?#q`wrC;VbX6R67M=%x}6uGM`=&@1#3|~OQC-ReV zv*W>tNVodj$=DBbkq*n;6Pmhh5P4f$V0DxEsu*>)_3u-ka58^GMCsWZV$+pB=}tGn z75fIzV;8Z?r@g(qSRjj zSYSJu`+auut=>9IKIo)Mn;@Pe=yWx_oo4a{l=x1jjw5Hv; zD4tjz(E8Ww^4?yR7EqzlX<>WDpAX`R{4<~qk}~b?;kslTw#~$m@nzZd{mVu7?S_?f zhTEudNRIxQ-}Xd%H}C|o%Z7DS>iF7>rNT{kCGe61OzcGaJs5cuD1O!Wr5K-QqdFee zU)J3wTh<916bw#Z92h7N@o>?&b>6W%EA?9Ps`(mdsG}Y;f0k#pS|%9kcNzmDf#Z!8 zOr1KQD*t$Fp^Ly&2VqGjs!p;2lGbk-S|y(=+>>@alMob5SHqE#nPwsNJ!D zwx73%?QPq&>yH*K%buCQb~I(%9AOWQKa+ZyfAh=X(h`?!icxu3!z2Z6BbhB|Cj zwl>+wRc;OA>)`HmCX5HDoP%n;eL+uIdntGCpq{lOz$h!x*96 zHr^SW-rk#^=Ij*X>cNcIB~Ok*FWbbD_N?j2(pUWKq??VC)&8(j=VDgx-a>c?9!$mx zgwwTm#)b{^gnndf0fKxDu#}30GXNVlhY4AQUbVd0P%-kzp#p86gI58>$RH3Bm}CvO z$lUgxir~zG3Suhy2~O*Ty%oY9+3uME?$ePhdt_XwZg_R1S)sYb9&q5jPe4tMF6-6c zCmj9p5QC;rf0>k^Yh_fK56>W6vB}mOjOLII3-7g)Ozo=HGlad*#phM%SF-VFZFFQA zzgERYRKceRb7+g5{yra;xgnSS9S5IM7kb2~FVseU%N()kKy|3bFM}Zw-Y&Y1^`;0e z<`)i+b*I@|k*Wi00Zr~X7w0U1f?>y4R)vd}p>k#po_-F*+xIul-?fKk{&X5IMjj*w zJJ+SYP(Kco!ntbBSCs3hUv98#4*oIhfquskcYSQ8yh5>BP z`pbDN)5aE&>)M^g?tBz$%{eQ_UzHh9{WzP=Sr@%1rA4jh8=Y)U|82*|TdZ@ZL}vi# z;KC@II72d&Xk{aRhy8n!?;s?TPLrt1rvIgQ=K&;`80@ zAI-2UW%q>)q&ZfROE5vD-(bA3JNypMadf=O?{&1Y8&(8PcEJW5_VGOatD^Er>3q_o zEs>)haI8?q7{ET}Bu9^i!D!3)k)!BxG5rfGw^z(+OVKQNRf;SK5Cl)fI2lBp?fw{y zkzJvju#%N0mnKs?HwLFee5fE)R&yN4G+g3j3ba!iU|j4k zjo~0{FDBp~@DuF5{d0k~!}ho7KhuY8e3Snj8jRc1@^qS*c$xA(VuDxH|^ z-+oR&(;g|!F!4Dkplm^*f0?6Ej$*y220>KIbVX^zXl3j0#LhLF)gC(rWnfIe!kGhQ zXo$!Dj|Q?EiG#upSm9Xqzv0T!bGWN+GXy()FUn{z9qkbuEBC=cl2sToL&>yr;3yfc zdVKl{bs1p{K~In_FOIARhhc+D7@blaK&lTK6aQDzA73qkOnuD%ZvS~9YLl@2pK;FK!qk|^y`HwP?TTN516v|fKInkx z4rc(R*mD{fuP@%02Dx)o>`I_uR2VNk!o@m%Zoio8Grwo0x8uDW@ZQe;I^kIq9y%N< zLv$|JIcTz*VIF(BKH|X}d)3^IPj+}dDWjvh2+lww z$!iEA9M}`~T|JEKj?63DH<}OLy{Ndbj6C{D{R5y@Rx$^ou^RY1NvkI#*H#5{)xtxg zFB_nd-pi9Z{A32#3p_QT41YrRku0XR4#1WV$NW4RjBnwEYt`)|6#Z?1<}4hXYiTDi zqw{iM@!Gd+h4owR94LXu58shsQl!y@YxFcw#3r9!PWT z?E~3zi_!2h36WxYj@`p~%e{!E=-#Z(W+G;Ny?#>9lhZoNsI4uweU!0@#_AUOZ5`tp z9P$Ca2#=>DUZ2Q_dpO+S>jX_Rx+2|+g188V)Z5_Iqt9z|-Q&Rq5>a42&xayBn&fRsDqRmp$aI!qcqcOrn);pq%xfZ7M)}{K$!LeB}j6xuqkWtC(tk%9B0paXZ&}z z;vGuC@cevav?FJ3?|J3SN_U12WxkR*g6lc$NGH8jkA)pNy)rklGn%}h=1e(j(AkiC zof-m@|bK;-^RcW3Y-y_w;*FFLY%W$E$){6TgHblSiql3~YJ zbe#v=>ZZrXEeo8e*m>>AJJgA^+u4lmz5d$fN;~*_^ND;-9arYPFD*8COTcCZyn}ok zV8He@G`YU4(w2ea(?apruW8n47zV?6yN4l?*Kih`t&n zwCSLNc%b{j3jnN(qePPFXniu(_i-^aX9ov~XS;WSmT3Yq@ml4FnS2aS-X1s{tf4-+ z|2Otm{E4hgom{I|+&20=d1VhC<03g(xcu5$OK0CkvU+If9u5VWV&MuO0;B77Ku15X z>1~g}ky$_&>(LO5JAC$6^U_#~j_|zA2=u^_z16FM4{0OgV|etkjghaNt0DVfsPabcXJSzrUGK@`L?<8g?B-_8z=jo=l1VLkD7lC4w91_m&W zRUg0V>zsR*%jz(V6W4oo#G~KSQE9hcwd^~s3H#wc_)g#BQ}H3!&fHdk{tEpRJ?O)cAl>L&AU4QZv41Q=;IYAP*UO@V0j8(pkU)XzSK-m5!Fs@D`AekSoV~G= z&IB!jcbKpwe^0hO1LU=#$=CIU>qOR9!!vey$#uskG}%lL1*~3>*9%)?OScJB?YWlq ziv4{UCiORrtH51GL&CgT9zy-KnK;AK0H0~FutY`{U*xb&Fy4b9o<}5mFKycA3P!x^ zm2`jI;k&wRt~<=ASf_K@$J!cirDy9t_L1(|px8SI#&fE}0Q%yDd9i_-JC2P`HTUg2 zFnFzP8iLrs`40|A^oC$~V+C=rY6uYYK zb>`X)It=dUn(69)uQa)+ZC6Ia8#^kOj~NZD?8J-oVOe%1cW?+i<;0xe$kNq|Oq@it zdCn7iT@>UgnoMxhC#@4WE`g3&TD$SK2Gse@ld^*~2G|BLf}_K)ra!2jokk1DY+%s~ z;Z=st(7n>{kUx;&lWRv;xt|y=h0Bg3)0rs9TpXUNo(nvt%CQ<9@3d~Q-5bSq?`aU1bUuUC2DMS;v=h(UM zME2^BoV5Mx(jtTCIAX6a_mhcjw<|n&5lp6WW}K8kUPk-?rh(cKZ46f#!3H-P3zu#u zd%^Jfgjh%UyH3Etd6fA<#kM+g0xH4l!MBc(>F6sanW&ulCO&3?k`xsb7#sqBS)fvj z+)e_^A;5C*M5Xg;3}5HNlh3;M9bh}UBeED|BF--&&cY$Sy=L;;!sC_q5PVEz##y)_ zIBa;VjI5q?+L%b2AtU?I?V{7o1Le*Sn>DZ>!)bw%dOW%bP8_IO^>g5AGc7Ce5YWR; z4|u_6o%e$XmZ9i6s_P^Zac#IF>fgDS7ZzUq{cP@%3nM zOJv|D!Hik~uv7Qs9AqjY$1u|~8`MRP-RjFNfv_s<^f}8zeY}9xW|jE9(gW+LgCIF_ z98#|q3n?CJwT&h;h%D`bBMOu$*f@$WyD>j%Xy}pbr=?YbRlYyqGr^FHn*&M?+!NUi zXs5{a3`5Bh&`(C0fR*WS?dnQ_74c)-n?=E}IK@7GJZHRd z>Axw9<7NI_)vl`I&_VH71zRvy)=>}}!M`=oiu7U@n0bGrwrA#eFXULf zx#3#?{AwERBf zTxU9T>sWb#$;>W#IJ~h`Iy%YnL?xP&@_Qy%cTU;!9>(4-3*2o3uRVZa3u+cvMq_ox z&H(^&9kTD`S~X7@+atL^e>0#y1F*my2=7H9^0-0YgJ}JFWWvvMPZVx;FEW-^xz9MR zGk9kc%j!a04#HV#qwtYnZQ6TcBL4c*>sLaM$U`2n=%)>}O8Gdx%ldE^_^@)NF z(zgHr8g&uLnw4mUbu<^aoRQI)U@mGmn@66^XF&tw zi1hxJASeplymAx}MnT4{^U=0FvS+jTX?4w$CM-w}`o;ib=(e)fgw8}VB>;ydPv~8IgAkTrGF)C+p z*`Ua4Gy8oMxOhS4ELis7d;h%lx5~tmE&mq$k^VA05l@kxGLBiFGkSF-SC2m)liApN z{rUP<9gOIBt}aL8cP8+P=wjMfrblBu3xqr!IvbQ&d+FKnGePhyfUq!es7pO(So%hd^-E924-!_2u=sj}UX9Hae)9Q;?yZ|RE9&H}Y zR`!t-j%0h};Ewd~Il=K6xDg)Dksr}D3y6;RRs1h&L}l3lbw`37}17hi5{4<*E>LTKW4LJu5+}@kGkpf5UXXPk}W4N|NxOt*^4n4vVjjLDh zm7_72Hj&ZxWFPUy^v-a5w4ISBYXnO@;;llXK0MtM|vK?^@5}*;EMt_PiLa> zvh+p8&RGlVNJg`u#Ns$RkB=NS&!IB`^2{-NPVz|ZGaqqAr_R_SOPgHJ{E}yf&lHx? zap0#{{`xyR+u^?c2w<-I9sn$LI|SJ8Z2(ekZz;-q+q}ISprAzG&?@L2e%tG>$d~Z| zAYYX0{%^Z(nSe>;c1z?wE1t4h=OX3S$l6nq8B8ze@o2Su?L@GOV;xI`GlG4_d3*F( zSbTjX+nI4VV+^B7<2hvOaA(Dv#AjC)QM)5U>;+KIHEhvbvQ8s1JW(*`1zzpBE7R?Yhio( za@JPd;yWT8jTQhiTr)lJf*-GaA%_~hQzs`{xkrn81>5_Yz!pVZw&(o?>3$9%&6K*s zs}ezI0x1(vu5!J-_p!)Vlz{$f%5nu3^J>9m(R2%(4o^ORXmBl;J@80yV0yfG-`ylW zg_iz!<7L0^4&rrhxAQ-iRQL9_jpfryS@8C4mxe29C-j!0>6#g2zQ=ex{45ier^RDo zT6(2U@Yr!@yeu;dU+I7Yhx^&ORJiY$jICHxqU$BkVwbY+^?{;Hy zh<+xk8J=N`8qYJ`)w*5vGQF+vbF;Z+-4E@n?Rji?tg%fwH4tk7aHno=aY04@W~9L3 z*9LfxC#=-KFcccNXhKR*fW4nMMdT3{w~>FKLGDJte1KZrv){#}cOMH(*#>dH(m?n% zqXCZ~m<}(3Ukr}@5O>E_4Bp{m4LV@*#$i^XAja}Kd@0h>BeXb%9@zNgx^5^*7pAn9 zUC;7m*R6qXO9NjU8ax9o?JS+PbbYn;R`1};*ajGp&RgDW=>di_AH4bJ(c>pqdZCZ!UBt_vOO8E1bNFqBV@89WyMj;Io2aj23qm@ru9SETrytoX)pzRrWB`5d zqVRP)1h_5qm+f-}%LtH5+ALu-Q-rolLUcg0V#R}(t#usvg4EbS({(q&~QLZh=gt0<7V znEPfNnf_wrrT&^pJ{XH)`cvONQ72=qil>8C4-I`@5TOh%Wo;OT&4wu$c5IxsgUqQ< zAzD3|4?&h48(Zwk-tz9~zqg9(-HC=MOXOu|-0^|Ic!N0PL6Pj!Q%JnX6r^q5iT|0sQ5I{05+0Z;|%SCk8TO>>Zf+UWwTDwG^a8urSNFFjxVA)o2GR;#fOUPU8TT(qWIGT_K zQm70M9=ytN$CvrCdSwGSLq~CB-C?s#E$x;sMiV%@ z0k#wP479Lr+49GEwwn0jxyoax;Fr;#<^&Y`-JMeRHnlb%ZGwQB zHiupcwig7hSQ7B+;fFON-~V`9;XiGs6jCxP{_VTLsCD~@fJ7JxK(1Q3a}a&>?>E-% zv4zO^)5=!e_e$U#higxntkF7fsH&}8dd`j8PMqlAT;#Xc#u^V8qvG6m9dFbolJ9In z8aQsYVzN>i;M@Kd4oBf0-L~Jv;Q<&kPnIqo3)0CjXyJZjjk3uV%i?oO5w^FG5{97a>Cx6F*-VAn#{OG zS>7ISFdxo*QFF=6<=HmF@Y})BGo5BkrCraMSUffmy#js3PZ@Px>^uMM2~TWK^OG;` zO*7iUeFbnutrdU#z27gFl<&V+04#U}UcQg?qfOcZq_*b{kpV0fSj|zwQX$V4_8_1f zyoZ?HH=yh1-QIec;g9a!+U5+dw^28v;|TY2aM(7tIN@8yeJ1a-#q% zKtACahp5kqHpBj`4bt8~|GZUA)ZW8u4X9h3`(EZ_*8UWH`aPqo)B_ntddJSc`t>){ zQS|SrT!8(=xCbkN(#;M%tsmpy6X+34_nO)&7<4b>Md}-N+gas_I_T7WfOu`Ex>DPb+|J^slLeg`>U zUVaV0wz6M8#T_wJ+p4{^1Ayz=eh({sBCeo0_$V9juDmPn%DeLaesLYePZ-8ocu~9V zkS1`vd$+Vd!hLIb8!~*Guz2-;s~m@V@ueI<$m*Wo2O#PH{#HKm-TVF5xd2OP@zT%! zQLP1ey~n>?akt{B0(ijX<@T9?itXvZQY)^tWdhL72Jluur_S7=sZK;t#vc{-Uk0?%Ks@mZ=+bb z9krX?$3Ytp4v&>wbDe5`;P`>f>C4M|=|w_b zQeE-Ei+@s=FZ`gE4d=_xJ+@Z@Y)G!CD7D~=50y{c{ zbyU2=AXf0jskb=K(CF292TqRKTf7mj8Jr`0wwP0>p%FdVMSkXk5A9 z^<=vUUnIjBecs{38EkqCceo!JCXfFJuZMFc!!xp&oeDpAQHu+Yi?uDc=*(;mT3EAy z#j`aLEGga-AKp73Jbr9@PjAlz1NJe-nNFOspAo*O&yfz^YKM-B_lRsXm+ZCe{Cay0aRPGA^?&}akN>gk>gOe2%l+Nmx_t13 zUn?tsb=&iQ`Lj<*X^SXIX+c1#cq|2LDX41!)-4;LwgjR;K)yL3f3w4!3;+*AWH8|T z^~y}KqCv~>@!EdTB(Ms9WQ;A(FzqAd1bjua^m#N{*+qew2h-BW{LLl;ocn+eGs1Cn zV){&EKMT?#9k94OJv~eNjD|CTK_m-H8~>h-$yvwC@GSi-ui1R$pvCfWRwmJ@?kkRE z?9pfSm+>As9IGeMSb2T9jih^z0`th8crvtYm}Xlt&W&LqI-j$ew{jIY-lKkJ_=D5N z;)~i^_|ba{ys@(Qjsk_DouvAWUz}}`d;T?v^G6Q^q^uQK(-SJ5fXnuj|LgLn|Lgzw z?(cj9KuxLQg7wqul3uRq{mVbEufOy+*T?VvS>%f4a>WOq%lPC+OGi+1K}i|cwIJn; zyQLtNgf%6s^3(ynM*s`Z9|@FQg5iJ!5iPGD&i?J8Z#L~KwW+2ZoHmTpUP}taw&!-z z*3#h71^9Hrw2ko3j!rPo49AlN)4}+;{mkK5xiT;Ox0>Hxo%}wMv+Zwb5+1Vim-%D* znP;!9N3+$VnO+I5h%cD79t?&pG(`Fv;b3_%U7pU&@LS%vK4Uw8!g?Zt&cK#x`$8ctI)svAO%SdNEIYhi1(arpNW5fOBV4BB+ z*QU_@nX`}Pgq^=Ln`>pk^7r&TDp7D^?Pa8+79W>kwU13+%T8!1Z9!duzPPRxHLZ9| zpJKTH840L&>mPpm+yAKT+OHWvPW9EgEKk?_`}-?ie*RZ#djEBO+}{14-h0IT%bkFy z0$giB!L}9fw6-k)iA-`uNp*jHp*$^MBbUgT*~!jz_May>zUQ^|>OHs~Ob?Dn^9;s} zhFM#W&NH%zVA|r*anyE(Gve=v_StyN+E^SBT~WJR?t3t!c1LpeU`Dv2F?!C}p80*2 zt{I*qn$F;w>Dw*vZk-P^na|2uI}yQ+=7-nD!Z^b996rwK*Nm2^->l6Mt!HE~gLkX0 zKeu>eS

CU45S^Wd-)v1E>A-!JY34l#1(G@H&0GrT&+Ml!1>Qzw}SP|HE&7J0;Yd za9uL6{KH@Q;`>N>L0VGE=_4#3{=xd{<6p{YO*I2|FIL_{{mhiTEAPs?@~*rq@5;OKtRi3M1@_$oV9N&7&Cjj{DCvsVm*)I03HNuv>-Eq7 z*6)7fUw@@m)SU5@6CU$DzH`C#hgTq9m%EJnyL;4HznVV(^S}Q1(Kr9reF4_{isiir ze0aIw;iG#zt=o3MHCJ2`kZZve8TTz#(?@Sut~`9iw|m$Vr~+7{Wr9kQqX*HW=Aukn~)VYvs8fO`Os<= z2bSfE_kZq+^`rOj@GAF@1#M2G;;8_4soC0uEh4EED?cI_eW?i8za?!MH22R8v}!{Z zX#1Fg537Hn?j(F}f9NE##(TC5=i6-mz`A-eRPQ(EeYW;DqV13dr`*b?Ax+K_xs()U+dnN54tvg zx|WKXAMtqi2~w(9?h=*+EO!YH*DwF)|M>Oa{=4G*m%KUu`wJcb;DYrP@btr{O&LqQ zTV4Y9sb2B%S8)0H|FZt*+rPRze*E5gpRhgw%LlLV{^x=9$1iYwy#P{&HQ^Z#}>P2WC`e_gy+p^f`h%8-g^B>nH|aDh~B7=(E4QWkscoDV$^P+<*b|?BQ?M})5aDVm*_st z2&NNGEakEMS6>7VMH;$Isb>djMp#n767FB@9sX2%iV&zpUGH0cPY!Cy42-mE+sFIPmjyv!~K`> zs{FI{@!P*}zf|BpA*YIjgypV%itw5NB%owq%}DlLhzBWVW>o45-8(b?d%KqI&G6I6 z9llKqdnZXPJUI7nEgn3k%jweT1IDzyN6X~I>mcu0s@`i)b49IJAX4-vHkh52oeAYkRaP zv@mA0!f8Ky0wX7Fd(^equi5;j-2xt=Gr=aDB*Fzx+$((~tf>%Ab5`5kcK=2$$3%ly*4q zGe7rtf&c8XS0A-~Q(QlZ@>$Ztuz%7#-!i_p!uY?X%;b`1DKX3+om0yz!h8G zO4{<^FipH{wX{n=$>lsbv@<^zmdMLf4CmYGAx|E^QPx0cwD*|?X+XU+cB#(&XL!8A?S=m%y|=o2v>H0o z*5b7D`kv@}#Qt&_ZYMHJ*yMjPuo!nFr`N4MvKs=!OGUBGl3cc@Bl3GDXRGf=bJ^p^ z%9>#w%^5Cd+S+jtzGXhl=2e8t(@id7Tg47@d(ZYuouuY@jlEOH2bWFb{*-A z-0!FG9bV4PCu@U*W-Cj!pN~78S6s@PzxbWkkMIBX*T3~2e*NL;Bk`~$CtP#FbxC-< zEO>Y?{)@xNLUa@~#qU^}6hC`VFL zdyG*&e9R>S6$w}?(q;Rr`XoV7_sv3OQqaP&v*!coc2&n_(bhE`sWiwIRqK%1~)fe`-#tuj}+v#Y6a3lGD$l=EG<_k z@P$;7Njt-d4AixC%`B|6J9`#hAFnos4QNGe4}jJVr?$R*%c;7c-Z7m z`qKG?I#{~#BXl(KsY{n-mlw%awyi2Q{SjJ4NPRq-{8e3pmjRS5FIlVT{&tUP*iY_a zH*|nD7+qPmk&|H^bQilEYP%srC4c%P=ZR3wJz=%!cUp{|=bauj-W2Z^7hkhuZIxZ$ zm@Y+Q8i|v>4R>N58^5}RhnlyytZ#a&L_hRGNuYTJ!m?Q=CPhQG|ZjorR+;Kg_#KWv@ySrN7IM3@hECVyls$LJJE%%FB?q| z`H65#?ejp}r#6p7=h^-duN_Z9xA8nIfb?q$UsA128?NiM{NZ=Mw*IBR^6%Ly-&FoL XT#Dp2m>_e600000NkvXXu0mjfT8UJA diff --git a/taskchampion/docs/assets/cgi/icon_square/icon_square_64.png b/taskchampion/docs/assets/cgi/icon_square/icon_square_64.png deleted file mode 100755 index d9d63e823d5e6a1f681e7d511ad0c202e685f293..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5036 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 z%0AM5?1Ov13f|`LFH-5uhhmdV;1ILpGD~S{o?yvZ<9L@n)4t_MZT;;o zBK=ja-#Z5DEN^o5DIv-_drN@}mKm>{c@tVg@Ro8bDPtb7cZ~)3MxA26I$f7R0*Fio zvK4lV^q~`TM!Z$25Qs}3fLDs#?%RWb7M~a4Sht-)=>v$@(MR0&gE{Z&9HP)`g{nw~ zwxiHBZbuqFt*JXet7X^&B)K!$?1tyO_I=`;y3vMMNq|Dl8{AP~?yFv7IIgVMYaAMy z#^=D-a9i*fA@^-{)NUiz_3foT`R4U(u-RE)$nm!16^dlPyxsnHnl0tEadlZMBKty3> z)mYqHH6G;x_DSd*k>V&C#z&PH&xBx`&0j<0hTloxe4ZlGLc9f?35SV3TR7v-N%$SH zK5~F}(Pq!iR>I$0bycA~Kc@@jcxRi{Pg2+an%_u_b0LJ}yTXgl$IA!K4y5( z_+Sp|9s&d?M+Sn6Kf67bA2)aQqPWy1T41_fsfaf8O^~}kzLdJ-m5dnp{2%{(f96=d zgSd;rEg1%hd+^qKb1K#qa}Uz^;+>?wv#gpdJnSr3pCJjRhZg*}Yav z@;~tdeXNOaeeE}=m5_&pv^|MpsUKYwh*G8?nw^e_*4#Jxt_pyavHP`hpz^DoC>Wd3 zI<@Un>bNJC^rWQ7&yo(T- z$zB8nC0@yUvtlhVNsE7;DbA=_W9($-10}yAF%f@k$fw*d z?^trDuGG5Mg#;ayOd&b*8w6-@s^3XWQaW!=MS`SnzKAp_{{2A0Glv=kmEToL&W`8V zOMQ^+)N}S}%c#Y$@-TVvk8#nDdh#BkVQhcv;mT1%`xJ+b>xPK`@!|saI1IgSu>M=4 z=>ww&t=8e0P#-O5%R247D9WGMy!~A4;Jwfv9HTLBLsxk8Ec_NsxA2>HDNv=gmIrZl zC@4nQ0(Uq`du24 zELpPSZRD-R8^dR>`}<{iXlvP3>%R3-_^r4)I)J_LOesBa9SxPeYP=j`#>ZHX6UCEJ zKv<+<{*&Ma%iEI+r}Xd0K$@SFKE{TH6uc%?TTkz22q}@!50+CZNt1)KJFKVH86xGk zRN@8j7vm2GJhFdD37Xcmedt<(-74F1@T~3d`ikhGs%$?xz<)lD@);Cjl2!)}(h7mmJ1Ri0>p0+fpc>2-)=WP7*gq6I% zFgtU&pZ>ZDB-^KnJ)aAwH3BFVco)IZvs1WUfgifT@g+MdQQm6|DZA^HFA(jiplK29DL@;7Pz@58ps_s>7u)%)k|}ZHN-&2mESnJI<)nd z(e3@E&O3)Cb zh$_FC>+f#Gh=BZRnXQHXd0p1|iuEq);OR#J;nW=O57kvCq52KhE-&G}vV97kngv6A zPN{>1uhnvWc`fc5GG{$H4Z}Iy!66d%N*G#ryrHp~HTq9IS_8@JrFCF<4MkZhjLK`| z+V}El;6Gh}fss*tzsgrk&?XO|^kz}}-0(AySJ}a-;MX*f&d)^jx4}mCu|Hh+;C;^s zQ#?KimGc)hu$?pU+`a&70U7)j{hV1`G}!AL-pUDLef_xh)(d?_iYA5lc}o^#lH*9P z-RwFy;pS%H_}J10%@6N)@tu=j70p9JAN5EJw&PR@2}$w$SkbgVg(0u8DA`?LwqDQT z883Avr}6E0USj~aqG>@6fR#ryvTE6~`4E^id%zZIRz7{@xt^GntdnQno?qp?jfeF4*I(mWe#oC}E%H?KLDW<-tZuo=nJP73c0J~NrmCmY@!BJ#97 z_ctZHQRp*v*n)@>aUK`o<7&$9aCr605K?D(8KMPrWo!SDrw{=}t|LN1yp2nU14~`eDQnwPz*L#vn<(~guC{-?i2mPy-@D?!=8k@7G z?$o)!i{>{fYRM9}i-DeMl0t8|2{z$Jnv5|O?+5lax&F-p=r8{SOtKEf2;k8&BMu=F z3WtgOLi#vB4-D}r8M8Hk>QxD$&nA?&A~fbo;mWJlOX9*TxNNdyw^s3`!l8nA3nGIU z6dqE=AfD8HjM~~}xwpZoVX@WI4AxiHS+1*NyKi*NSc^10mI^TCR}3kJ9W34_)SCL^ z=cLJe?bgT2^sYtB;vouoHfHkBij5n2K!OC~xn&XBmd{qbmqB@bqcH*)OV)1iD6$|P zZ$u}hYrMP#Zbms!--_#^cVjL981mDh9hO-=T3rc)HmrTJaTo2U;#s#N+W0zl*oR`UcS0IRX^j6bHAH15f{9)@Y zN?&YNBm6ACL?}nn+%|Q7yA`n_S=u3m@vbI0k9ZC-5tJ4ytumg|YHj7k>~)qvl`Y9v zA+Fs`-0r2CyxW_a0fXs>|6V^Y*CJQVv{uE4Mc%L52%YT%!|lt-;NM?|y^%ayj=8b& zs%gkuvn2g{qB@-=Rz4v{Q2v>op_n$+-RYHI>y$ZtMbf}SGm4H>W0o|?$r@k!o1khy zEea{k9!|dW)E=<39WUvP9$fo|CS_7R(2S5yWg8cLO;Tl&x|+f)P@+IBX1(oa|7-oS z;suvcr2O7z9(I$Rylf1W$Lj7@i`fRsJ!NP31h)gYD6~!PpEJqqURMSoS57VtA}=by3P$DD{0<1@Tj+!rZ?Jn z9BcT7dO=p?dv`;FocoG=np=w&)PF=XaN2W!aq)jW7f=w41HMyxXOC80hV{2SY(1cR zBnpV7VkoqND7?0p&=7VY@h2{H>kqVaD-HarcgPN7Dkq_nkJGEw{H56E&M6>gA(DY? z?yb8Xxv>C~qq@b>7Xs<^a`{=q2r{6q;PE z;i66XWF3kHuWD9){q<__{L<;`%_LpGa!aFvr{CQ_O?)Q58Y#cW5Yy>KQGrdnv~3hR zy0kYd>>#i(ft{Za=;r-Z)RE*A`-rV*`hgCX7ucA!s9l&t1+rAL%`c)a%$FxHf09!e zFL|PW%O~3R9+O6nJBOk>yc=NoIA!VIT4V5gHo>-I(5spjp2#LrXp*&LI^<;sjJI_b zkl_dWIWoG8)~mO(+q;i8HimQNemOe-oS$)=A+7+n`1agJeh@X-pUyTo6+)OOQXm5( zRsCHXUs@c4-3cI6oATmKU?E8tN<@k0R8dsL**vbi*D~d?iI}Jjt5~g@`kVE-jNq7$ z7Gy!-rVuoJPP#;Z`fdLK zH-Gxq8#~0&+gxrb@y8OssHE1`A?h2tCE~0J(!G`zYemHArh)*uv3my@8GWp8 z9`mgcHdxmdU-C|4e&wjvKz_z)L+$_-crcXnWP*>P0*^ry`xKQwA!;LA?q|~jgW5J4 zv)JO-*nL>u<-KvQT52cEILQ@07$JKw>S6`pMr^MIVkaIZ`DHs8~NBH+rWA zBi|V4R%1z3u^A8UWxhJ$*isSu^mt^8=T}QV5Ld;P;g5r*! zz0XS0CIm8Qwmh^TpM+p;0*SNkQ%~%pE>6wp z;$5|jBWlmUkOUHi(7a<_6Um?=sl8Aw|2U0+s?Nu4nXRFvTuiX5&wcQO=3oL#a=k z8jUMmq9bIDDSDH~wo%?`^}|`O&y&OdTBl-?{CAOvYPLct-DDP_nB3T=V3@RqhKb&( z%u?4q{2BQI|%Iwf3z?tI*dRBYQ^%rMxx6tM?1vod{5;WnijSn+;W6?rvH3Q&f72B$Y!BsC;_bmcV*WqI!9tHqr)fiNfK+jQQs$$)>7f`-kgd% zJL>uL8BYprv%V<)yACV?7@sJ*{Pm)*VH-wkgq}`9#KpoT0Cl(+Ih#q5t_vIn@?q&D z@(}q($N=$8BJIza1J3g-Eo+=W^gx^!OFwD!j8Mizq_%K@8miOh;bv??j|Tf2?|vn_ zM~)RBkC))hqPzxp5s}ImdpE@-8pVecyyv7dXNs!TRp3mWsa8(YYj&>7h|zE#JFfdY z|G-X@>`U}h_%X#Nbgigg-6J2~swC0szDjFvG>#A({RDcT?!y@(81%|d+shp^Z-oFa3aVG;W`#?=fpTL26mgC9~;6 z?bi- z_!L^H6^5+9-)=sF;Om)eCQDTBB64YD5WC~I(T=vbkz=w~rCs3mNZ2T#9JZ1sl_ zmu7m$E>l{Ef={HU)Tb#A$$NLC%syZevqnm#(wor}CTsYiK+D74b>!-3y#;YiLS^2$ zkK8RhtEs2;2{AXKf%jGNSpZVHpq9PIVgfOY6;OoKW2fHvNydt&sBs^qTa<@S3Ju9AGId)${^O`@CGj1UR`gF5TMQ$jB zYBq<#-hylT83_k95ImcOFiRdFG88X~_yX{&7R%LT4qy`~K-a$_ zn=t~!WN=zK&}ofW2?@Gq*irw%atzZCNf|Aa>s&4-H8JOu4vi^>e9w5GLUPgzH_#A! zfBWLBB0;K-0rV!>;+s8Il3&%2H=aua5K`vc96=&~`{BlL>VfD^A<}_Eu*akFz^(Px zkbke1z~U>C)!VWoU%f6-ArBw_)2PSVWDq7@O&x{Zy6#xb41 z_TDsyB{5@=*=CmiA0;fdww(7NjyYmEnR|C5Ne+i8PL}VUP8S_0(Ys(+{_EoNOpKF{XAJ&rE4~mPHy5_td*_zTgWi zmAOpjVJph1k1_|h7FRw*A-K`}DoogMfexvSn23?dIs-w&eHtyCF{8M|EL3dDVxysL zT!_))o~|W}^X(F(l5{Khhre~*6(y zZx%V4)fO?s?eJv{1wP)NI_+-x%ZhWqXt&3AMuj>>`AxmbD-@AapZ>_*ttOLNKloQD z#JhP3E=+tt`+~$6|A0xgqa|ZL&kKsmDYomL!Zjy+FmJmV=hHlMqs*|HJ8#!z&5u6TL9?=GjPIZ5STa zyz#A3)_8xm-Jxs5U;(mpuc;oX{#qPo;hU3?!WpDz0~yU(TLQEu4+Z2=L>;bMmsd=T zV-tx8*w0g<8?5;XYC1Iv#r@V#4MGjhvgDsh|3usTh zl0?Zi?eHbSYllgkl0zI1v4VOhnl5s%EMkF*%(?h%Z)4UTsZ*L!r$F>otx!V znN~eCK!44(eExD#(bwbPe@bKNg(>j13#;8Ly9nm$+TR$Qk*v?WNlQJbd1U(lSz#0# zKh4v4Db>xOh-ui}TRC>d6X1!X(j<0Q?K8H?4iM)4n~4h1FeN~%X*F8l2h1Zi>xoN>|n{Z%!)l85zf(7QW4zHWv&C$7EJ zK4u!@Q}`oXfUt`36b7)J{}#VY9B<5^X0uSBNZap$$F#`V{8ZHd+vRRhBY@vh;v-x~ z6?kV$KNiulxYZ-UA15f>Tbe#63m4_!yg~JAwYtIfa(B{h&ko7C`mK<@-d1BO$vW+9 zI5HaHJ>>D;8x?dcUP=3~aTLIr%8Bfy=APwc&K7J_P&o=``MOAhq)YX%%m!mjms7XH;8k4|^` zTj?gLQu)6t4UcQ7Lvdcf-kN1<7UQ=m==(X-rF>ju8yg;WpS$ZvLo=(T)ru~yQ+*=0 zApBkRsdPhM%j#Yo>r#p{+lO>rQ8*2niXdZE9e`yPj!o)ZI;;UN1)aSBkCJ!A@gY>B z3|>PjZ^+e`r_INW`gGl{cv*u1o(n$#8#1-^8Gk(9YgY%tmGj)!uzY2r>;5+9oYb8f zy3@NpiSZZm+81>kQvV@@`E$>k(?Y{x?d$=NTxflch4?fSn6oI(S9BUH0%CEq?dP zI+_$JV9dVf9Qkz~WvCVTtD!q207|o=^M$60qF!(Eb>1Pz%Y1i9PLwob)hJ!By=M+} zAN&asZ?VV$&4+`j#k-cMO?ElnCJzEwmv*}4DX^5?n{39AR{fS~JePv0)<_+i{Ii9S zfX}HRVdUyTz1k(Z87>&&o~Ge3I@Z}4+Q8jRieINww&A|x(!o(kwMDkm7n^|d1;j7S zwXIhD4|v)7GI^@pP=y0((6x11?bMOM5p*AwD9U6J!t*p&RiP~W?QFB45f-EI%V?g5 zDV8MCOC)CUNhl=m!(D2aKI_CoX9sGXxtA_ z@Y$9CRaVLZxs7ju8@Tk$vhJ+y~CKz5XbV&#CaA6iFtzC7U`bam}nOTBW zJ#T0QbOcCO*Yr!IJ&_jPMC=(Fh`=IKMW$IPU*vq{W6>lQ-tr)ve`DK4YkvliR*sTy zWdzu+*X%)rAQ;y3-^&eXdv|GivR&{#B}HK^Ske#U!T#VCXL)f%_3~8T1d8FdM~d}< zdy5HBnGaBG;Ld)$-cFYhLx3n zRQk;{7v~byq&Y!__5;tQcoCvPB?&P^eYn{<^eN;=JNd>7&(=Vkm->pI`!GaUNEBc# z95dOAD=DZ!2;T*mFFQQSJyeMIqT+do^CY!04zk}&Y4kM@lkvo<<7Dh*Gxn?3#ufs& ztBH!kW#UDHv_Fat2p#@=u~>iDuU`(Ok>^|NMs*n-4<09w(lBt_}HSHR`=HI@)Lj$<$0+12ronK>gA#KrPCq zXL=@GH$yE$+aPlcKNewjvz-RJ$KmOFxtuq{iUxDN+qtFpyN2E0Z3np3pQxft($S`& zFTb`1+i%bq>NCzOt=&fkk|djmJBH@Au((!+FgeLw>;mEx5R~p$z&S6q#VLuGT6u|$ zZ=BEvo8{swZZWvaXhFS0)D0Xn)m!)db#K+By(4{xcTs*8s+04|fjH_toHYo~1{pl@8%B$Q)o?Syx+T)2ikc*8TO$9-2B6SQ}czeyvC059sQJT#-1ettB;6}@M?7MxSx37cJ*aeCQdtYfgm zpZ$F1>>|gPp8q`}NYi}qsq^!-r;AJ&&oxE^M~RJ%=4o#h3pAN`d21l9pm|GNsEEgM zVNAds_!SQrGag`~$GmgINdRqqhi$`D0ULG53;-Kuq%f-L6O@UTTPS6;ow;|e?Q5+W zr~jq>m^$-%1LF3b@Ma?^I7T#W`Oi&UpRP?HVN>XU4)ofW93!S@?Pl>cR&8_E8hL1q z^g+#tD(xx5o@C|r03n&gkKfN?d$kZVM-L_*zd#YoXP~1qp6Y2-Tn?o{%+Q24?(QRA zkAQmRxilRt>pu)cjf@%yiJP65Y1CDB=s0mZY}F0>)r&bKxyQV+G4YyZd(|&3?!${> zjH|n|_%MWn_her|dD=3M&o^VAj0|*&jg%1p{sx%F(}JS!8S~H=uc}kz6kS3o#n&`k zQd)jc9sIdykV1=7Z9#v>G9Jk#)d7+aDC(Bx$9Ld@bS`i)Y<_g+0G87hqP>&lIWCAR z_ep|(cJDMGrpWgGik*|r4767g4-zG5zA`&>aGdmUw9%=0A2iJ;OQ!5@MR)QRUje~L zFWl?78QGy?hpN= zTj0oY>$1QF`NvGlkugq zcSnKa4^NQSFIKz>HF7T#3LnmSpCtVOauU-z6}CulTd|_V9+u~}V(Dr-CaME|^l_)0 z8vOCDdMHu>>}uGy;xbmp!!13nXk}!xD7Zo9#k@gEhsRoUj0)?>PlMcS9Y{*b&W=sa z7_pU(TeDr4md2ImN**IO_=8{Yg#UtWO%3OfaSMF=hv zO1Z{v$#|`|s4AiI%3X_3fJfK6TFeC(OPW2S&$zvfpDdHA7=<04&xo}xNQ4Zko9pgo zbO623V*#6Hfei``v89Tr=dbHzMqesm29Ih7l$SZGn4gbiw$PGdSA`FMA-CVTk5TGA z5;j5s=9$KYEXqh?K9=Zc$M#X<+gGSE)gpJiu)X7P2OnOYKV^{#m6hvto6Qmkmo`@MjX9avhgc}!D*#ISKc7} z-iLj*C@>Eoz9#Lc57d3?z}~O>_|z=_rR!=QH8Ql`p#}f5Jt^;=BcS_q{k*PtbudWB z&?p%E85faB=B#vgXn8jUYc6O1?59!#L<ur4R&vsYz&s(L;@7b(+C+U9^hbydu^ z21BQg8!S|4mLY~uQDL7mfl=(pDm059XrdovN9WQgilEa<$Z!=nWRX%B|CXZ0Hd+`$ z@o<2UnqAZe4Br_60;QR|)&$^Q^jcoeSGb>$2p0zTky{I(stC+?7?m5~kDLJ6uGXwf zwB&oqfyr|jO1#{M5`7GfiC2&U;oYR!C$(>^C(SN-?~-}%qUi7YvB$+)&Hwd49$`lb zxPf*g55eTm&%3jAa7_29OtUUYG;OZPmholt8LDK{Zx5%}YUdxWUKMhMLw!(Ne8i** z;5E1^@QVt^JaKsu?_F%iq1WgWIa>c_0TNmDQ;SncnkZV!ZC5g%@qLZ2?-w|>72ld4 zNq(EEJY5$C^^GjFl!bD1)4j(NJO~4NG_3$dQz1D=@EVs(mONmP@O&lbq)<+$SSfn}k z^QdSfHsqN)Ua6A7)2FGrYO#6rM;O#=F<mI>1>B!;GU)2w$ z>zV;W{c4r>NNu@AATuN?34`i)E)r?^m$7L?B+RC4Ex z?{H(c^ks(55^C*m%BiFze9M(C$R`i_MyweunWr0(>J@u%aUq;FuU4m!tOmQQ?z~7} z&COzFlfT#l&POX*+C9g;G8Q=&P0$utDs8!ts>{grN?qkxyAd$v`L@GD{LWkn!-?tZ z2LY(VoTond1v`B3rWNVL8LE~G6hJ$W(03y{xQIt<58&D zcWygMt6$+Vo*`$c>m*e|_2N*0oiw2o%+OjI7kJLBpNCCEE9|_|5e#DtptvK%3zTZK zZl6`{gt-_#h_GV?_a;+mSrc|OaijcNH@|$A3$G5+mb@49ZmTeDPENOK{EW(-vo)VO z@*hER@}7FW2?S^BH~r#=^5-U9&l2nRuCO_CmoFuj%fA0i12o;artZ6Vcr_<~QGGhx z#JBfmdrKCh_*)D9Mpl>iaw{ee=Xjs0U56cXG-pI1l;JJoxN1C0#gOuHg0q?oqW~N4 z7;Gv$*-h>(O}gW^O#09U_N(7*uL_7LJ0bziuqyegHFJMt9WC4Ws{BbDg)kOFFc@hkxXZ=#po zfqk8ON;qCqJ*A-IJ)Z013CIIXhW|G^?48+OT%@0+5Liiqy^ny?Ws>TtX{2W7o@)^^ zC=c?`o}XJW#{0rZofp7V;2(Bn|A-s{g_mh`;s%jxz)F98u3<30< z5L^Ril|bbTY2&$>D0Xra*R3jK!D~o7os@AN?a&G7>NI;g6ZiZ59LuX8Wsh0D=-RsF zpCUe|@1Km6#hKz2lBGwNoix+yyT>uLX(0rwsg0X0G#mL>#^Op|#h|*9KfV*!BKhcO zp95BxXL767fvM2?BgG^8)D)-@ul2^+8iu*oT8AaFfUmmEDf1#ib*VbW%-0E1yxRlw zV%@Q^;;dAmEVZ>;*8bPW4^CQ2zwB6(>Bae-?JKN~!833w(O^}LkAf29n*kwamvsh7 z*pfuT)Mzk%_(Aib{cnx2Cjp@brCD05WCkbIr%JpTvwm9pH{2Gs0-AG;1xwkNz;saA zcGKGPt;tugzEH)Y0)$B#R40P3WMD1jR#g&!#8@fga+6z-sLRMfJgce@iav!7laV^@ z#c6~Q?`FUFpyec2Le=17lZZ&4DsNdj^f#OpdXVUWVkA>a3f_B41tZG(Ro1y*{=-0K zs9F{|U&x;!U%@bW4`*~u!0DJxy-a7viz@axb&HTdeosXG`yD(JoN-mM+Y?-?#~;vk zL6$_sSVHS8FSi9{{2tV~H0P9<7IaXnFQ`Yu&|X&AoJIuIK9MZ(oKgyEv zefLOABhHgfA)(XKOc%vUI_dGaS*>3EA^uP8z`!QDBRIY#ZT~FvFxR`*e8G4@Bnu(J z-v=G^#PVFX9p+jAPhup-tZ$~}8NK^o7N4yFS(pSTkMv#p?@Z#1AhQnRCx0JRPfA+j zZrZ7C&ew{3SgjIky)FKlD*f)|+sVRvv1wBNljEmNx$$(yY^+}4(a+)74>QYwF|5Xf z2Og-ocZMgL1Kp&`?`lQt$2}f@44~a;E1{?8-k|n~&q6sIHE_TO8FdFqp|!fYaX(8o z)G%g>Zu<+b0$NfA-B5WAO#L@s(dDAkC>~AvSG`w!;i|()R?i5uTyS?QXlR~T3urL0 zC>tm9`IR)!Yqi!Z^x}g$GM=Hshfk^>sn-J^2h30hg-2K%)keOmxokhP2|Nb|bn$9W z9;!9Qd?a;Q7mKxV5EQR=e+HDN@&tOfH}Ocz(t~CNbZF`|M;Eg&HJXUQZ(%p>4mm8} z4OzoW&3Qjek1Wf84g~{%ihp`M?m3Gl^h`tw5ulCQoF1pt^49L*t3Bp1%^8oQmFz#W z3lL|emm~j=r>_i)GFsOLK^l>6kOn~-29S{M9O-VQyE~;@hHem$?ha|`?g55II%eo^ z_ILI<|M@c)>wVUwlu$lmBnyH#}&c#?K-Uicah-~Jcz z@XjMYfP1`Er=K}ld>zY&CyD>^d`nNiR(k`jEOR-}KTba%X%NW!wpSr?HIk zHeFLbNWlqcHO8^mE?2l|=JnZ^i^!j=cJuI+`VNoT*@%I`ijVv0i03I<>|5j|xC|W? zj?w*aJQ@arlKpD0B*>?{7>`K^{Qex>*Mr~Z4803ek8%Pg45qX?i=hnqjU!2OtJ?3- zi!2C(MG=j4YmB}n%Q>fPGLEPJaM9KDUSmYqY#N1NgpxW%_4^bR^JVi*!zX$)aeZrP z*N=-tRd}YvRwk6|8WokFcb%f|cqb`ko>xmQ}glkWP+p2y{liXElgAkIfD&7&T z)BkC5N{=4LOR4b31^c&~^J5Vu2LqS7U$is3-xlT2bosMLdsbLC#RFzDXc&0AaiGx1 z7JIYVA%ZA0{cRl1jH{J1EP*O^y3Lsu;y<}G_ek#b$IjyR7=Z|JyP;{;hETriaPJ4= zz@5(l>aTn{$^W-;AhI(&U#DDQPi!R{p5(oqM9;18;pH*9Fto+2Y9a3UWP#AHsPbg> zu5|_qB#1sR;Pj0mxRGzgF&3SBBr1*SAf+XVYfwovuYKV+vrAAUBBOTOXft3 zXN-BE_8Jv59eI=e1peXf$m2-DkO?j$5PK6XR1KL&UU(EoCb`6J)72Ty) zvebmU-Lm2t+K2@zbpw?SeYqRjShii&HnH;-nQ$5He>?k8e$U%c<+7(9%+XO@Nhr^B zR>@Js4{_gG<)F3It%7l_$GJ_F8hN7@ z2^WqK0G$~irbC)-i)=qAJsXO3&!z&csmP_ytCvMQiNOqb=ZF}EUOzXtArfFim7#sg zARC|s?wNNQ2bbPUI`@>Hx~9Xh@>I%}T@lRS=ub&(VIzqLY-@1J{6~u6{4+MyV-gXY ztaXIK^cq89E}&i1qRt=Bp_5lkr9AQd+5VEPxd7YoWhjGppIGJai9J`KB)zzO%I!py z@t<)=ZnH@2qSoI)6CJD>HHbQg3^rO!D! zi%EE|ha_t-gJBw$$sb&0a{LhkZFE%pr;S7wsq<}})jadcM&LxpVC*5A% z^@Mz>rB=AOqk2u%^sA1#QQ6MTtqDRgDA_TyB3YQ}uJkXJv3YN$a70u-@nO`!njFo= z*U$mqJt=Gt@#xM~l;6A&+|x1SRJpTXWZ{}MdxGxfwBgiHkNu^y>KFi(Qw^du%2!7H z4Ew!gp?wnrH(CRYZTc?a2WORVDPmC!fFa0k`+08+Zr;E#L3|2c;xu}z4|n{M`>JGA zjA?4aX+pBXkl0~bjLjPXCb1NGV_`&IUN0h+cL*_6ewMm{TVfA)j*s`o*Kd9PXWyWJ zU7JOKGYTBlz3z6#el+*{NR=o_bj4tlT;abUuPj$QiUn8!H<1@l(2C$-Xm9xqu zC&FC_^^fgX9&V;^?HpU!gwgud+k}Znlgpbeif|Z9>9vE=+n7uAjQ6{gci}RgHI>635FbDQhMeH2G(tU0a61-AI0ao5GutSUx?IPYXtoq$K z@F6#u=?&Y&$}dIR&pcb2qrTZ&i=DVX_TSYpcGAqojGH|@)C~FvUtIo~L4c+1oIf9l z6&z@Ei={N-h`d;gQOr0V%%{BHY>r|w++obnwcm56@{-$#EFBi51pk6DI0&pw*r!sU zcl8XM%c>RQf@*`=*4is*478XlAU9r-}1AKhnBt=dz(BiPV<#fYKH zu#3dvtYY|WDprxqE1of&_)+A|Q}!92bI6&eT2uR18iupeNoDf9Q6$QY10VC|g$*-j zmVv@{Celt@{1DaA7%u}e9tgd+*q!LDZ?-Alng~X(<75tj=r{a^s!i$P4Rgm+D!_Fo z07&*}nN)C`JWY|TO?R&%t%KdKI$Kxii1z!J#6(gwT@2SO(!mVG7%fKI&G{dUlbY5> zCGiffr*Sf0_{bfbC?kUYd$}wjzH059lPliq^B+7-z$Pt2IKP_{6ewoTE($^^`}KjJMZBNo`TX`-^vCxZG&y;%@r=k> zp5lI%`vI_*b3Jjk;PLD2PGC$;2f*|394JdufO}uY)pbr6t%|KgC(V+HBf10zrM?Z> zc<{*ZnCp^f#ZIrZnmwaK*hFiq=Bd2Tm#gc#thHpfTQW0eYGq0@Q3nBxW=pj&t$v{~ zFqAF|q~%)0Y3lQucLr~ZLC(9>9}d1L+4=sf)B|aDsGIi;3X!d62DY4~e8^Vx!F{NF zzJQX7J->R>P{DK{-w^zwFFl_x^Ik?3^Io50ZI5I}36?*Xkx?O==@1WiXTQ1a$Z*2bbL)@RI=|QInA@|Ok2%Lj5dhRoSx3Ux z_zQ6M#^Dl_a$myKRGmNeuRI%r*!JVXMqGsK_T99H=fpWsmPf3I13b%!Cxj;40o4zb^R zT_;*{zRsYK?z~I64$mf2l!FPJC}JFXa7%3##pf)wHu@FF`PuOV5&&Oml8x7+ywpti zQw;f22?b-Q5r}6agVGmnEo*st;mh(ZM{Xs!6RH6D3x*f&z$qqZnO643} zL}KrH1C{FrwYq*3@vl`U=2?WNdTo-_3J_oPcVki`f@ARTrUxLAg{V2w+i~bo=ML8l z?`bURHh**SVI;=BUbCUzzRtxK!KDPUcmdCe?PUxOlQm;eu4BBmGkZ5#PUwGHTiP|g zueW;b%5h~Fsy?dLkL1hXm;8cT_&**xi%Us$*Y8@6^g~3`Q5BfT; zSOW+STp3(N*(_vX%)ZsE1jjw2jKV+rns&@7`_jzI=FQfMt759SB}J>$++pFX4A4W3 z6|N=7GX)g(FCt8GELH=-sU>7cFm6&q4IaOg+c`|w%(-(C>VL7$NOsKSL(n~I)!k|2 zy?EUIS@@02gdDx7_GI%EpYk)m<=5>QQgq5Y<-;r6ad)gjmZQ{0gyp8v_wPNQU>q>k zgvZhUyk{u{F4VdV$)^_ z1+Zzz?5&C=le0D?HH?NZf;0Dew7aJie`y&1?X4us`T~^C;hVLu`h!PqYm>r4x=u$3 zXU5t#Uuv19$561HkiRPPak3&2KV|2z*5l^C>M?u3B|ZwvZA%r;(NVwY*1)NN;& zY5uUvBf{=i6qOY^M1ajJ5pFBtSqOlL;BKTVE26}B`kwf<@I`ZJFle+kF+zxL5!`}r zTF#1>i{csXdPy?Wlf)&>rCV zWmVgq1*dFW>+T-?|>-b>TzIdl-D0vQi8RM6q;u=&j&^S*q?nLuTLnW#t z8EX(SJ)*%T8LQ-V`bf8ekg7<=IsUnqc3s&sA&3oKb0g03Y3O){hf4h0S6ij_koQR+ zWthUt_~+#J9ay{QqXjDS(yyI_M3HiT-MA}N|hXnU&pOe0!! z@|q?=sd-v)81E{Q=60E}X+%WTeoy&0qu+@$YWnxgxR<6%kV|sB48$RAaGd(0prZ~*<~1m+d$ z`v)TYeJd074VG?CA#2B7NBli@#sR$7;tTXQU;KLJN+cTW^XOFSh!Z>mn8Wd?B#e|W z%4VHy$3U(yPD?Ak$u{%Z<2=$~LuB^nkt(gBT_ff69V%sgw#`y~@Hna}ZkuPqF9|+@ zvC7L5i)tGDc>H~EYi~w(WAKdKTWKoqS{g9uF{;e zJK%n8(iX!iXi)IY4Og8iO{8Y5FHotUxL26n2}lBaY|ps;In7Dms*PP51R!ia=fAuX z!N*RX7-%!?0QCJtqmiKhdT%>UO82?|Wqlo4@(YIFH`}0PN|UN9ePj2HJTiLU7C@tR z%~%HCy0+}m`b??}_D7g<`m>Axvozs=;iB9oZC1!9^jgh^&3YvAQ>8}6|N$wev@-1+Y7xA>U2*a6Y+O;8? z4I;o%EC?G8&UF6iu(Z*$kk(?~AJoC_mVD?s%z{mi3m+Zd7roH@^=a!09ZG+(>UIM4 z=_GLBGPav?rWmixpf=xE8~vxk2J~v7wtxMd{P0PnmT_V(}44K+ub&1|SY=Hrd76VhI^i$VrF#y-lr z{s!HXf&vYqNy1Xr>hZi+9CA?el^TXCjA5hc-ohwL6*}5D-D8b+-R`8Il7Kn_N6q=~ zJa|*-d}6hIJO=C!_|T(FFl(F@zBdG&HO*`2Vxyl8`$DpiCH<;dERe zE|=aLUeTx#9rjN;WX=gJ`<=EK9m*`}9q`sLXQd?`cvA zl;uWpPF`1+T>j=sPd#MmJfi-!_^VX7n8rmjZq(>dLG_rl4sMY+7|(Lq(U|m=_|o)Y zFRM<51D1>t%~jgx|MsfauuP7NlYRD5g;gX>^@}P0wbUD2qdj~CoV}1Yn0z~EtdyJo zepz)MPb#g#`hE67eQ=5>13$!m|M6xs3vJ?w=w;pt<)}5nJdl_%+B0Xzl$8LZl21hg zq$y%H6dS)A5t!s~@v-=gyzs|Mhcw{FU-#~tZsz7=vEO$~3B`4OvCV%IhQY^o?y7&; zPePV;`#8BTj&#QEg*!vBxggH#ir95~)7h~#ez8gAz+u^LlmDG1xB-Ftjwz9N5uTN<|{`|=Z?vE_`hi6WUm-pj`0N|&76IXHl=ZOwR z(V4G*ct!&quVs(%#}~cW40%aX8ScKUbz77>IITxDP!Ly{+Wc0xi1hqIPy^KW%fn~% z&uAnbm~`XR^U}P(L{Aue{uzPnsL*l_6ydH3XMGtXL77DN(pwV2^Nk7~P84z(LB(KU zzIV88QQm5RHI-wK*#!^sN);W~$C|X=(RK{?ExuRxy|f>Zmmu28>tTLus_8>t-n`aa zI^9H0-9%Dt zU|W!|S}$Y5t0q?X1n;bD68TFkQ`L5|%~#+@qfQU!A~$Vchyx*R47r6`;W+4b-9X%u z8e?TF3nYoZo_s^3SR#J%tZPEJ>22LUzP0_ja$oG*r4nzzav4O1F2$3v_dPx|&ZnkV z&>gK`lFGS1gLqflP+k9Mu*m8O5ur>t(Rr*hBju1vBhpNGH`pdlg?q|=Kh>byiKmBQ z>2Sw`Hrr*&Ro_c_o>}nF17$}{kM~|>I3C|v^!UCF*aEoW51g$M%&Ay%z&JDjpaF}c|yhwVCRaU8bfar@P;)BL`J zvn^f)QA_zFQ>J4rGUBUIf8V-HA5JVYUBr;e$8r%?u;9&(4zhU=kFh-Vn%OGc?2z7= zPj+)~1G1|z{I9yk-zSf-TYzY#kosjWXom{LkBCVc{t^>F=5H-{06RtD7V{-VU9IWr zL;_GG<&EKEGVElU5>CrI8F5;5m+iLp5guv89vQAYZ_{%2K;dQ0^zy$;yYu3A0^ggChs&qYFc7)7?XxgB9Q$5IR_9lo1t#K? zLMrrqLj9aIU(Up^u4P*^aQqCMI1%3cYfXi_2MrvgG5i$_Guj*OKdiN;PT+&~Ur zl)K!NR$Ha@f)XXt&sSP2s|>P+7wrO_8_>*;pfrKs!{A36uC|H zc3d&E9c~nN=a&Dg1^D6&8M3Ml_9Y%g#r{(ms%nS9MkeeKEubd#tI@CK!B=!O?Z3c@ zpU{PG7_|Z|xSQ}9gz+cB{D8LVlafHE#nVmN^PD1}A?r&>O(%ch5K~PGsOJmh0fgCJH;Rby)Rk zX`eU!!Q7>c!ihZR2A3j_*3=Ds+v}d}_T(mMP(&8OJu@xEy3pZH@uB%y)T`R+Y>tiI zXF)W)JAumQI@bdW7}}MYNst#P8EATFIz)C*)(v(IAuK9?-UlO`zi_8Rf>qJr$XYO^ z*xgHdZr}vqIXgxUYN+h5a5n$&$$C*h-c-&gNb&Gg6~2U}jT)6gbHeC9;JkO^8We_T z8mx6^p4?m8#edgjglSg$^_aDfHxYQ6&!R9+}IL0c(>dI zT^FKK+~7?CDsRPGZ3f5ZkgJbV8v))H^=Y)|z>UD3?LgDyRzy>2u~Z#6Q`d%TMpeED zQa;ka5b2p!JJ-mPA=ayPS|PlY88|tMm163~Us}3N-f_jiY*&5q9gQt7({qPzm}twn z9sA06&MlA<>ZE4Sf(KA$jrtb>EYB3MW%d6Ddl=a+6LbA|`=U^Ls?izHB6h zQagTX>5dNtd5^BI{}Q@iUjkoW*UUu!htd<>hs_L}WL=`dmGg{GS*%Zqeh%hZB7rwV2B&nJbeAKQjY93KX_3yR4VP* z4Y%3fHhYVx~;Nk!c2h z_7nkikm+T|3^tLt7NGEiz|OQn6`gUAsCQdS12J*}nWAj35$BtZsQ3g*mEmeHCb`)2R>JIveQw| ze?Q;#u%Ob|uZ|;xFAl?>XHFazX-v8ff_F}TvnUR{M}NY`Hmag6B*#q6=hcQ`ziM! zvSe0C&t0D^5H~k&nR^M&*TT!e7BVy`!7Crn(~@ZWDQ0*UK9=pGgd#Ll&1?hUezion z@L=dS85QPPoRbPeCMv7sv0zz!O$KyqB@46MT&qMiisKVB@T}))5bXT$P3!2tkL1ze z30S(~PDAw(^xzC0tlNs^!oL&_SR+FkBXJqAz5ec+q>PfNJHa%yd4r?u;{p*=HC|<$ z;c$j0QAbiepC04i!4anC_r64uSpmOl&-#V=2~9AweUgtP@XmY>-wlz z{Q>GxYf}b6#K$g+Xtb$Qm43%xDOES(e4ghB2}i!fz-Rh?mOU>tdZ97VmE3V^t%Pz& zh3)h@+q#Xl5d2*uj2QM60UkH8k?FY*tX&TK!{eP~1QGB(f*rd^2Tkn;A9#44W)O&` z_@p!b@Ty0M;`;SWx$=zkwXHZeg@7ot3{OfjU$=;ODN&?2cV#?*1>;$JFT^x^?9dJ+ zr?s4%W-*amso6!I65$-4#v+zfo#6yT@O80wv>RTU?|IU;s_)PH}AA;J+c(rKxuTr~oVs+p@ zy_XL7Q?|aA_xSR0H=b()TzcMBJUxXk^T$+_IAYY?MKwq_jt1TEBMp<2?pD1IqKE=5 z!*;g+lv@?6AoG2T7HL2Ih0)Rvh@vm8`cLAe$MR2)mm7_B-z;_Ey&Rea%?kY!7>`c- z;7kI%>qi31iLG!BDOb~3;JWFKY=lIaDyOoo@&fu)rh9wQu?O+4$mU-O z%b+CMsE7#3=YBI=(AcQa1+-uez!L#i^sKs`@PUxyh8Pmqv`A?&)Go8^9;WAgxe{hw z$sWc9v4v5pHjfO(m?`}~uG>jVO*z1knrB=*6j z8VetGvEl|ScPxAc9ySx%Dm7n#P%{NLFrG|rf3r@tJ0-_Q^8TY-PS+8I=xQ6ms{@1^ zJ|J3O5`>6Wr)(*s4|+8b?`;+YRA?{V$x=De%#;imrlstPP*BUwp-xE&v5kBQ=c|>=nyz1X!f9>_K&y{7PoqP`bh&B!+WaZRUs#+ zYHdT>Ox!B5zUQnoZG}h(mpck4Q+1f>_UK+7qsy(+FweUhpvAM)H$3r3V|o6rvVL_; zQnW6tdK}cM!M#ebxn>BAi`u46Ui5)mO^zhHADi8Eis2peQnr ze@SdByGt!+V#F)BqhmbAPcHOzhZoEc2sZL_IR(Ce&h))R8S>~I0|PWm65NVtB~+qx z$qldkX9dxL*R;(zD0^1ypbPnqOx4{30U18?`0qYIC^@Jp`C_NbMp6^kqHJAt8}@3j zBw}`-+`+kb9pbAfwd`cL|4pE?nAYNo9bce?ViS$C{7gg3F${(v^L{9`ZNQr;*tXJb zPa5yY%D?&`aN*W%l>C!mCT+lT*J0j-m*KiehuInS2L}zW$qbb+g4h_EUFw`psW->X z{AfjD?&imXsZ4wop27itaeB48tuU{9nNT9pw}Qnu0(LQt{T@TXSJ#`5S3t&h#$&@+8isFlEOo>4KjgY4WCXA$$Q| zT(K_s2i!kiqsp#z_~)=%3VE}b${6j1Geh+2hnj**YT&RPYYP9+{N=h8RR{jy`q67C z54;>{7I|2&G|qmeUyNo(5hYkREsF&{(V7&Jq?q6mI1-76=@?FF*X?ZCGyCUz@arcq?4COC zRZ;9Y>C@tW4+?$=H4zG4;IF>Qi9zFw{+fiL7pr;gV9gxyR^{`l73FAum-7IXve~+! zL{nfVtLYtT9bgQO*&~`y22M_S%J*CMwnn=8|A{-~?r*5@ed9N>K`k*wjS^g`#bF?s z2UfJ=cE@>hWz?Cl5hN3~X>-<<$}pz}_8ojF!QbBGy-SoB4VNaDJkx2^xR??-U{^|< zQ&|D{Q7`mMnzB(UgqpzTWIO$&!ut(!PHAqIvaV&79k&<0S&VwGYr5@ZY)f+s7$I)w zJ>tc(=XjON$F2tM0n0a)A@F7x-Rz|NJ`BT&rq>Yi59|*#9?~#(LIb?%Kh&@8Lr_0A z*4y4;Qn&!A*O)}(wcK^`RaO!g7rtAHXR8>wnD&+3_#ZG!W z4Pf3u+g&YIFd_n}Yuc~bnYTy%W3C{KO}BT8 zkyl#UDtnDQI9l$ZeQT=$kAc@qPdh~`UjG#@4qobGb>F6DbHC#Ng5}}Ktb zZ`}+Fh+C^0WS>W6yrFMi^jIaYf+Z#3xxDkj_al^-zHIwY?7dTAZ$x&yi3H+BQ-$u~ z>+QrmNj~$%v2~*4URT3EUTWSvv25+!`j85HC>hgHm8M!++L~y&#Kcl1lw<&;9 zsejflLCc9FG(uwnm60mrFzRi;B9b|FXfF+M1oPG>5DxVCJUvNH#>@0O{gZFTxFyA! zXHF1%_20$!nIYv{D6xkho+1*=oT*?ITg$1`Fw7mrYERKoJvopxEk}aAWU_p>1i6b6l7cQm5tdpKS> zUMBYOwb&t-CfRc%#hB9E*O386MLq00cA*sEfkREV2N$B8Zh$9O>})3mB+0{*u6zy) zbRPrZ5o=pGG)&?;C4zB5i&qz1$-`hKaef*4LTSh7Pqr2oIq%ADUd+(A!RGYd@r}TS zSRIJF^xsVQrN4i3;MOV;+$cR8m-_Wrd;Rg)2TD8~d-2xh2fgqc)NVMzb92(__g_#p zfiU1J>>mH=g}uyS_6M?z4*Hm|QD-Qy8`}fbXDZC|^d3|%yHHL}!bG$4!rkzIU@{{k zkC!XiodBBfTEOAeG-w8aCKyo3a{}$p&>LsHs^jcXuKI=6Q8@h1beh!hI?!_w8@frz ziMiW$Nign_KmSz>4mg(0JsC>`N=dD$luSL691SMcpmdn55l^(g8OR;Txe+H-#KFKD zHY`%(aPH0`Fd=7iXORx=Z4D0w;|;HtjDvCn;c02n^mbr~P|P24mUds-ufTRWDbRe) z3_}^obf?n2`=cW-LNr=Tu)Pb4bCfLI6H(Bcz>CM&z#}@nQdChy0TKk%7voC+JV@hp zGVxlqyAuDLV=C3aONpapLpKMnn&g0cR{By*PL;-0A%hFsuh!@fN4}E^(iGXda;|L^ zG=^`$wc>!({)Wk1?))*-_KSJOk3)EKP$O=`X^5kcb5j}z+mxI7Q+#sU${2-=kEBaV zI}L+|0$MKF#maWl=<&-JZ>(z(#EA7dcpg$7Nr}_PT!$s4@>1cG9ekIgHt7S;xlL_b z6<RrNP=uxup;1(ANdJL(tZf;v~Jp2L8 zp}h}xre3h4Jpk1I<)l%kQp4(M@eI5e^)dbx{s>xcadfAgNC+*W+di+Af^9*+(WME$%yY zCmBsHW0aC-A(uyZa%C%z-6Jb`xC?Ai)|x{UjC1?ts4-gYU%gt`;ECFa7bHCte{$G3 z;=)!^OUGQR)*I{e)?zN$?{oj@kpuN5X}5FH8LSm3hFK0w=sQW+*fA(`>zjP>MilJ| z20h``KZ)GtW1CnCQ624l9>M-oSThz}I%#FY12_S^tT(+(Z)N{;`4HrtLv_bDdJwI|oT^8Y-yDq#w&8+x;b|z! zf`X?amYcA{-#?^yEn4=xJ4p4?tMRK97OWbN1-kG^p;u-6rP8Vva!!sH$Mdvj zt0kxTd0_$n)2Z1m*_NluzplKTsVnP$`+7bDxP=+S%7*=R@N%3#;Gt}l64E$p0<4Ai z<}B5pH>hXu{`a^n!dk+5Hnz^~)Cr6#0S7>(SjBJ4mG=$ZWZ8Ja3o=Zix-|+UYq-3= z4BPWJG`tX7wHcs-utyhh8A^g-!k2&8vu9g1R_ug7LOj?+&^DYqo~v& zoP5yw_VPmUTjL_}L|iJkh1DW|8#gxVd~VW=)7qO?Q_CJ}r$>~bAR~ET4uf%HCo`0W z_6f76;xfGEZ=uLV-QWy@zy!_ok$V+!tRcih~M``&?4E&J@{noRh_23lT zQ``^C@~~cQaQpfnYK_CsdO@VQ#uWaeR;5bnK%aXVakYSF&D^QMS?tUg?VsmL8dd+K zPoF>uT!cG`c&vC(oD+gPX{eVSHqINpf2#<~?Mec+jf6z!#1iFwHNF`8R12N5g~PRO zm9|p1qyYCBUU%d9VUQ%J*LRFx|94gMKyo%hC?77V!9#cJ`g8%EspJ z@7pbEa#(N`bOPr||L@5V&_dQH1`tAJTgo}Sl;e1S_~8EhKFaa6`TRkMedE7dEd-&w zzlSkoy-?>4z2UWh81@qDuZdxGhR{QLENDtSL@?;|Z7q_-!XSjD?fKvON9dwXg)gx? zdggw4M~?5VW9mG)0wCL{gc&Dl_v7xh$#Yg zKyL=}~X8!g^FF>GrG{;_`j zMb>k-dR+43b7#x+U=N(jZKvINS1w5^x1Xc7l>fPe%AlwEOCDa%+PoTl(p#zy)C#Tn z)D$;^s-!`Z;si6Ja$k%uL-S@D=cLfGb=Re|jwVCsTPKw>USD~nA1LHsyn&OQ$0|w_ zO!xmu)$}5)z6Vs%^Hl43*JR5xFY)t)J-+NO2Fm|AL#!1c&>*?v<54MhVrSx%Tz+wf zzT&0aFRZ?FzYz|PQk7@7004~q77RhF{h;hPE-CG7uS8v8UMiq!QvU`K++HKi z!tz$S=Dv8-&5@HHzk-huI9ZMph_JMA?`iF3DzQA?5C(t@S`GRBVXbzzQVKW12;l^_ zwTFGtnlrv)6?@DGgn%EfwB!DJOb~kXVak|KNalA}9x;`F#lg_R<)ar(xM92W$5|F3 zyP&U?u+e*Ch5bdhgk^j#m6FDih9Gq14m^SLA0L)hOpoOxR~@)C2o(EZ>HmyaTvtkThc-wqTd zh-G^q3^-US@?L!FDsa;QzE^d^QzwFl<3w|1;rNdWpPHCA({jXytO{q9XTalj@^~6P z7SU^AHKp%{(oaV+^%};}=VsT|ar5H@ovu2JhZq@jt2wv4?nzEi=Uw$==gBJIBeCY~ z=i+H?bp-apjK$jFlvhd!TzRm?fiCv&LVSV~aMBWRUwLx%SM~eWB4a7&pK+GzZcDkS zI*Em#v6k5!%xVO*D*6I|s`Y zDKD*b3@i<`RB@MOjCL*C4#vvr52Dgj@LW-JS4xgX=~($^f7NnXsjksra+vb|#-ULd zkVSJ6v93x+rI%Wh?wM$88{HB`9Soj3mj?8!BzYIODmYgi!b+0eX~@g~IQe8<@wV zoKyF!j}|ncgGHq%8xzLJp%#v3DZ#;RNFT5m#O0eLx}RI3`LKIA!q{xPY|W=|Wxk2^ zqa`)Sb*YF;(75VM870JW`aRDKw2hEAWPImnlcompIYctBRC}I}@ zQKg%gM&IB0jhYl5^_3~ad__ygc)NqJzK+H9TWUxure+#&G5||pt{0l*@S*jWb3C*3 z4yffvuT8gcXT`aeA}0z8?hx!u;pwTu^czWB0YMEl-q$^xSA?eq>OMS?3-kdRM1hO+ z*z<=c)#q;L!%I(=y=nBMebh+$FsJVtmVB;z)L9vEY6`y$E}YKkNI8}i<FQTAK>i2 zZr)&N9BOP=7q|l;y5zce#7DH|iyJmA?AKO_u6WyqSt9+aX$*|_2b`;G0nO=f8=(xO zl;>bqJyNn5#VvK@(mhVJ7aj$Ago~G+N@}KHEcvJf7rB$h&4+O;Wi`mZOtBbCDRl_!PLi_L1|CQL+k-TD~eZ%v4 z$ldUIulu}dvB6knQg9UgI~Qo0Upd;W=U3GaM}j6bWaD4_HKp?8Tnf)7;Y3dw%nGNG zrQcH+xqdEdMK|e5eJbI~CmP5MOEn9(QFnp2weNW^wj1wfFY!fC4}n}yVd7Wyc}$o8 z1Wo>IPBkn(H}4x=lbJug_PiR(XJ`4iR@=FG8(Vo*eb|zmykVh-pgwvleLHl^( z?g|`E(1KSJ8&)}_Iu+Y~omTln)WxJ%egKvho0r=bO`kZop$FkN^wB^(P@{`IPh`-W z__i{=Kd}P(jPGzq51hWx+GKnp(#4*1FNK!@P1!i_tG^qK#Fb=kl+y#zy)~h9A_+pP z7@~>z;K?xOuk`E$q!t2^p5JVgV(1i1If&gNs`3a}wV<^w^ykraANZx;E%$T15}noKz7UeQw3H)h<-Hnu?y|$fCmCbxYF>ZimkIk|gm{f_ zve>=X4P%1WKn+zUzCI#Im?xj&gIlgg{(3+PF}3a8k^xo{JR2rF2ynA~^mL4BKKLU9 z#l~-TNYmOHRcCLV@!dwSc)c$o#M{^TmufV$&V{hLnX@=I9G+7qc`P7}CzTwQ={!Te zkQ(y|P6oSa5_%CDq;ZvND1!qu(_atyj4OOko0g1xPj7A81B5Se{J)+W>-G7gtP?tZ zT(WZ7!>_8{jAcb;D?UhR#&$CpAfJ(BXS0eO7596WdTj*_{k}t{?olbhBhHLeNMlHd zvfqi|2-}8-@px0G3ko2FTiGI=VV@~1ixm@8^bZ(b@{K)na_`40<`@>{ebMB)9aNy1 ziy0;~TI>IN)Gkd~ohNI*Nuo_=GbVA(7=XXi@yX5JWzyY>dDDbkt*Kzl$N^h`qf=0wVSU(3nPqfX@Ws;APZQ@m?moZqTau&ifFj z-*rzfKv*?U`5hkep5(E(}xW_D&&Xc{-Z+^I>1 z@iGy$?0yW^@jC2yzwhXivUCPNFaJ3-&w3S15Y%?5NiQT37U9L{EoT>&E$2#9KT4F} z)1Pq<-p~)6_pqRHd}-EcEz=VT;do36l(Uu00GT(i@9e(NiU#SV_z%&KlWlrGWlziR zgRY4%1uDh5KL{_3S+I^z*@rMtJ(gDwNva}|xJHeL1tJ2jO9KkHAkN_n%e&{g29#X7 zzM`rXj#Jt$FJ?nJK0c$JdR7YTZf$DFdaiH35O*>ROtGPk#+FPE`!;gMhu|px)H9a8N&}%;7ldEUg*7I#Rg4_Wku?h;bq&tt(ZK z-7n(z9;)08LTFF$i{76FTw+Gnc!%-8s7*18WUiKPwzoMvFv(}KK+Va-1k(N4xglJ#bI1sf*xs)(JE@k>?Q#mp z%&3KTE`%jVOELTYW7K#eN(HH)rA-i;m;pP1{JQLFF9_b8PEp+YCgjIj0X$ZrD*uV; z0>y}&0oH)v6Hv(m*0CeRDCncQS4^FdbR6r)wZVV z?x6`7h}T`zF9r{Cn#AASrE-Z*rT;}{bkWGa>m3t~#EiDI*jnza3I8XUD;OM0r03E& zr?gAByvsL|Y1QQ|c~EKuY&n-_?RJ{dlzVE5|0zn*zQ`L&q@l^BdQbk$b-#kZ_Umyo ze+pyC`$`NS91SkfT-(BSqETPyxg$9M#Y@FceT(yOu)2w=rGhQtB)2YPR1=&jY-

    jo|cDkI|#lCS%UW>kPdn(ILo+}Rc_4I6P-L}Tq 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�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%0Aa&Io1@(a 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 diff --git a/taskchampion/docs/assets/cgi/logo/logo_256.png b/taskchampion/docs/assets/cgi/logo/logo_256.png deleted file mode 100755 index a01735a0d28ad29f2847887819497ca515b7e449..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68818 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

    UVi8leNyWpE`}8y9>N z97Z^*P-esIn0y~QfH8ecMLTzTFChX1WsOqaYY^*?sGQ6ljX37$J;z(G{T|}6mWQS< zaA)P+lwIQeGF7vz?RzHzL&aZzq*pYbe(SA&!<#q0!9*$Eu`9HYNG1L5k3=JIPWp{% zL`@;vo=Z|VOiQm9NZf#-_XO;Yz7j6PjGOM9NP$CNSIpInk6r#8HB*1Mhh9EdioX=( zkO@dxd7$I6I%1HD)5+$0x2mMm#{v)Ft0&iPG zCGbfp99U<-Dy;A_x)?-K1jva(7*1-1===quSin#uh(tgd&pTM-#SZNxRyvFUJ%i@r zF{^js%~yXPceUZ-^fTO9-R^d0=Pa(aOvG%EQ{Y|~cdq#J$-TVdZ|xfX`L}+PshIHX z`b~_3N=fEgG0_4|RHAycOsB1hJAL$t8qBL0c=c^;7A*+jLXeg1CRv7kz#Tcxt*1OR zzr=I17rog}&mI`XA5h%v1_y&g#1?_|PsC{$!KaicoRScnS_Igb_goX1feZt(slpxe)U zAgt)mY?I#WOH`8P@`{tU;jP#IDLm3}aq&g&G`G7TB7kM>f+N$tXBR3d{wz(W`1^mq z^INRf*J-CKPB2u$2l`B;M7iX2AA)FJz%MqA6muV1{BQW#(MXX%^RDS276a&d30vk{kn5H5nv2g z0~fF%0;G`2Eb-I0z4Didj^=yT!!v-ID;J;VPXsJamb|A=FwS}XwzMcDRk~18;#HWO zbS~4b#|0f7h>RR^1JODl@x;uUm{TB_#spN#(Of#__-%OmoqvHfEtd{|j3e&Aabg{) z84v+DeNKVW(Oxa9p!;vQv3iR?f9(&MiV5El?|61!DQYc|mGtj89tj_O{^LO%{#``y znUiBEbb6MdX)Wt~Ab4T^B6voX0La#l!j$Dy5m}nGf|<^AD}!FNLV? z0LGc$RNhaYqrkYP-nMrMrxO805)GKH%bcp2k?&(=P60A#6Qz{QCg(U@T*aKch1P<$ zb+pE@YAv<)+IHF5NBzuqyL~#F*~|**5#G=%2qWaqLeV-Ptiu@*c1YPJ7E#U<5M4>y zOUE3&4ep&k#+o&kFZ?t|=1!*&G+3YZ8U|+&oQ4Q&X84VYzZH#b_{U%W3d$MYT)c~I zElMh;N-`CaDx^ZU$WEi`0`kYBsbFXd94JYH_P^6hr3pV`L?sqyZLp+xZ!Fgc<~J*5 zlR3{n{1ZsL;_qN`$U(jAj`sR6D*iUW4L*SJjlBCFx#)1cBe121PbmVNnv`}v3EL1@ zjeLi42gR0q1Oy`<()45O@25 z^(8Lw(u`RjOceDKQh~5umKF$fa3-pxL|!`Ph!fs>|Ic{qoqt4&;dph#_2cVYU%$`I z^-Ye<9o9CUXV4#5vI|nyKPvugQ2aGCeE02daP9rCqie-m?FtqA{p;ZGA6b8u%)y|3 z8~yf!-UC0*-29a50Ype-XYo1y!0Sm+{#4)jb;&|3dFtWMFtsykRkP?k{$@Y4IsWbi z238T++{p_p>1jr;_T1Wc*BQNwGMB{x9S(WkJJ`knY-@23Q9w{3@Vld?;@KyD5+`bw z$LEnk;H+cq94AdnD=g0S$9@akusbQARcJf1``|eN8ASn^xeg~LLI7z(qU?kS#E8VI zlBh@oZhnI|-u-`Qh2dy*hwI1hbAA0jH`doVHb0$UY-rr}?H`zuUk&C8V!^;JrS z!%swTqxN2oP71I5Bq&|2H$Qpb3ej;?lHlQ&%KSMoZRxc9UP*PalUfK<}@5~N-P3LX4*oX1vqs$uqurrvLm}19Pt+1 z1(EW*KmsHJMTL@U4)jBO?D4b51S1gDv?*2C$Q)77_k#A@>?P1w<==7Xvh!8bysn^O1q0@#>fxs~fz3{2uSG-{;8O=ER{Y-V>xacPK>2>#wY2BL?GF@c4cQS=>)=~ z{g+IkC=(*!0{gEVUWXxlh9V^d^5K?~`VKd4zRI;XzQRhJaI!w(#_9$)R_}9teS;Ho zw3P^yj`r#lf2{du-~0*+!`suhY4GfKT`6W-P~+n!l*7AGCj;{wfcFG&8|JI>jy#b6 zLzbmA7N;yY7nli$u`s_`aiErb;zIB7*CBsU{5_DnKa@T9_J#Caj6Gj6*%x5%=3VM; z7$ME`7M&3`?L~c=_!*ze<)1ASOLok^3_QRDK}E%cnh8%_{4_5-^B-|+)?8g(W}+2q zYdJBN+l?mztBeTP5X2H${1`E~ZB|M|pd%1bFOca36!?^6R)q9V5-o#nzzXz5!_njp zH;>-njW>RS6OK4uA9G`Mo%fF4^_)O^hZE}~2~#2vS^iY<=c2nW4KKg_ZC-uv|3fRu zHFJU#l8I7Gv_#`sf6tMG{XX@T$i$HDYq0-!0m4PasPoP}xW0UtJcDELu4b;5JbCd& zW@16De7f(2S~9QZg9BI}sNEm#4(U_e+r|Ney7!f|@6HpDBswsK9O?7$#n@shTc#u1 zF)=Yik3!*3603n0!-QJG$FKfdteW?E`IX<{$@QbWQoq5f4Vt^Q@d`u*H7cBVECMQt zUL3GlG+@D0n3@UmuG_4Rs1xu}pFXijn{C-2>XTW@0QZ}8}qe{Yy# zjLj5dac9gGe?HNN_xSsxzxY3y=_xN&*AOjKS}>7)o6OChhjhm zp=@_7STOquzIIMA~6UTHl;oR&3&n`dfkM^qO98SHzf2#QVD1dPVL`-of|Gq8P zM#HI;3j2EkX78f&3@ojNF)jeIAc%~m*h6OV`<11pW(^o7RH2?4%cr0I1wiu3_kV}0 z?lP}V-eT2Qgs{Y{o>oKU6jdMsox3pfN*S5?3#2k`CKBX?U_b<-P9S6#GWdWB)w({r zP#pV1hSmFba3}EAwXg8-BfkJ^Itd>~mtxFG*O|s*jwG-6GnhaG{`C8Q!14MWT-|WX z7%C~5NFVJN&-NQEuF&6s(la>u2z$29k+k$_bHv5#bZIu0POQI51yqFm`SW3ZRZB5+770*%H} z_nnXytmD&9|234X`QA%k04aqD-Iq%hUWAe9^x% zle}mr0PV>VYk^W0tGs-uZo=G7dE~+qT+vTZDb1o@u$(L(Y{lPcA@}kdBLXF&5xCQm zFGmhL34j_|_(jNf{;^KSZ6wBvrmJfYNIm1KB!hs%c8L0kk9`@fD!%jGuW)7kC~wwp zaHnlqMdjaGL9LirdLlp)>!0j~$nkV#u_tYU{3UU~*VBsw$_O7C>Ks}~f7-Bh-ed64 za3Z{$uzUSC5s&>m2kpb$;#Lq?z-h~*o`t^qgT2~t%1=YXKmW#8(6-_2=`Gf+r52K@ zl2jtNNQ)#_V(4u2`{}TvFr}$f{g9l&n7a_L-L&O#{G$K=Kt~W>MtmnZdn_VRNF8ND`f-c%DaI_sjKfYkthHz>;n2`)uKu6-5v z=+AM;Wo~mDi^I$;3mWQr62!AMZ~$vwzy23od-qS7)-`XKBZTn22epz^N}?*dz;q$O zwiYL4(#L)tZ5#T>rjw7wkI!`?lowNs@T|TQu8SiuSmrk*^J>Y}^DnRvODa))bQFK5 zg^29w@{Yetgd!sjAB+e9U4Y9dM8uEpFBb#G5+R%t;z1id0kHJxcYPZI!P8fM5+OC; z_~!r0#q}j*{T}bPD`*9MtfgQg-9Q`|W@Mn#4VZ)|#Aegsq|tD>lC*O=k|0}wm7WM# z>rG=y3xtyxWzk65^OjcJyuqzaY6(|v3DF+ ztz)sCaX352vj<;5BAC^44(cU~YC$FHk1XOF2Re_Y%{*^`%?&l;)>uH@9#grS03^;^ zBpiSkXnK>frCq;w$iJ@;iGYt2sK`fbeIcHD^Jjndt6W$=jIQ71?REvNL%0@M z0ca+|Zg2vF%5nwaprOzw>p6#L5f!~u0wh{?9rOZG_YlMZi$8YTSWg7(fn%+1VXj-a z_FM4i&vM9<+~Kx2{fUOTm{3<9iU0i7f5M%kH;~hYn~gPhb2J(n{)T!a;q&vN~j< zrVpgQ|0tvzgG`iwIXfSbJ8tKx**yM@Y@Yk-0QUC-2%jAX;20+#X=h_ADe_zT0hby% z1w=6*1`AbGXeMZ8)XdP#n8+zlUi>_t`|N+tS_lr-7r3M+w9ava<)~>nG7T$ZX`J13 z76N42*Pt6vaV&YCfVG)?Wye0G}+?B7#v~W*v;y9w$li z_lw>V(wR3i*3S7;8=dklzdFQ^JL%9j1xuz}UOdhf{iM(FH<^3IpI$Ik(~p+oFJ0#) zgMH>?3G`E)=L3w_QivPX9{9BPY!BLu6}Q=Mn|$9#IdK=Pe+eQHi2o`opGQ9Y>EJQ= z*!dUvkr#iFR%i~^=eevGxYm0N9vQ>2X=oB6;KPAK(#kOicj0zC)&&=I%HE#7(a_qg7C3jk*<))|~}{K(1BWmq8X+e@unSw~pb|LkEgr@=4sT7E=p|2|{}KY2Ruc~D zHypCp_@ ztVIrqKqLe**wCZLU>7goBtm&kKv&?--Ql*q!<*OsfOn6+hEfU(ym{+;yz%bWsHEog z)+_#Mhn`a~bqoJv+7t=%6s)_G@P8gr*lt!sPi>QQpo2Tsoc4)OPXsDzDke;rQ!(YzxsUN9pZjHuS}-CHC-$BYT@y zUjKbwf9szhfp5S3Tg=6Tm-ICNCLzv$CKZ(sxZs8?rRw^g((#T6BxCACobcDVP!mJ| z8LkO$6%_cLzl-`bj>Yku!^s7nS$@uY`~}6|vO1s=eU_h(lMdu!gt^JeQw@)gXZWR*3~U;GzKl8mWsIyX1{ooIIx7vnvYiW)3tZtNrtsCr|(k5a-zI$$&;ejk2 z#Q_plop9KR^$~j*fph}hNG1Z*qbHcQ;nuB}sH%$ZxOb@p)LQ!tzslQ2Vs6B~-k_+& zBBBmJdwZW~tH)FFSslnE8n zKm=+I>xcQ$7yd4K_6T}i^K5kyhoCWrV`I4;i9qWWTqzNVdTF zoj@Q0%80O63Xm}=io{+zVl}zRx5Qr{@yNf>nu%2CaJUB!wB4Ls3tc)|PiX9*KdC#1 z2bbtDyt&T^8!Q3_s~yroYaEOBC(QH#Pah5ye+#`}qNZs1(Np|&n?xqtr&gg2Z`$-; z$_1o5&|T)&jE~>jlOp@6}zr@T3$qG z;W+_gJtts%j=|RTY)X1OwvfGoP%<2cN_Wx(A|Z&r0s{+y9TI^UI;;?AB*KLgicy?Q z*C?e}D9wS;%tN%F7BRQqX4Xp$Fvd@e-4ySCuXf9v>-*)h?a0pSKmS)h7W#j$aa*#j2C+wVkIYyvUg+=n6W_1nuR*#CqMglm@S{gwu)!nC8Q9v#&B#s5jZxcBLdOMg_H=8Db;L`%mXVy z1|?}JiV%?q#Oy-K1~VZbgh%vBN|u#oQB^Fa+Oz(mKOi*^avHRg>I7N~)^3J*cU@2I z{&?yj1VRf$Jg+6tXnS* z9JStAXzhAm0na)QCn<^rGV>7_Bpk`bh44Ax1fU}V@wXx*77-F1#DdyMY9uoynTn8w zI3T}}UWr)RXu1Yl@2)Gg>_mbt9l9h*h3=hwx&nb4a?XJA!BmS|6;nCqv5TKaQ~3

    9Hym-;S0b4@MzWwGU?gg90-GTdAAcF~8gGgrsAbxv3a zd2LC-Lms%|&|2is?h^!mP}#Ni?Ulm?7(N2%N-8i4-A?9T64DX1Jr49TuF*&j65>Ky zgEFY&#ZHH$V99z7@HlzO;~`Eor!#^V`-1=0o&4*_)>*yS>_J{UjqQ!lNso|B>-@UJxy}A9`ixa5pjsRAQXB9|w!Z|uEM#q%eEps9^Ls|3 zsqOfU13zW{Lto?c9>p^9KHw{9V+PVuxem*al;aKRepT6lC4`V@DwE5~!q_M-VW`^RDVv*rVS*FJc^H`*pr+dYiwZu)cs8&H z9Xi$pQCnF-lG*Q@a?@GfZEbI}QBT&*v{mVFE+A3;%V0y-;hwwEolNN})P4EeTajMHJ!XTEBlJyVoXbmlfh%8XGsV}ft+T+Eq4#nN4HRb5Cz$p>40d=F zmd%>?5s)v__r86BKl(~=Uo_HN!@pJ1^gXW4oV|$i_nkV6)Lw=#-N>`zFs%pOfbpLj zC*waTbROGrb|9vUgMZ!gTYlfrblqQA5hz~7ALxq8)GcmE2irqo@9Ng5I2VERD*ekx zB;r5&2mCj)IS!`u4r+OjJWXR#X0sal?hM?}y>QMVn!pu?V#P%tij{z1i+so-8-S)9 z%6~Gvu^#fEijHflilxk_6;j_~(T!)Ixq6-ZM~4p!`RPESo&Q3EYQD+957zjI5GFL8 z^ii}x4_fxhaB%cmN;L19MQGS~kK=#%RP1DJ@$5jm?9w0G-m>Px0ocQva(rfmZTHT_l0^48Dx=a$ zN(wC^QMNU;w7tTU41EjY9#SzI78=XZg2<)1wJ)1S#(ljF0q^QK=tYiyJ|W7Q^R-Y9 z#Yo8t&gG_(GB3JGC6xy#_Y;@>{N#q%lv?Yl>iH*AxF%UnBXtyS#{0?u*81=~(WG}@2NW-2QTv7FcO&0Nou`~X_D<0& zIhJz`Jo&I3Vea(GCbL5v{Ff=pAB|$TqOp?Gg9>+9fBeO8ap4h`r?hm+>Hb@a`!&`4 zsK+}k$Z`NK1%J%1@6(%6#KVYzms{f`^$D~?t|^f8aYaz@b?Zgm$>?Mg0f4F?i?`Ek zI5XtXXttHjT>X4}ci`vTBHXYZZ%e&(eDaoWk#-mL7(`$m-pztsy(M5~fm==2nkK!1 zazy^1mn!^74S#J~`;2c7&;$iI!gKAr9~4SB`i9GHaQV$Z!9GPi&Um&yQhNK-Ty9Rl^a{o)@mRMSC3G~5EEwn~z&2=L@M%I$q1P~h+HDIznQ5N)>R3J zrs;`^4ElUA{~BVg9)33oaDT{opiIRdS<}#CuSbs7gVCBQ8-$(43ZTH_6@`7uj^pta z1!kQm8S&bFuem;nd_y;1wS)VkRtC2gs2zA$Ai8u0P1BKc4v*IY6wny{hJ7vS$_nrK zVd^F8Iy+m=khoiXsK`C6EbF`SKiUn;Uv_*YS$yVE zTwcxvKWTZ{W=*wVYuXL#3zNB6uzYKiO|+Qz5ddeLCksxJG>3EP#1Ht&1TdC1Y{h*g zbH}d$*xw#PrCR8f0_{~A1q&KAPsEKWSymB#``R-#rSed zg^wn-TO7ZNt>nu7-3TL$(|hsV>qS0fK8@4m=}nc43Rt>6VeM4hL;4Lh&y;VMXok>N zvp!3Nu*$>qBBc_*oj-M$>wvg$r>R2MqL)xynAnLf0v{2BMic5=1FQ&Fj3lIPi6;2S zaOh%GVd@fbax?M2UJB~HvOnj)=b8u-?#V*?$hWq1IsUoJvOKgPt_8Cl*}U1K4G#_) zKLrM3_Zsan?@ix7YogJxZ1=w1o6Uu+z3#BoV2rTp*H)AVn*YQigztQ%uM7(iNp&KS zb8;4F?#go4r*zRxENL*xuRw$#5*!|5ovP!^o7Iry-5TWnb3smvs_!f)S9(pgf;J4= zrB4I42mI~XiD(%tGsU*)AmU*kRr@C3KsG-Td-Tp%e*bbe$rE*WN!e#QMq+fi^78fO zqC)~EC8$I^&Q~M3?8OBg2`VG^nkM2MXotuB*n}erO4F0l#H!*FX_>_{zDfdxd8A** zjF1vY+x;Gcp%}^>nERw(t|NKJr2BS+=QSy7vETEaV!wcK3co`bmRiTI;op7GxO6uI2fBX;D}b*QIJs|A zV>jt11N#kJiiUE8^@1PNRa!&LyJqM*52z|aLHHlXd)T@20k^ACs0U8$)?h7)&B}Si zFZiDmf!tO}(cbO1q<_{Vi;Kp$`qpGc{6>pJgRkbV=`O_=x&z}y7&vdQ zFMLwfyIa=eOfUf%#X$BL)Vy1fq-~7f1uamRt~*hP&OCNhD1^gsfUcq=(U!-#S*haPzJ5 z-ZQE07(G$t#PYX;uLSFF*ZG3C_HK|wrp1(q8}t?~TjMi>fzHO(i{N~-wai>ma0z^4maLXea+;NEB zV_q{pB|%d!a^?5hpy27sU~W1o(_gh; zyn)BAy+kW?*6#qE5jQAw#GORUm2J22ooq49K`-!Xdq^dzgT8x^SMCY=)IuM^mp3;R ztp)rxqt>51!-x#aK~Y8$0&~S_MDH0JVpOvh*jnrLnnw8^ zxeb~E@=VNNec_Z6YQjG_NKOmJmEQBRWtqWl9g$wmvpJXu@(`nFj3`3OY@WnD&>n}D zQl%p4Tk6e332Pr44|nnT1j)E(ayz1&nB47Mo72%oxfDcyXlt#gu2W9#LO`cSoh{v= zx|uVV6os55z9c$+IS`-?ww;4ZB~9p7WO+w+M=%Jz^d|t>5YC`0==%qR?*rxG)!>q_S`Gx_76MeZ~0lt2vJB)5LBlw2#&H1 zzW;Epo@1>iO0Z%Ka?pE2=eQ=V**fpq=peoCOcWbo~D$`?? zqlK?_V!OMCeWbNs`WP-iX1PAxvUSC?Y$VpCO3|z{2JM^cHXCby`-;~#6sekJSRfs) z4JA4n%D!R-7R{_cHI9Zv?o-|*bPH^$$mh#i@4b4cCa?<4j0^9+R?E=T>O_cDx(aQ! zv1piW*7tPv1?_L#mS?c}<;dgdlwoni$kgvpfM_^OU2cc2)+#x?Tp`N_4l#5@Hmj+` z0%au_G4k5vmylZyJ6CSGp?ci!nyCTMP)N%9R|9pC2Of90RF}LkZX}^|mYmZ79$d5z z>?YW?6v_D#F2|s&!u)esTBP^Iq7fw78vau8InscFeL3NeOK#J)iH0`xO~R)KuTZt> z_Zfy$NfIP#4f;&CI?mh%;pi*l4be4Y1%dyg=_}lt{@%YS6%eEwMZgK8M~{|fl#CuA zQW7JiLqd>lkdy&RN_V%wkQxHg-6BYLKJU->xqg4ZuI)NI=RWs&)$MoPvLCZ_Gw``M z`A%P(>}qj*Ez4m%LG|5Sz43|U)Y?bIK)4_}-nGj{eNAF<_Q4dgG4qkIy6P6=)LMl5 zxOu#xgA-R--4L|u5O~c;OakJJ{<`<0Zj9aXKNB!wS(-o=q%5Yjb`e0mloJf_%moVp z`wO6oofMVr7o6;8{vj&1GaZ&MVayrVOtrH?{XJQ9q>pymL-LvrP#u5nW1zRvBGg+v z#;joUf9v~hwT#@)%T-KV8wTujmn@0fwrip1Ip`ITf^=!xipx#E=r}=#Ni)L4BF8U8 ztSrC}JXA+o56t26?~V&;ooBi7{c48hkdPQ)&AiM9fVd$$iEHN{;@v3nIvhecPnwk) zZ>sFpy5WFo(GqRfonGGQ+uD}}Y8|ztn*jZ&NUV>6Y^eP-w|SaNtC2ZLn{N~S(h@qY zcnG>VU!wgB{rI;uI|V}p?Aa@hzef6#cL${Jo|R1+d+cn~bBiu9SoSCPknbsSK^tv}eq0wXWY5>^*{Mu>L-c$Cs*$(6527Sn#e||Yj z!=mpa1yW7o#zW=4^sDN28Y&H#3lc$HMo%gK28Ecs@0)aqrYQ7o-+H*al@L#%9lx~p zj{bs14yeb#gaYT(v)_LrsQZ4&0@4EKs zU54#_KaGYOyohZ8FaU4i_FTB=5XID5=dhRgxFEn#xu)y}lnEKJea(RrP8G7U)8^2Y zOF5sTMj6v#D3LWcr%*9^V;-t}KNl@=Vsp<=@V7SW<5z}D6#4>J9k9@YMU zLk`BCk6jnt9qFvuDH$RU+WZkuzBn+{k%qf*TBVayn(|kP%SE}e_^#bFdEoFB^;RiK zFosFZ`u6em#Qx=nfLwvi3ZOWWS_Awv*0dmbgAXBv$y}u)VzM2?)po<=efO;Jx#K@D z;JEuXn$qIDJma-4m#S~`KxAA7ldicaxs~(JK#2G?kk9f!MX@9a5e}_!m<1{7uIh!c z`{{k7SXL^PNSXWJAc}~MrOw-6;MKhfuYkain3v?!nKOz zb8iq%pg=yQ%|zw=gE61M&l90cEiISnCQkRak4*B;zunEM?(MuB7O8)n&ii|ywsG4u z_{fX%%|~tg^)I$G4?B}yy!4EYNcJ3pnk4}psCmB)hec$mk>-}wXA#xjufJgy#7N$M z`N^I(`BiA3tTN-tQY*EKB%UvM_tCO^vpgDm$X>_C)_+t{XC93JQdrdweq^kcv24kf z(v?}|GNnRvodh$`E=1dRSURb8%DEShmiUgJlC)`9w{^Jx2deUxI;;h_mYf!y6FcoW z&D3+dlCS+ch-NwJ}EZLQA8vWYIqcgLS+~lvmMmuR1&202p zt7;r{xd7c2t%auJ^=+6xB2JgZrgu7=<=g0SE}|uJ-W+Q%7JgcSc168hl$bwS`3L%z zbbk{T(68_C%p-Zxls@_@wxk@wP760*;{pcV19#`Uj_3YuWDWj1Tvf`O_feut?rYG8 z;C*0bTG6}cmpr~S%!k1d2*uaYA2nE1e#!)B0zgK8yrC ztkI6CAtZ3G`N%&1dSYrA6k3oZrhU4zba*y?zrCWjeVcoKNp(M*QFmVer9rS+_}dm? zxjhc#O&&<9!Q&Uwc&?-R!f}ghXYw;LS*N`n)B|9n&}2}8rbVsjSs7dunX31BbH?`V zxbWaP9_7iR02hy6#6^g~3tsccvDhQMpz>CC#;x}xkLh#qE`JjuIDOctaP#B0TAha= zs^W58Ywbp)o$m=FF#Xs0EDiURm$4eu|ca0epD}7KJLX4XlbX zPvbbY{uR$Bg@TF^xU(F?#_JFN#Q+V~3C1;z4es3~G}Y?uISd|}*T8j!F%}ZUm`}Nt z`gN!7mQM>mz4pF3B)2a^ZOT}4tuczC(=py2CQ3uq*m)dGi@Z)A!7N;-YZ4GNFlnog zlbOH{G+O4-Pw|>vmYWtdZ9e0XN8^zEZcBW!eT|fDuOI;>pkaIFdO<9GPg`eFIml#ugDf-ZR^i}^HqO6flqbD)l)qG1Fm`hOi)K!fL@_y942i}zN@L7dka8gsI(i=os z2vdA7Cm$Qc{L;#VPLp|dj$qZEX@#RuU?CH0<&kN*z6ai*Po7Hw9Yfh9oGhIvlREt6^ zP)+vwt@X2jAfVCC2Wa0TT9A|NX5+QNiC$Y?eg8z?#%#77cGiq1n9T9~3;rScXc4-n zl1~a-oiQ6hKkho9efh@3VL#x$WFis{61WrADE$%&c_}tk>Bvy7xz^Bri}ZIqmKi+u zF5CU>$&r5pc^M0KUU{n5rS28z$irySL)S~n;%AY+TTZnzd1y~tq(kt>P$wl}#Mp9A zxLTyEoVAkrkwNecCn5*dj z;Q<206_@SzJ2B`#(xc-2ssYD)p@oIGg|q=9DQo6&Fbcp3pYevj z4PoTdD_pmezy%;te=5%5s^Hul zN=kh3fWGDzFL4tkkACLR8m~6~oSx!q{jx&Hk)JuZ_4oPFm!txLcduv$=kJXP+XPBW zH!_`NYJ#z#gN-k6d`iA0bzC4xE(1P%KZWjmDmunqE*)u9)9vt#JRD!7&nj&Iy!)TJu4~x6g0t&nSX*?MTr*u?1KJQ^|Nj6P)Q3x=-X1vi${34f-Rw{Ub1is13y zC0|s{n}O|g@VowfCrJ%t`K08cVd2n;_pMJOM-J5knco@j5x}adKki zSM9_q`9i8|Qj+#!C-1ZgV2Mlr{}Q*KTuVKp+OR0(1KLJ7cQjbJw8+(%gbl$dLs>|i z%K2&!%@Te3kH6UyUm&}^Wkooe5f1Q8a^c1!xH|<2J#8p2s~U^B`X7#Q{OXne5UZ<` zOTsQlr_90zVw%jxlc{nuX$?X2QGbTDgx!=Xy0a&ZT>9t+%sEwg$X!IeC^b*+va0G4 zXF1R*zPA}M3Ds-=HW6A<0H`-Ik-etN^kO>)m}(E=hU18FiH4Z7`+vB+<6AK&oWlVS zI;qt<`=7k;_WP0|^7%J^lsiA}R6=hccx}C1Q~U#VN_$jUDOD)lVj54eWvN}Ww66Tc zSd(G8gx05i9N7Y^`i8s>m&W-G0Vp;X9tmk;%KWsHv?U$FOwUyj<*_F=sAM&4PTlVq z!$tATKPU_31oY29MV3z?;(XTNYwN#$d-snqfdd;IcT`~~d$>HKhD@*jYi9i>Pd=Gm z*W7LXlen)QEDs}`D`(vpGRuk>%mXG_IE6G$(GJ${c=0F_oej5Iv`nJj4k|aZg09n^2)qWwplJhG7~T_;imR!Aled)<1x%l#TiW>A>GGWsinp?fF~(q( z<(7G+R2sZO}+=^ zlTWY5@l-AM3v6jEEH@;d*Gkt#NAwue%6|1s1d*DzlT;)+OARZGAKBBUr2F@p#oE*N zCGl$@uk!1vOka=74M^Am2-VH~P4nCh)s2_PZfT8_g_u4d!zkVEFSvhMmR5vgH511= z5(Z2dkd36x_i6>u>mN45|0IquE3w*Ny!|}EtR4&W)z7L z__!hX-y#Obq+bDjfLH^d#M1>(W?kZMJx<;-;d8#UsPZ+w5#Q|zi`IHZ^W}x^r|d;A ziS0=*2d}23_jQOW2eIY@BfX0!^OoN&KGb#@YT!i}Ud-;8Ml}o(8x9{a4agb+p zY+3hCIdEq?&rLVri0KZ%kAwV4Fe%bCNZ$Zuf)Ws@!X(;B8<-JmKo~P#+O|QzqW_`N6uQSY`y5LkwRWgYPPu_Gqv9RRn^w$_}FauE%k@)l0c6# z@@XNTfOY@K{c1^{8hFg6<<~@B>rRYWIChyJ*t&qy8(4rH$&s)%{?DdOe?Iu^{6Z9C zd78cBA#eg@#jE&*W8x!}tc8Nd-xjFqc=DGhw=+VggnCZZD_m3 zU8GHh0fE(%;^_P(rxd8~_h4fhi>&8?$tm}69s{JZLVRn!o|rX+SMjWL#Syj55C@NR zm|87M=#kl{zKH9#x%5E><6Yo%;_^3(D5!Y)YBAfD=D#L@epb!*jNP%dgr#c(!qrk3ar+dT9kk8Q(~7&l87GgJ-nnD0WPj0|{97v> zX`e*jVq%;YRVd;8NeC|~R5<9+1~OyFjV{!DBi#+|&h_u5wSHDH+3Gs`8A)~g6%N3O zNJ*#ph4+49F5!Vw4XM8CtC1Ni@(Pt*h{x_95lw%ZK@}B%nrgV_@MkfuULQIyNMY1aq zj_xh(Yn1N*NZjlTAQpW+uz$DCdpD~@(gGvNo&6(SR6ABhjY^IKB9+)f-g>2y`xjL@ z1oO=pL^Y6qzFs{`tg*kdNU|kTxijLwRBgJyf^A$L_MpfiyalpInFw&LQ!5@pukt{8 zI~i-S-rKqZ4$4Tdg3E9;GpWnV^`11A4D|R1C2Ph>`dY2?OEOo?kZKg}1LPKde&t!L z>ONM4aC2n`M7)8e4Qm-vN9?LTNs98nLgIIyFKd_?f5Kydfv|4jlB=T}iwD&LB z7(tdL(wK28_WPKWiyvl+_AQ9wR`)ZoeqT~&<@%WP_?u2My5=ZFF5C^H^-*bussugm z9XG*0D-s$!#j!NRG;%-m?rY=&uJxJhT?)s+mBgiQ@|4&;Xkd`7yrV7d* zsz;{5Uk2CK;?Zx?5)^So1Vv!P5nFgypAMdzQ`an}^{AyGlRZ9FMMKC{U+Gc{%jY-X zu)9pYOCgBkZsiAt&`&M&55J5S#{ zhLl5M7kxCGH>*GY?!p)%cA%kvtP?kK(m*x=h|I1<3=BT6>Q&IKcNi=_^j$ssg29(O z`0P2d9j$}+^~h%8xM*%mV%y61pnCssvigu%&vq8He;ZYv^=mG4&TP5v--C3Pc>1!7 zlYc^Q3^OCIFpm&lMw%Sm@6IOY9+xD0@w#j}k5_6Y00?Zf)wmc>OQ?l%sZrnfD_NI% z2UbeS2QjWLgQhQW=`9X0@9X|*eYaY*G!G}R7NjYOjpr{*#*^9V5s&9QiDBJ}tO`17 zV_<6)3WiU|Q7~Ho{@WN!1_?XK zA{i$l=l%kjK-E)=t>OF9kr2#F;PPU-Vs&agJt6VEO!c>vR+m0kX#fh$gxzpUpQOHD zoNlN#;W|qJdr02BiCd=qE(X|g+`LW=9|4^ZK$0s(raR1y8T6&$e6s`;0}y#xm0hvR zU%dI%|LE<&m86TUDa3D?6anI~8PJvkL|ObXIsa$CG@*Hwe6Z$fz9BhMh+QHSQ&qb- z`XNphg8oU0qsWQau_@h%xfP=CRaiPaaRai4NGkdQ6(6Tp{ysYDle#%-S-RUC-}n1d zcV+)K;C}UP%f5cN!LZEzdz5!IbttNLX8-BP7F}V;L)vQlY4&gbVj!z!g?B;3|EC2w zxoE3_6a0ejjpQr;ctd1-mMIfR?Xq0iL!jTIXmUTewzt@J z@98ywCAcjn*38c$%LdJIBEi3HLX!>@bCZWI`{1;$o6b{{_mbigpCi0~j-8^;_)k7NFoIsbqN4;UzkqO47(UGV7?C?o_*?wy1LYjV--d^>ZvUM zl=gD8r8T|akBBoht#K7l{0_%UTF2Nalo9hhc)6eYeU(3>&CMeHm1=*vJsW7AuBP7@Z73t1$Ra>*ac$6r^v4`f>9^pxhaaf zIXef?rOfvD@Vc9>=fR6%GPy9a^5g;m`fNB|E>|zK$A#sY-`B}V2diz4PvnX2Cf~?v z6}4?mTAv3$-|Q)_-rY<`+e9Ze8ti(DsK+E#H$K1#S2LP1a*zBfaW)~E6Z1v~dxLsg z_U)NMu}v!9*9>PIEic@R*;R8{-s&E}{I}ttr@W zkvRCeK&sN};;NoMV0*YG5;rr93uQagPM{^!y2H{e38k2Ojr3z1y1dT%%i@Q9NU`s5 zZ&BDg_1#L4LLeHv$RS7?1bJzfPip}Tiic50Ev#mmMki;z=PIDZhaP&Z*vM?w7Ln!r z#4h7Gz8s*YTLL;=9^1gz8du=dzP+s< zqpvyVfgS|ZIMYx~Ogyz5YYC;bx(QdTl{1rC)XGTZx^eAKrVBLZ{YqiAs8TDp^9$@} zhXrd)9rv7fuLf?=7#&OgsqsoYTo8var>Mlrs!Kyr23kSb$`b$P2Gl007U&a zOiQKiyFB@$Ed#@-s4BcoBHZ(gloD^A+(6K7VYZ3c)>h8*U$XC^YNe&G!2PFu)#V&# zpSx`oa*11P*Yg~<`+g8>A>iQtVZPktyt++2^F{{i=HgYp;opjD)D=N>+=f_id}H=C ztZWu=*KPTV-Isp4qntxe$hQ=}TvQh(h88 z3MN!n#W)|X7g+}${qgzy1q|m&Cv$NAd(}|dCR!!mSOXI{R`vlN!h+8H zd133tbt=XAf^L{r;tWm9J*h0@!0_jXWQaV!EFo|()k4k_;05J^vp@Mgl)~z23j?${<4u-yq&2OOOHy&>H!si<4 z!1!)eT`(QKTWiD?T~+V<)NxL7&*XAt`a(gvqu8z-z53Zdpk||WPq}kpA`&SAQ5spt zl_h*)_M@=JAN4(N)m$PTjxOw}x3h`1a^R5ickOjV;X|mPsiYDOi{^E!cJv1jzAzbM z#`Tw5X+c}fOnd_N#S);wXjY`Ss|+}HiLld0`@A~$oIR?h8M7Yw|5`_6j4oSpG3 zK&KM(Ed_@)GLI*Ze}8|?e6ywz3j-k!Os*$dG)v0!;kPz@fzrm`UHC7}qd>9f$J|Hb zoKI#6SG{wGqnWx&R)Vs!(jQ!4yJ#{R3+Ar{I%zkmT@^_`{;;fwi;gtc{|V`2!z<;G zqXu=qJ;-Vd#*R^4=lwX6j$x+pkF^a^tc#PPj##l?9IvUC z+KF{Ra2?ekK?$90 z6Oo8?^8;2q1mQEc41Ov6C)m56)|R(&`krJlh>X&y4kbXn5f-$qZ0qE#tz`_gNyfyu z@rL?V8pdgVvt}L{pUj&&qaKUCbHUlpP=KK(nc*Cg_2zAj4io8%ghB6VF5>uFhBVRC?*f$=nqrietRVWKC zVNqILBGY_eN-mPd+J14+#^h&pN3rkiJfw0{l390=*7)U7=`W)8p_xbf2KquQk1_w1 zre%$}Lz_QENi4jM@4?n$NT}LznGWa15JnyFW=__sF)y1hw;GXY7bJpwPFo#jTGC;* zVn{<>A)HszsbsGlh?o`2`^Y{{>KPO@IV;7eyv2C0=u!5y!FWT( zIvH6^tmuNa@@x;9rjOoX8ETyKP!(D08*UmTF~g{z0ke+4vB8kkGykFVNqXObhNpz! z2Z}KI+*C(x6;hq37-wVu)XAh_wS8O~?Sx35RR4nvrl$Kl+p-ZdC#V04sf3WRF-d@} z#$6#(94^m8L4ar!^-{_u;zZK5S%g0+djUkQJ`Uu1i!UH9{VjOeaVkOkovu;6y1H@% zgGf~wLv)fc(Ia1?-T9tBkFA$WkyWx|*FW~on`*NvLIc|}Xg^sSO&OEmN`ab-WhGSn zk#=I*qb+pl-vC+u+~NGY?s~^Y#?Z0Ya?Upr&~BUFX1f_Z;`1qnH+!DIr^0MkR~(hf zgkbo>y2G|pzh)vmQ7AHsfIqCJ_jlIWXJUTONNbO->GS0u*0RfMu!5#|Dvf?57{df- zDbE!_>F^xHkjpB|lwh&fSr}?yGfu4rPeB)!3T>m5=VwNn_gD(1c6WM@yNsUyR!psX zqm~WRsr?b6Ed8ODvKC#*WNlYsh(1<*8JH&=I?L{ej_ChzL{VZ621aBOCe+$1W4<(# zl3F?6kHbmfmiv+k{DiO5^+A`?em;Y)J#$a{o^+CF5(iwaI^T^foz6?|s+}@1lkz+? zCY(o3No0|fI(&*doJ=)=1wIC;{A=Zg{1BJs4X%{2;ED^CtMV$ddnmmIU!OTmi7MK< z90qqwFk5P2QNm4u`Fx^Kqe!J6#pK++@5{eq&nt&V05_7ZP_Aom8a|XtR|ccNAxguV z3whw8VN*0cA^=l9I}VQuObvTF;$~DE-zz9=^-NPTqxcnmW4dA~yw#<#E6m1av!wk# zKZ;mgy-OpGBGm3LOpOgvJc%p=fv!|C1zRvL#zK*P6uyqT0oG@TBISHp5ocQG` zNkXfI#)Vo(!&}96x7AFH7xOILT&qCBq3*iTF_D2WYo%bzn$ZX4g<0bNBBNGb=f&V4 zEW9P2HIA2y4uuTHgI|aOF@~-EcmfetYR)p)XEjlZ>`| zwZFz~JFcwK61*OqChBDO>Uf0r&`KI_m9U**j+u>K79ceG4TbeSgKQ63dXqPpxCj2 zqwW6%hF8XwSG6EGHX;z;G+sWfNipJBZvY~IkZZ4oj0-#?4g*4+z;?`_^6Nk)IbP51 z8{GSwl7L&9iKV{_$7hd9CrbR&Lq?#~+ZF?$8K=YE4wsEtOBgm4e&SC{SeTVAT^`ri z$gB-VnWN@P8!uj48Y|LBYkN#xbSQQ~VokdhVSdy{(m3j!+F`_A-tHA-oJLpz>(iU+^O@lrz|QaeB^c?Z>}xBIbt~ zJxpG+NVgucJ~E&HJPkOr;Eh|l#5^)qAK$mDwQ|cs)61$6b!j2HIm3X)6Y%CcEWDSD zV`qN6&4~vb%XQGp#+`78^MR{br1b!J{sX2tc2duVdAcP6!JwDS?XX1D#yKCPdEQ6kkTz-QHx+CGU*2^ zf3t_D$bJuAF12l`|T^4US4Z{D0@TVzrHZ+O)^WkE4&X%Br4)aEPCfp#~w@7N|^SyiXfP%R{dawR1Ck8PPE?^;y+ zz({E=SJnftMT$CZ=Q3+@N~s$&WpVu7UlD1yfDJI$FqzT~p|4gZN9@Iz0_}138@lUb zSNtfR4pe8hiBnV7Lyeu-0Yt%{!5Et9VOBPWz5>U{d04WrnncGgBL@9PX3VK}g9^t})J}no6KT?>BZV@P~Dl+-9}x2<q6<&)Oq=>MY;oj0+&@IQzG3_go96o=YChe~(Ckp;kP7S$%#ZZH@~2Y&;~7 z_B@fBwBD7o?;HO8geGXOmhOq#!d|syJ=)ay83f0dRl}!*5jqw`Y>5<}97o52bl9~J zjlBDw21Vgu0fjzBiI^(mk#1R?arP#+C#4Etm3_vtH!N?#E>ir#IvTl(+p#WJ(t~s}1_O4qZgR}Jf{4`8oMp>r^;1o^c?sKrB{eXeN6S_lI)5V*W zW=Kan2{sLdA3L*T8}Cuec(eII0wziCdik-Zoa!qKDf`fsve``>b0t4DfihTGb#+l? zuCiYDvZ4Ha6t3wuzA_8JPy387Xj-unh%9{0Wh|AF^9?IVN1G;4QxC3mrJe;0e5EFd%*-Z>Y9hlaFbMw$BX~9v%oYkM8 zkGPY}i4Jvtp?SMNdqYx)f?lLUgJpkfhe);`Q_xt%kpx1#fEHus1A2KzkoejbFTvaS zLuhj3VA0mhX9@$x>o?O`qqN;@9{MOj;t?m!2=L*MPZUDKO_9T{tjO{Zs}gZoULy{? zD7uoj+zJgxThcSU*AKnE!oO(lIg#RUn5fy9!wht1Z%c|<)`KSrvvIo3<$Rq*E3+f@ zyHw`hPr3me?Oeet&JglMFrF`$qAdR9{@wV}4HZz%c1{o%bNl5}rvtttpS{oP#~P%F zN6L&!dB~bvqk$G6F@)3-n@v3?1&6z*45h)TU_lhvQ&1L9N$p3S-hWMgo*^)`CXXbB zt0?aO#32nDpfy%gHR|ZOEinH5BA0|+SJo$fo*b``G#vU(;Se_*oRgO~_C?;At2OH> zzLo?d`x=O5t-1=LgQzN3OW?Qb#axq=F3A6#by>|r}82)k%K<0q0992pGkLE zs46ROq<^^i2U7f}*^dyV!%b4pwtJ)eGktOR1mir)geB^LFb*fee9K=A`78kZA?C^J zrH+PEAgy0K8DX<&Rxeg6VQX^iy$5-2&*u}RE_UA;{S2S1E=QJ5wl**2=3D9}|Cpv7 zoZ$P5PHV8&kZGt6&_cNDk?+5dQyYBA!yj5n`PRl^Mc9DbaOz>m+k?QxM|#N$Fcm9G zMd~xZA*;;_9cJ^yL15~K&T|9A6`zmk7)u$T6fw)vD)wDql!Qml<-B3|g!SxH5*Rtb z^GeFv;Zq7>DlU&=>7+7fT2sLL5SI_Z0~t^Mbj*&em+aP|9~Ob3{Yls1h7B8IuIgVU zky@Yb*4s{ z7MM4t@QfO6E{&e6^I+Dh31h0Y^GSB)e>uP4{sCB2kFl)Ii}S(<>IU8=o`%d(ot#iS`Mw z@tHv{_lsgSCT?TJ)dv9tmRYoiKTfL5i02AdL{5doH&GXGda=Y^NwT>EU*2~c7|)`w z9T%4{`smrYWTC?)9n|{C=WBtuyVHuItTVA&7eUTrIakcPH-vt`zKh74XXMp$FOeO|-5p{AUU zrbi%)VtPZ!$$w8rX4wAV$&d%XU{>1frF*|6xii4u`*T8&pQk_@vXz5l@Ebp4*2SDt z=2uwfG7hq2+i%&~wBXa9%644Pe^`}4CLH8}nKqHa=E z#QBa1wqv3qdxjEu>KYC}ci#RVd@?W%Wt?J2vlTK4$iwDm58)V;`%^?Q`LF7qd(F)` zkkF{Z&f5te@ROi8W65_XSzxbVQVEIO*Kijh#_##>tT^||rUcQIK({kv4_igm;V=8p z8t77a9|noQ_`UnO)M>%q`_B?w*JRs%t>m6d8SNTK$9;*cVa@;y~A^20| z#%%j!(RESCpe!7!#m(P=wyT8?F`Lz~;EIe#4<<-ax-#KKtD

    L6y0K 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 z�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 bdg| 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 ASpWb4 diff --git a/taskchampion/docs/assets/cgi/logo/logo_64.png b/taskchampion/docs/assets/cgi/logo/logo_64.png deleted file mode 100755 index 47e028add9cabb7252469ab8c1b6db7a73a49240..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7359 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 diff --git a/taskchampion/docs/book.toml b/taskchampion/docs/book.toml deleted file mode 100644 index 7e2fa9820..000000000 --- a/taskchampion/docs/book.toml +++ /dev/null @@ -1,9 +0,0 @@ -[book] -authors = ["Dustin J. Mitchell"] -language = "en" -multilingual = false -src = "src" -title = "TaskChampion" - -[output.html] -default-theme = "ayu" diff --git a/taskchampion/docs/src/SUMMARY.md b/taskchampion/docs/src/SUMMARY.md deleted file mode 100644 index b5bc3c6e5..000000000 --- a/taskchampion/docs/src/SUMMARY.md +++ /dev/null @@ -1,17 +0,0 @@ -# Summary - -- [Installation](./installation.md) - * [Running the Sync Server](./running-sync-server.md) -- [Internal Details](./internals.md) - * [Data Model](./data-model.md) - * [Replica Storage](./storage.md) - * [Task Database](./taskdb.md) - * [Tasks](./tasks.md) - * [Synchronization and the Sync Server](./sync.md) - * [Synchronization Model](./sync-model.md) - * [Snapshots](./snapshots.md) - * [Server-Replica Protocol](./sync-protocol.md) - * [Encryption](./encryption.md) - * [HTTP Implementation](./http.md) - * [Object-Store Implementation](./object-store.md) - * [Planned Functionality](./plans.md) diff --git a/taskchampion/docs/src/data-model.md b/taskchampion/docs/src/data-model.md deleted file mode 100644 index 2a43df62b..000000000 --- a/taskchampion/docs/src/data-model.md +++ /dev/null @@ -1,5 +0,0 @@ -# Data Model - -A client manages a single offline instance of a single user's task list, called a replica. -This section covers the structure of that data. -Note that this data model is visible only on the client; the server does not have access to client data. diff --git a/taskchampion/docs/src/encryption.md b/taskchampion/docs/src/encryption.md deleted file mode 100644 index 8a1e57a8f..000000000 --- a/taskchampion/docs/src/encryption.md +++ /dev/null @@ -1,38 +0,0 @@ -# Encryption - -The client configuration includes an encryption secret of arbitrary length. -This section describes how that information is used to encrypt and decrypt data sent to the server (versions and snapshots). - -Encryption is not used for local (on-disk) sync, but is used for all cases where data is sent from the local host. - -## Key Derivation - -The client derives the 32-byte encryption key from the configured encryption secret using PBKDF2 with HMAC-SHA256 and 600,000 iterations. -The salt value depends on the implementation of the protocol, as described in subsequent chapters. - -## Encryption - -The client uses [AEAD](https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html), with algorithm CHACHA20_POLY1305. -The client should generate a random nonce, noting that AEAD is _not secure_ if a nonce is used repeatedly for the same key. - -AEAD supports additional authenticated data (AAD) which must be provided for both open and seal operations. -In this protocol, the AAD is always 17 bytes of the form: - * `app_id` (byte) - always 1 - * `version_id` (16 bytes) - 16-byte form of the version ID associated with this data - * for versions (AddVersion, GetChildVersion), the _parent_ version_id - * for snapshots (AddSnapshot, GetSnapshot), the snapshot version_id - -The `app_id` field is for future expansion to handle other, non-task data using this protocol. -Including it in the AAD ensures that such data cannot be confused with task data. - -Although the AEAD specification distinguishes ciphertext and tags, for purposes of this specification they are considered concatenated into a single bytestring as in BoringSSL's `EVP_AEAD_CTX_seal`. - -## Representation - -The final byte-stream is comprised of the following structure: - -* `version` (byte) - format version (always 1) -* `nonce` (12 bytes) - encryption nonce -* `ciphertext` (remaining bytes) - ciphertext from sealing operation - -The `version` field identifies this data format, and future formats will have a value other than 1 in this position. diff --git a/taskchampion/docs/src/http.md b/taskchampion/docs/src/http.md deleted file mode 100644 index 90b93753d..000000000 --- a/taskchampion/docs/src/http.md +++ /dev/null @@ -1,65 +0,0 @@ -# HTTP Representation - -The transactions in the sync protocol are realized for an HTTP server at `` using the HTTP requests and responses described here. -The `origin` *should* be an HTTPS endpoint on general principle, but nothing in the functonality or security of the protocol depends on connection encryption. - -The replica identifies itself to the server using a `client_id` in the form of a UUID. -This value is passed with every request in the `X-Client-Id` header, in its dashed-hex format. - -The salt used in key derivation is the 16-byte client ID. - -## AddVersion - -The request is a `POST` to `/v1/client/add-version/`. -The request body contains the history segment, optionally encoded using any encoding supported by actix-web. -The content-type must be `application/vnd.taskchampion.history-segment`. - -The success response is a 200 OK with an empty body. -The new version ID appears in the `X-Version-Id` header. -If included, a snapshot request appears in the `X-Snapshot-Request` header with value `urgency=low` or `urgency=high`. - -On conflict, the response is a 409 CONFLICT with an empty body. -The expected parent version ID appears in the `X-Parent-Version-Id` header. - -Other error responses (4xx or 5xx) may be returned and should be treated appropriately to their meanings in the HTTP specification. - -## GetChildVersion - -The request is a `GET` to `/v1/client/get-child-version/`. - -The response is determined as described above. -The _not-found_ response is 404 NOT FOUND. -The _gone_ response is 410 GONE. -Neither has a response body. - -On success, the response is a 200 OK. -The version's history segment is returned in the response body, with content-type `application/vnd.taskchampion.history-segment`. -The version ID appears in the `X-Version-Id` header. -The response body may be encoded, in accordance with any `Accept-Encoding` header in the request. - -On failure, a client should treat a 404 NOT FOUND as indicating that it is up-to-date. -Clients should treat a 410 GONE as a synchronization error. -If the client has pending changes to send to the server, based on a now-removed version, then those changes cannot be reconciled and will be lost. -The client should, optionally after consulting the user, download and apply the latest snapshot. - -## AddSnapshot - -The request is a `POST` to `/v1/client/add-snapshot/`. -The request body contains the snapshot data, optionally encoded using any encoding supported by actix-web. -The content-type must be `application/vnd.taskchampion.snapshot`. - -If the version is invalid, as described above, the response should be 400 BAD REQUEST. -The server response should be 200 OK on success. - -## GetSnapshot - -The request is a `GET` to `/v1/client/snapshot`. - -The response is a 200 OK. -The snapshot is returned in the response body, with content-type `application/vnd.taskchampion.snapshot`. -The version ID appears in the `X-Version-Id` header. -The response body may be encoded, in accordance with any `Accept-Encoding` header in the request. - -After downloading and decrypting a snapshot, a client must replace its entire local task database with the content of the snapshot. -Any local operations that had not yet been synchronized must be discarded. -After the snapshot is applied, the client should begin the synchronization process again, starting from the snapshot version. diff --git a/taskchampion/docs/src/images/name_timestamp.png b/taskchampion/docs/src/images/name_timestamp.png deleted file mode 100644 index d829039b72ece8d2cd64cc2b3548ec9376d72ce8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8194 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 diff --git a/taskchampion/docs/src/installation.md b/taskchampion/docs/src/installation.md deleted file mode 100644 index a597a11da..000000000 --- a/taskchampion/docs/src/installation.md +++ /dev/null @@ -1,3 +0,0 @@ -# Installation - -As this is currently in development, installation is by cloning the repository and running "cargo build". diff --git a/taskchampion/docs/src/internals.md b/taskchampion/docs/src/internals.md deleted file mode 100644 index 7cc36ac29..000000000 --- a/taskchampion/docs/src/internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# Internal Details - -The following sections get into the details of how TaskChampion works. -None of this information is necessary to use TaskChampion, but might be helpful in understanding its behavior. -Developers of TaskChampion and of tools that integrate with TaskChampion should be familiar with this information. diff --git a/taskchampion/docs/src/object-store.md b/taskchampion/docs/src/object-store.md deleted file mode 100644 index af6054ad3..000000000 --- a/taskchampion/docs/src/object-store.md +++ /dev/null @@ -1,9 +0,0 @@ -# Object Store Representation - -TaskChampion also supports use of a generic key-value store to synchronize replicas. - -In this case, the salt used in key derivation is a random 16-byte value, stored -in the object store and retrieved as needed. - -The details of the mapping from this protocol to keys and values are private to the implementation. -Other applications should not access the key-value store directly. diff --git a/taskchampion/docs/src/plans.md b/taskchampion/docs/src/plans.md deleted file mode 100644 index 4ee20c5d0..000000000 --- a/taskchampion/docs/src/plans.md +++ /dev/null @@ -1,35 +0,0 @@ -# Planned Functionality - -This section is a bit of a to-do list for additional functionality to add to the synchronzation system. -Each feature has some discussion of how it might be implemented. - -## Snapshots - -As designed, storage required on the server would grow with time, as would the time required for new clients to update to the latest version. -As an optimization, the server also stores "snapshots" containing a full copy of the task database at a given version. -Based on configurable heuristics, it may delete older operations and snapshots, as long as enough data remains for active clients to synchronize and for new clients to initialize. - -Since snapshots must be computed by clients, the server may "request" a snapshot when providing the latest version to a client. -This request comes with a number indicating how much it 'wants" the snapshot. -Clients which can easily generate and transmit a snapshot should be generous to the server, while clients with more limited resources can wait until the server's requests are more desperate. -The intent is, where possible, to request snapshots created on well-connected desktop clients over mobile and low-power clients. - -## Encryption and Signing - -From the server's perspective, all data except for version numbers are opaque binary blobs. -Clients encrypt and sign these blobs using a symmetric key known only to the clients. -This secures the data at-rest on the server. -Note that privacy is not complete, as the server still has some information about users, including source and frequency of synchronization transactions and size of those transactions. - -## Backups - -In this design, the server is little more than an authenticated storage for encrypted blobs provided by the client. -To allow for failure or data loss on the server, clients are expected to cache these blobs locally for a short time (a week), along with a server-provided HMAC signature. -When data loss is detected -- such as when a client expects the server to have a version N or higher, and the server only has N-1, the client can send those blobs to the server. -The server can validate the HMAC and, if successful, add the blobs to its datastore. - -## Expiration - -Deleted tasks remain in the task database, and are simply hidden in most views. -All tasks have an expiration time after which they may be flushed, preventing unbounded increase in task database size. -However, purging of a task does not satisfy the necessary OT guarantees, so some further formal design work is required before this is implemented. diff --git a/taskchampion/docs/src/running-sync-server.md b/taskchampion/docs/src/running-sync-server.md deleted file mode 100644 index e8b8c56ce..000000000 --- a/taskchampion/docs/src/running-sync-server.md +++ /dev/null @@ -1,11 +0,0 @@ -# Running the Sync Server - -> NOTE: TaskChampion is still in development and not yet feature-complete. -> The server is functional, but lacks any administrative features. - -Run `taskchampion-sync-server` to start the sync server. -Use `--port` to specify the port it should listen on, and `--data-dir` to specify the directory which it should store its data. -It only serves HTTP; the expectation is that a frontend proxy will be used for HTTPS support. - -The server has optional parameters `--snapshot-days` and `--snapshot-version`, giving the target number of days and versions, respectively, between snapshots of the client state. -The default values for these parameters are generally adequate. diff --git a/taskchampion/docs/src/snapshots.md b/taskchampion/docs/src/snapshots.md deleted file mode 100644 index 1ca134f34..000000000 --- a/taskchampion/docs/src/snapshots.md +++ /dev/null @@ -1,39 +0,0 @@ -# Snapshots - -The basic synchronization model described in the previous page has a few shortcomings: - * servers must store an ever-increasing quantity of versions - * a new replica must download all versions since the beginning (the nil UUID) in order to derive the current state - -Snapshots allow TaskChampion to avoid both of these issues. -A snapshot is a copy of the task database at a specific version. -It is created by a replica, encrypted, and stored on the server. -A new replica can simply download a recent snapshot and apply any additional versions synchronized since that snapshot was made. -Servers can delete and reclaim space used by older versions, as long as newer snapshots are available. - -## Snapshot Heuristics - -A server implementation must answer a few questions: - * How often should snapshots be made? - * When can versions be deleted? - * When can snapshots be deleted? - -A critical invariant is that at least one snapshot must exist for any database that does not have a child of the nil version. -This ensures that a new replica can always derive the latest state. - -Aside from that invariant, the server implementation can vary in its answers to these questions, with the following considerations: - -Snapshots should be made frequently enough that a new replica can initialize quickly. - -Existing replicas will fail to synchronize if they request a child version that has been deleted. -This failure can cause data loss if the replica had local changes. -It's conceivable that replicas may not sync for weeks or months if, for example, they are located on a home computer while the user is on holiday. - -## Requesting New Snapshots - -The server requests snapshots from replicas, indicating an urgency for the request. -Some replicas, such as those running on PCs or servers, can produce a snapshot even at low urgency. -Other replicas, in more restricted environments such as mobile devices, will only produce a snapshot at high urgency. -This saves resources in these restricted environments. - -A snapshot must be made on a replica with no unsynchronized operations. -As such, it only makes sense to request a snapshot in response to a successful AddVersion request. diff --git a/taskchampion/docs/src/storage.md b/taskchampion/docs/src/storage.md deleted file mode 100644 index f733f4f98..000000000 --- a/taskchampion/docs/src/storage.md +++ /dev/null @@ -1,83 +0,0 @@ -# Replica Storage - -Each replica has a storage backend. -The interface for this backend is given in `crate::taskstorage::Storage` and `StorageTxn`. - -The storage is transaction-protected, with the expectation of a serializable isolation level. -The storage contains the following information: - -- `tasks`: a set of tasks, indexed by UUID -- `base_version`: the number of the last version sync'd from the server (a single integer) -- `operations`: all operations performed since base_version -- `working_set`: a mapping from integer -> UUID, used to keep stable small-integer indexes into the tasks for users' convenience. This data is not synchronized with the server and does not affect any consistency guarantees. - -## Tasks - -The tasks are stored as an un-ordered collection, keyed by task UUID. -Each task in the database has represented by a key-value map. -See [Tasks](./tasks.md) for details on the content of that map. - -## Operations - -Every change to the task database is captured as an operation. -In other words, operations act as deltas between database states. -Operations are crucial to synchronization of replicas, described in [Synchronization Model](./sync-model.md). - -Operations are entirely managed by the replica, and some combinations of operations are described as "invalid" here. -A replica must not create invalid operations, but should be resilient to receiving invalid operations during a synchronization operation. - -Each operation has one of the forms - - * `Create(uuid)` - * `Delete(uuid, oldTask)` - * `Update(uuid, property, oldValue, newValue, timestamp)` - * `UndoPoint()` - -The Create form creates a new task. -It is invalid to create a task that already exists. - -Similarly, the Delete form deletes an existing task. -It is invalid to delete a task that does not exist. -The `oldTask` property contains the task data from before it was deleted. - -The Update form updates the given property of the given task, where the property and values are strings. -The `oldValue` gives the old value of the property (or None to create a new property), while `newValue` gives the new value (or None to delete a property). -It is invalid to update a task that does not exist. -The timestamp on updates serves as additional metadata and is used to resolve conflicts. - -### Application - -Each operation can be "applied" to a task database in a natural way: - - * Applying `Create` creates a new, empty task in the task database. - * Applying `Delete` deletes a task, including all of its properties, from the task database. - * Applying `Update` modifies the properties of a task. - * Applying `UndoPoint` does nothing. - -### Undo - -Each operation also contains enough information to reverse its application: - - * Undoing `Create` deletes a task. - * Undoing `Delete` creates a task, including all of the properties in `oldTask`. - * Undoing `Update` modifies the properties of a task, reverting to `oldValue`. - * Undoing `UndoPoint` does nothing. - -The `UndoPoint` operation serves as a marker of points in the operation sequence to which the user might wish to undo. -For example, creation of a new task with several properities involves several operations, but is a single step from the user's perspective. -An "undo" command reverses operations, removing them from the operations sequence, until it reaches an `UndoPoint` operation. - -### Synchronizing Operations - -After operations are synchronized to the server, they can no longer be undone. -As such, the [synchronization model](./sync-model.md) uses simpler operations. -Replica operations are converted to sync operations as follows: - - * `Create(uuid)` -> `Create(uuid)` (no change) - * `Delete(uuid, oldTask)` -> `Delete(uuid)` - * `Update(uuid, property, oldValue, newValue, timestamp)` -> `Update(uuid, property, newValue, timestamp)` - * `UndoPoint()` -> Ø (dropped from operation sequence) - -Once a sequence of operations has been synchronized, there is no need to store those operations on the replica. -The current implementation deletes operations at that time. -An alternative approach is to keep operations for existing tasks, and provide access to those operations as a "history" of modifications to the task. diff --git a/taskchampion/docs/src/sync-model.md b/taskchampion/docs/src/sync-model.md deleted file mode 100644 index f03626efb..000000000 --- a/taskchampion/docs/src/sync-model.md +++ /dev/null @@ -1,141 +0,0 @@ -# Synchronization Model - -The [task database](./taskdb.md) also implements synchronization. -Synchronization occurs between disconnected replicas, mediated by a server. -The replicas never communicate directly with one another. -The server does not have access to the task data; it sees only opaque blobs of data with a small amount of metadata. - -The synchronization process is a critical part of the task database's functionality, and it cannot function efficiently without occasional synchronization operations - -## Operational Transforms - -Synchronization is based on [operational transformation](https://en.wikipedia.org/wiki/Operational_transformation). -This section will assume some familiarity with the concept. - -## State and Operations - -At a given time, the set of tasks in a replica's storage is the essential "state" of that replica. -All modifications to that state occur via operations, as defined in [Replica Storage](./storage.md). -We can draw a network, or graph, with the nodes representing states and the edges representing operations. -For example: - -```text - o -- State: {abc-d123: 'get groceries', priority L} - | - | -- Operation: set abc-d123 priority to H - | - o -- State: {abc-d123: 'get groceries', priority H} -``` - -For those familiar with distributed version control systems, a state is analogous to a revision, while an operation is analogous to a commit. - -Fundamentally, synchronization involves all replicas agreeing on a single, linear sequence of operations and the state that those operations create. -Since the replicas are not connected, each may have additional operations that have been applied locally, but which have not yet been agreed on. -The synchronization process uses operational transformation to "linearize" those operations. - -This process is analogous (vaguely) to rebasing a sequence of Git commits. -Critically, though, operations cannot merge; in effect, the only option is rebasing. -Furthermore, once an operation has been sent to the server it cannot be changed; in effect, the server does not permit "force push". - -### Sync Operations - -The [Replica Storage](./storage.md) model contains additional information in its operations that is not included in operations synchronized to other replicas. -In this document, we will be discussing "sync operations" of the form - - * `Create(uuid)` - * `Delete(uuid)` - * `Update(uuid, property, value, timestamp)` - - -### Versions - -Occasionally, database states are given a name (that takes the form of a UUID). -The system as a whole (all replicas) constructs a branch-free sequence of versions and the operations that separate each version from the next. -The version with the nil UUID is implicitly the empty database. - -The server stores the operations to change a state from a "parent" version to a "child" version, and provides that information as needed to replicas. -Replicas use this information to update their local task databases, and to generate new versions to send to the server. - -Replicas generate a new version to transmit local changes to the server. -The changes are represented as a sequence of operations with the state resulting from the final operation corresponding to the version. -In order to keep the versions in a single sequence, the server will only accept a proposed version from a replica if its parent version matches the latest version on the server. - -In the non-conflict case (such as with a single replica), then, a replica's synchronization process involves gathering up the operations it has accumulated since its last synchronization; bundling those operations into a version; and sending that version to the server. - -### Replica Invariant - -The replica's [storage](./storage.md) contains the current state in `tasks`, the as-yet un-synchronized operations in `operations`, and the last version at which synchronization occurred in `base_version`. - -The replica's un-synchronized operations are already reflected in its local `tasks`, so the following invariant holds: - -> Applying `operations` to the set of tasks at `base_version` gives a set of tasks identical -> to `tasks`. - -### Transformation - -When the latest version on the server contains operations that are not present in the replica, then the states have diverged. -For example: - -```text - o -- version N - w|\a - o o - x| \b - o o - y| \c - o o -- replica's local state - z| - o -- version N+1 -``` - -(diagram notation: `o` designates a state, lower-case letters designate operations, and versions are presented as if they were numbered sequentially) - -In this situation, the replica must "rebase" the local operations onto the latest version from the server and try again. -This process is performed using operational transformation (OT). -The result of this transformation is a sequence of operations based on the latest version, and a sequence of operations the replica can apply to its local task database to reach the same state -Continuing the example above, the resulting operations are shown with `'`: - -```text - o -- version N - w|\a - o o - x| \b - o o - y| \c - o o -- replica's intermediate local state - z| |w' - o-N+1 o - a'\ |x' - o o - b'\ |y' - o o - c'\|z' - o -- version N+2 -``` - -The replica applies w' through z' locally, and sends a' through c' to the server as the operations to generate version N+2. -Either path through this graph, a-b-c-w'-x'-y'-z' or a'-b'-c'-w-x-y-z, must generate *precisely* the same final state at version N+2. -Careful selection of the operations and the transformation function ensure this. - -See the comments in the source code for the details of how this transformation process is implemented. - -## Synchronization Process - -To perform a synchronization, the replica first requests the child version of `base_version` from the server (GetChildVersion). -It applies that version to its local `tasks`, rebases its local `operations` as described above, and updates `base_version`. -The replica repeats this process until the server indicates no additional child versions exist. -If there are no un-synchronized local operations, the process is complete. - -Otherwise, the replica creates a new version containing its local operations, giving its `base_version` as the parent version, and transmits that to the server (AddVersion). -In most cases, this will succeed, but if another replica has created a new version in the interim, then the new version will conflict with that other replica's new version and the server will respond with the new expected parent version. -In this case, the process repeats. -If the server indicates a conflict twice with the same expected base version, that is an indication that the replica has diverged (something serious has gone wrong). - -## Servers - -A replica depends on periodic synchronization for performant operation. -Without synchronization, its list of pending operations would grow indefinitely, and tasks could never be expired. -So all replicas, even "singleton" replicas which do not replicate task data with any other replica, must synchronize periodically. - -TaskChampion provides a `LocalServer` for this purpose. -It implements the `get_child_version` and `add_version` operations as described, storing data on-disk locally. diff --git a/taskchampion/docs/src/sync-protocol.md b/taskchampion/docs/src/sync-protocol.md deleted file mode 100644 index 98caf24e3..000000000 --- a/taskchampion/docs/src/sync-protocol.md +++ /dev/null @@ -1,115 +0,0 @@ -# Server-Replica Protocol - -The server-replica protocol is defined abstractly in terms of request/response transactions. - -The protocol builds on the model presented in the previous chapters, and in particular on the synchronization process. - -## Clients - -From the protocol's perspective, replicas accessing the same task history are indistinguishable, so this protocol uses the term "client" to refer generically to all replicas replicating a single task history. - -## Server - -A server implements the requests and responses described below. -Where the logic is implemented depends on the specific implementation of the protocol. - -For each client, the server is responsible for storing the task history, in the form of a branch-free sequence of versions. -It also stores the latest snapshot, if any exists. -From the server's perspective, snapshots and versions are opaque byte sequences. - -## Version Invariant - -The following invariant must always hold: - -> All versions are linked by parent-child relationships to form a single chain. -> That is, each version must have no more than one parent and one child, and no more than one version may have zero parents or zero children. - -## Data Formats - -Task data sent to the server is encrypted by the client, using the scheme described in the "Encryption" chapter. - -### Version - -The decrypted form of a version is a JSON array containing operations in the order they should be applied. -Each operation has the form `{TYPE: DATA}`, for example: - - * `[{"Create":{"uuid":"56e0be07-c61f-494c-a54c-bdcfdd52d2a7"}}]` - * `[{"Delete":{"uuid":"56e0be07-c61f-494c-a54c-bdcfdd52d2a7"}}]` - * `[{"Update":{"uuid":"56e0be07-c61f-494c-a54c-bdcfdd52d2a7","property":"prop","value":"v","timestamp":"2021-10-11T12:47:07.188090948Z"}}]` - * `[{"Update":{"uuid":"56e0be07-c61f-494c-a54c-bdcfdd52d2a7","property":"prop","value":null,"timestamp":"2021-10-11T12:47:07.188090948Z"}}]` (to delete a property) - -Timestamps are in RFC3339 format with a `Z` suffix. - -### Snapshot - -The decrypted form of a snapshot is a JSON object mapping task IDs to task properties. -For example (pretty-printed for clarity): - -```json -{ - "56e0be07-c61f-494c-a54c-bdcfdd52d2a7": { - "description": "a task", - "priority": "H" - }, - "4b7ed904-f7b0-4293-8a10-ad452422c7b3": { - "description": "another task" - } -} -``` - -## Transactions - -All interactions between the client and server are defined in terms of request/response transactions, as described here. - -### AddVersion - -The AddVersion transaction requests that the server add a new version to the client's task history. -The request contains the following; - - * parent version ID, and - * encrypted version data. - -The server determines whether the new version is acceptable, atomically with respect to other requests for the same client. -If it has no versions for the client, it accepts the version. -If it already has one or more versions for the client, then it accepts the version only if the given parent version has no children, thereby maintaining the version invariant. - -If the version is accepted, the server generates a new version ID for it. -The version is added to the chain of versions for the client, and the new version ID is returned in the response to the client. -The response may also include a request for a snapshot, with associated urgency. - -If the version is not accepted, the server makes no changes, but responds to the client with a conflict indication containing the ID of the version which has no children. -The client may then "rebase" its operations and try again. -Note that if a client receives two conflict responses with the same parent version ID, it is an indication that the client's version history has diverged from that on the server. - -### GetChildVersion - -The GetChildVersion transaction is a read-only request for a version. -The request consists of a parent version ID. -The server searches its set of versions for a version with the given parent ID. -If found, it returns the version's - - * version ID, - * parent version ID (matching that in the request), and - * encrypted version data. - -If not found, it returns an indication that no such version exists. - -### AddSnapshot - -The AddSnapshot transaction requests that the server store a new snapshot, generated by the client. -The request contains the following: - - * version ID at which the snapshot was made, and - * encrypted snapshot data. - -The server may validate that the snapshot is for an existing version and is newer than any existing snapshot. -It may also validate that the snapshot is for a "recent" version (e.g., one of the last 5 versions). -If a snapshot already exists for the given version, the server may keep or discard the new snapshot but should return a success indication to the client. - -The server response is empty. - -### GetSnapshot - -The GetSnapshot transaction requests that the server provide the latest snapshot. -The response contains the snapshot version ID and the snapshot data, if those exist. - diff --git a/taskchampion/docs/src/sync.md b/taskchampion/docs/src/sync.md deleted file mode 100644 index fed75d17f..000000000 --- a/taskchampion/docs/src/sync.md +++ /dev/null @@ -1,7 +0,0 @@ -# Synchronization and the Sync Server - -This section covers *synchronization* of *replicas* containing the same set of tasks. -A replica is can perform all operations locally without connecting to a sync server, then share those operations with other replicas when it connects. -Sync is a critical feature of TaskChampion, allowing users to consult and update the same task list on multiple devices, without requiring constant connection. - -This is a complex topic, and the section is broken into several chapters, beginning at the lower levels of the implementation and working up. diff --git a/taskchampion/docs/src/taskdb.md b/taskchampion/docs/src/taskdb.md deleted file mode 100644 index b70e9bf3c..000000000 --- a/taskchampion/docs/src/taskdb.md +++ /dev/null @@ -1,32 +0,0 @@ -# Task Database - -The task database is a layer of abstraction above the replica storage layer, responsible for maintaining some important invariants. -While the storage is pluggable, there is only one implementation of the task database. - -## Reading Data - -The task database provides read access to the data in the replica's storage through a variety of methods on the struct. -Each read operation is executed in a transaction, so data may not be consistent between read operations. -In practice, this is not an issue for TaskChampion's purposes. - -## Working Set - -The task database maintains the working set. -The working set maps small integers to current tasks, for easy reference by command-line users. -This is done in such a way that the task numbers remain stable until the working set is rebuilt, at which point gaps in the numbering, such as for completed tasks, are removed by shifting all higher-numbered tasks downward. - -The working set is not replicated, and is not considered a part of any consistency guarantees in the task database. - -## Modifying Data - -Modifications to the data set are made by applying operations. -Operations are described in [Replica Storage](./storage.md). - -Each operation is added to the list of operations in the storage, and simultaneously applied to the tasks in that storage. -Operations are checked for validity as they are applied. - -## Deletion and Expiration - -Deletion of a task merely changes the task's status to "deleted", leaving it in the Task database. -Actual removal of tasks from the task database takes place as part of _expiration_, triggered by the user as part of a garbage-collection process. -Expiration removes tasks with a `modified` property more than 180 days in the past, by creating a `Delete(uuid)` operation. diff --git a/taskchampion/docs/src/tasks.md b/taskchampion/docs/src/tasks.md deleted file mode 100644 index bac90cc9d..000000000 --- a/taskchampion/docs/src/tasks.md +++ /dev/null @@ -1,58 +0,0 @@ -# Tasks - -Tasks are stored internally as a key/value map with string keys and values. -All fields are optional: the `Create` operation creates an empty task. -Display layers should apply appropriate defaults where necessary. - -## Atomicity - -The synchronization process does not support read-modify-write operations. -For example, suppose tags are updated by reading a list of tags, adding a tag, and writing the result back. -This would be captured as an `Update` operation containing the amended list of tags. -Suppose two such `Update` operations are made in different replicas and must be reconciled: - * `Update("d394be59-60e6-499e-b7e7-ca0142648409", "tags", "oldtag,newtag1", "2020-11-23T14:21:22Z")` - * `Update("d394be59-60e6-499e-b7e7-ca0142648409", "tags", "oldtag,newtag2", "2020-11-23T15:08:57Z")` - -The result of this reconciliation will be `oldtag,newtag2`, while the user almost certainly intended `oldtag,newtag1,newtag2`. - -The key names given below avoid this issue, allowing user updates such as adding a tag or deleting a dependency to be represented in a single `Update` operation. - -## Validity - -_Any_ key/value map is a valid task. -Consumers of task data must make a best effort to interpret any map, even if it contains apparently contradictory information. -For example, a task with status "completed" but no "end" key present should be interpreted as completed at an unknown time. - -## Representations - -Integers are stored in decimal notation. - -Timestamps are stored as UNIX epoch timestamps, in the form of an integer. - -## Keys - -The following keys, and key formats, are defined: - -* `status` - one of `P` for a pending task (the default), `C` for completed, `D` for deleted, or `R` for recurring -* `description` - the one-line summary of the task -* `modified` - the time of the last modification of this task -* `start` - the most recent time at which this task was started (a task with no `start` key is not active) -* `end` - if present, the time at which this task was completed or deleted (note that this key may not agree with `status`: it may be present for a pending task, or absent for a deleted or completed task) -* `tag_` - indicates this task has tag `` (value is ignored) -* `wait` - indicates the time before which this task should be hidden, as it is not actionable -* `entry` - the time at which the task was created -* `annotation_` - value is an annotation created at the given time; for example, `annotation_1693329505`. -* `dep_` - indicates this task depends on another task identified by ``; the value is ignored; for example, `dep_8c4fed9c-c0d2-40c2-936d-36fc44e084a0` - -Note that while TaskChampion recognizes "recurring" as a status, it does not implement recurrence directly. - -### UDAs - -Any unrecognized keys are treated as "user-defined attributes" (UDAs). -These attributes can be used to store additional data associated with a task. -For example, applications that synchronize tasks with other systems such as calendars or team planning services might store unique identifiers for those systems as UDAs. -The application defining a UDA defines the format of the value. - -UDAs _should_ have a namespaced structure of the form `.`, where `` identifies the application defining the UDA. -For example, a service named "DevSync" synchronizing tasks from GitHub might use UDAs like `devsync.github.issue-id`. -Note that many existing UDAs for Taskwarrior integrations do not follow this pattern; these are referred to as legacy UDAs. diff --git a/taskchampion/integration-tests/.gitignore b/taskchampion/integration-tests/.gitignore deleted file mode 100644 index 140a35ffe..000000000 --- a/taskchampion/integration-tests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -test-db -test-sync-server diff --git a/taskchampion/integration-tests/Cargo.toml b/taskchampion/integration-tests/Cargo.toml deleted file mode 100644 index 217961394..000000000 --- a/taskchampion/integration-tests/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "integration-tests" -version = "0.4.1" -authors = ["Dustin J. Mitchell "] -edition = "2021" -publish = false -build = "build.rs" - -[dependencies] -taskchampion = { path = "../taskchampion" } -taskchampion-lib = { path = "../lib" } - -[dev-dependencies] -anyhow.workspace = true -tempfile.workspace = true -pretty_assertions.workspace = true -lazy_static.workspace = true - -[build-dependencies] -cc.workspace = true diff --git a/taskchampion/integration-tests/README.md b/taskchampion/integration-tests/README.md deleted file mode 100644 index 34a308aa5..000000000 --- a/taskchampion/integration-tests/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Integration Tests for TaskChampion - -## "Regular" Tests - -Some of the tests in `tests/` are just regular integration tests. -Nothing exciting to see. - -## Bindings Tests - -The bindings tests are a bit more interesting, since they are written in C. -They are composed of a collection of "suites", each in one C file in `integration-tests/src/bindings_tests/`. -Each suite contains a number of tests (using [Unity](http://www.throwtheswitch.org/unity)) and an exported function named after the suite that returns an exit status (1 = failure). - -The build script (`integration-tests/build.rs`) builds these files into a library that is linked with the `integration_tests` library crate. -This crate contains a `bindings_tests` module with a pub function for each suite. - -Finally, the `integration-tests/tests/bindings.rs` test file calls each of those functions in a separate test case. - -### Adding Tests - -To add a test, select a suite and add a new test-case function. -Add a `RUN_TEST` invocation for your new function to the `.._tests` function at the bottom. -Keep the `RUN_TEST`s in the same order as the functions they call. - -### Adding Suites - -To add a suite, - -1. Add a new C file in `integration-tests/src/bindings_tests/`, based off of one of the others. -1. Add a the suite name to `suites` in `integration-tests/build.rs`. diff --git a/taskchampion/integration-tests/build.rs b/taskchampion/integration-tests/build.rs deleted file mode 100644 index dd10a997c..000000000 --- a/taskchampion/integration-tests/build.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::env; -use std::fs; -use std::path::Path; - -/// Build the Unity-based C test suite in `src/bindings_tests`, linking the result with this -/// package's library crate. -fn build_bindings_tests(suites: &[&'static str]) { - let mut build = cc::Build::new(); - build.include("../lib"); // include path for taskchampion.h - build.include("src/bindings_tests/unity"); - build.define("UNITY_OUTPUT_CHAR", "test_output"); - build.define( - "UNITY_OUTPUT_CHAR_HEADER_DECLARATION", - "test_output(char c)", - ); - - let mut files = vec![ - "src/bindings_tests/test.c".into(), - "src/bindings_tests/unity/unity.c".into(), - ]; - for suite in suites { - files.push(format!("src/bindings_tests/{}.c", suite)); - } - for file in files { - build.file(&file); - println!("cargo:rerun-if-changed={}", file); - } - println!("cargo:rerun-if-changed=../lib/taskchampion.h"); - - build.compile("bindings-tests"); -} - -/// Make `bindings_test_suites.rs` listing all of the test suites, for use in building the -/// bindings-test binary. -fn make_suite_file(suites: &[&'static str]) { - let out_dir = env::var_os("OUT_DIR").unwrap(); - let dest_path = Path::new(&out_dir).join("bindings_test_suites.rs"); - let mut content = String::new(); - for suite in suites { - content.push_str(format!("suite!({}_tests);\n", suite).as_ref()); - } - fs::write(dest_path, content).unwrap(); -} - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let suites = &["uuid", "string", "task", "replica"]; - build_bindings_tests(suites); - make_suite_file(suites); -} diff --git a/taskchampion/integration-tests/src/bindings_tests/mod.rs b/taskchampion/integration-tests/src/bindings_tests/mod.rs deleted file mode 100644 index 327ca83fe..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::fs; - -extern "C" { - // set up to send test output to TEST-OUTPUT - fn setup_output(); - // close the output file - fn finish_output(); -} - -// Each suite is represented by a _tests C function in .c. -// All of these C files are built into a library that is linked to the crate -- but not to test -// crates. So, this macro produces a "glue function" that calls the C function, and that can be -// called from test crates. -macro_rules! suite( - { $s:ident } => { - pub fn $s() -> (i32, String) { - extern "C" { - fn $s() -> i32; - } - unsafe { setup_output() }; - let res = unsafe { $s() }; - unsafe { finish_output() }; - let output = fs::read_to_string("TEST-OUTPUT") - .unwrap_or_else(|e| format!("could not open TEST-OUTPUT: {}", e)); - (res, output) - } - }; -); - -include!(concat!(env!("OUT_DIR"), "/bindings_test_suites.rs")); diff --git a/taskchampion/integration-tests/src/bindings_tests/replica.c b/taskchampion/integration-tests/src/bindings_tests/replica.c deleted file mode 100644 index 0126791ff..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/replica.c +++ /dev/null @@ -1,330 +0,0 @@ -#include -#include -#include -#include "taskchampion.h" -#include "unity.h" - -// creating an in-memory replica does not crash -static void test_replica_creation(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NOT_NULL(rep); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - tc_replica_free(rep); -} - -// creating an on-disk replica does not crash -static void test_replica_creation_disk(void) { - TCReplica *rep = tc_replica_new_on_disk(tc_string_borrow("test-db"), true, NULL); - TEST_ASSERT_NOT_NULL(rep); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - tc_replica_free(rep); -} - -// undo on an empty in-memory TCReplica does nothing -static void test_replica_undo_empty(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - int undone; - TCReplicaOpList undo_ops = tc_replica_get_undo_ops(rep); - int rv = tc_replica_commit_undo_ops(rep, undo_ops, &undone); - TEST_ASSERT_EQUAL(TC_RESULT_OK, rv); - TEST_ASSERT_EQUAL(0, undone); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - tc_replica_free(rep); -} - -// adding an undo point succeeds -static void test_replica_add_undo_point(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_replica_add_undo_point(rep, true)); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - tc_replica_free(rep); -} - -// working set operations succeed -static void test_replica_working_set(void) { - TCWorkingSet *ws; - TCTask *task1, *task2, *task3; - TCUuid uuid, uuid1, uuid2, uuid3; - - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_replica_rebuild_working_set(rep, true)); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - ws = tc_replica_working_set(rep); - TEST_ASSERT_EQUAL(0, tc_working_set_len(ws)); - tc_working_set_free(ws); - - task1 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("task1")); - TEST_ASSERT_NOT_NULL(task1); - uuid1 = tc_task_get_uuid(task1); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_replica_rebuild_working_set(rep, true)); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - task2 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("task2")); - TEST_ASSERT_NOT_NULL(task2); - uuid2 = tc_task_get_uuid(task2); - - task3 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("task3")); - TEST_ASSERT_NOT_NULL(task3); - uuid3 = tc_task_get_uuid(task3); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_replica_rebuild_working_set(rep, false)); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - // finish task2 to leave a "hole" - tc_task_to_mut(task2, rep); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_done(task2)); - tc_task_to_immut(task2); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_replica_rebuild_working_set(rep, false)); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - tc_task_free(task1); - tc_task_free(task2); - tc_task_free(task3); - - // working set should now be - // 0 -> None - // 1 -> uuid1 - // 2 -> None - // 3 -> uuid3 - ws = tc_replica_working_set(rep); - TEST_ASSERT_EQUAL(2, tc_working_set_len(ws)); - TEST_ASSERT_EQUAL(3, tc_working_set_largest_index(ws)); - - TEST_ASSERT_FALSE(tc_working_set_by_index(ws, 0, &uuid)); - TEST_ASSERT_TRUE(tc_working_set_by_index(ws, 1, &uuid)); - TEST_ASSERT_EQUAL_MEMORY(uuid1.bytes, uuid.bytes, sizeof(uuid)); - TEST_ASSERT_FALSE(tc_working_set_by_index(ws, 2, &uuid)); - TEST_ASSERT_TRUE(tc_working_set_by_index(ws, 3, &uuid)); - TEST_ASSERT_EQUAL_MEMORY(uuid3.bytes, uuid.bytes, sizeof(uuid)); - - TEST_ASSERT_EQUAL(1, tc_working_set_by_uuid(ws, uuid1)); - TEST_ASSERT_EQUAL(0, tc_working_set_by_uuid(ws, uuid2)); - TEST_ASSERT_EQUAL(3, tc_working_set_by_uuid(ws, uuid3)); - - tc_working_set_free(ws); - - TEST_ASSERT_EQUAL(18, tc_replica_num_local_operations(rep)); - - tc_replica_free(rep); -} - -// When tc_replica_commit_undo_ops is passed NULL for undone_out, it still succeeds -static void test_replica_undo_empty_null_undone_out(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - TCReplicaOpList undo_ops = tc_replica_get_undo_ops(rep); - int rv = tc_replica_commit_undo_ops(rep, undo_ops, NULL); - TEST_ASSERT_EQUAL(TC_RESULT_OK, rv); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - tc_replica_free(rep); -} - -// creating a task succeeds and the resulting task looks good -static void test_replica_task_creation(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - TCUuid uuid = tc_task_get_uuid(task); - TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); - - TCString desc = tc_task_get_description(task); - TEST_ASSERT_NOT_NULL(desc.ptr); - TEST_ASSERT_EQUAL_STRING("my task", tc_string_content(&desc)); - tc_string_free(&desc); - - tc_task_free(task); - - // get the task again and verify it - task = tc_replica_get_task(rep, uuid); - TEST_ASSERT_NOT_NULL(task); - TEST_ASSERT_EQUAL_MEMORY(uuid.bytes, tc_task_get_uuid(task).bytes, sizeof(uuid.bytes)); - TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); - - tc_task_free(task); - - tc_replica_free(rep); -} - -static void test_replica_sync_local(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - mkdir("test-sync-server", 0755); // ignore error, if dir already exists - - TCString err; - TCServer *server = tc_server_new_local(tc_string_borrow("test-sync-server"), &err); - TEST_ASSERT_NOT_NULL(server); - TEST_ASSERT_NULL(err.ptr); - - int rv = tc_replica_sync(rep, server, false); - TEST_ASSERT_EQUAL(TC_RESULT_OK, rv); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - tc_server_free(server); - tc_replica_free(rep); - - // test error handling - server = tc_server_new_local(tc_string_borrow("/no/such/directory"), &err); - TEST_ASSERT_NULL(server); - TEST_ASSERT_NOT_NULL(err.ptr); - tc_string_free(&err); -} - -static void test_replica_remote_server(void) { - TCString err; - TCServer *server = tc_server_new_sync( - tc_string_borrow("http://tc.freecinc.com"), - tc_uuid_new_v4(), - tc_string_borrow("\xf0\x28\x8c\x28"), // NOTE: not utf-8 - &err); - TEST_ASSERT_NOT_NULL(server); - TEST_ASSERT_NULL(err.ptr); - - // can't actually do anything with this server! - - tc_server_free(server); -} - -// a replica with tasks in it returns an appropriate list of tasks and list of uuids -static void test_replica_all_tasks(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task1 = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("task1")); - TEST_ASSERT_NOT_NULL(task1); - TCUuid uuid1 = tc_task_get_uuid(task1); - tc_task_free(task1); - - TCTask *task2 = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("task2")); - TEST_ASSERT_NOT_NULL(task2); - TCUuid uuid2 = tc_task_get_uuid(task2); - tc_task_free(task2); - - { - TCTaskList tasks = tc_replica_all_tasks(rep); - TEST_ASSERT_NOT_NULL(tasks.items); - TEST_ASSERT_EQUAL(2, tasks.len); - - bool seen1, seen2 = false; - for (size_t i = 0; i < tasks.len; i++) { - TCTask *task = tasks.items[i]; - TCString descr = tc_task_get_description(task); - if (0 == strcmp(tc_string_content(&descr), "task1")) { - seen1 = true; - } else if (0 == strcmp(tc_string_content(&descr), "task2")) { - seen2 = true; - } - tc_string_free(&descr); - } - TEST_ASSERT_TRUE(seen1); - TEST_ASSERT_TRUE(seen2); - - tc_task_list_free(&tasks); - TEST_ASSERT_NULL(tasks.items); - } - - { - TCUuidList uuids = tc_replica_all_task_uuids(rep); - TEST_ASSERT_NOT_NULL(uuids.items); - TEST_ASSERT_EQUAL(2, uuids.len); - - bool seen1, seen2 = false; - for (size_t i = 0; i < uuids.len; i++) { - TCUuid uuid = uuids.items[i]; - if (0 == memcmp(&uuid1, &uuid, sizeof(TCUuid))) { - seen1 = true; - } else if (0 == memcmp(&uuid2, &uuid, sizeof(TCUuid))) { - seen2 = true; - } - } - TEST_ASSERT_TRUE(seen1); - TEST_ASSERT_TRUE(seen2); - - tc_uuid_list_free(&uuids); - TEST_ASSERT_NULL(uuids.items); - } - - tc_replica_free(rep); -} - -// importing a task succeeds and the resulting task looks good -static void test_replica_task_import(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCUuid uuid; - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_uuid_from_str(tc_string_borrow("23cb25e0-5d1a-4932-8131-594ac6d3a843"), &uuid)); - TCTask *task = tc_replica_import_task_with_uuid(rep, uuid); - TEST_ASSERT_NOT_NULL(task); - - TEST_ASSERT_EQUAL_MEMORY(uuid.bytes, tc_task_get_uuid(task).bytes, sizeof(uuid.bytes)); - TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); - - TCString desc = tc_task_get_description(task); - TEST_ASSERT_NOT_NULL(desc.ptr); - TEST_ASSERT_EQUAL_STRING("", tc_string_content(&desc)); // default value - tc_string_free(&desc); - - tc_task_free(task); - - // get the task again and verify it - task = tc_replica_get_task(rep, uuid); - TEST_ASSERT_NOT_NULL(task); - TEST_ASSERT_EQUAL_MEMORY(uuid.bytes, tc_task_get_uuid(task).bytes, sizeof(uuid.bytes)); - TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); - - tc_task_free(task); - - tc_replica_free(rep); -} - -// importing a task succeeds and the resulting task looks good -static void test_replica_get_task_not_found(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCUuid uuid; - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_uuid_from_str(tc_string_borrow("23cb25e0-5d1a-4932-8131-594ac6d3a843"), &uuid)); - TCTask *task = tc_replica_get_task(rep, uuid); - TEST_ASSERT_NULL(task); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - tc_replica_free(rep); -} - -int replica_tests(void) { - UNITY_BEGIN(); - // each test case above should be named here, in order. - RUN_TEST(test_replica_creation); - RUN_TEST(test_replica_creation_disk); - RUN_TEST(test_replica_undo_empty); - RUN_TEST(test_replica_add_undo_point); - RUN_TEST(test_replica_working_set); - RUN_TEST(test_replica_undo_empty_null_undone_out); - RUN_TEST(test_replica_task_creation); - RUN_TEST(test_replica_sync_local); - RUN_TEST(test_replica_remote_server); - RUN_TEST(test_replica_all_tasks); - RUN_TEST(test_replica_task_import); - RUN_TEST(test_replica_get_task_not_found); - return UNITY_END(); -} diff --git a/taskchampion/integration-tests/src/bindings_tests/string.c b/taskchampion/integration-tests/src/bindings_tests/string.c deleted file mode 100644 index 2bd2749c0..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/string.c +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include "unity.h" -#include "taskchampion.h" - -// creating strings does not crash -static void test_string_creation(void) { - TCString s = tc_string_borrow("abcdef"); - tc_string_free(&s); - TEST_ASSERT_NULL(s.ptr); -} - -// creating cloned strings does not crash -static void test_string_cloning(void) { - char *abcdef = strdup("abcdef"); - TCString s = tc_string_clone(abcdef); - TEST_ASSERT_NOT_NULL(s.ptr); - free(abcdef); - - TEST_ASSERT_EQUAL_STRING("abcdef", tc_string_content(&s)); - tc_string_free(&s); - TEST_ASSERT_NULL(s.ptr); -} - -// creating cloned strings with invalid utf-8 does not crash -// ..but content is NULL and content_and_len returns the value -static void test_string_cloning_invalid_utf8(void) { - TCString s = tc_string_clone("\xf0\x28\x8c\x28"); - TEST_ASSERT_NOT_NULL(s.ptr); - - // NOTE: this is not one of the cases where invalid UTF-8 results in NULL, - // but that may change. - - size_t len; - const char *buf = tc_string_content_with_len(&s, &len); - TEST_ASSERT_NOT_NULL(buf); - TEST_ASSERT_EQUAL(4, len); - TEST_ASSERT_EQUAL_MEMORY("\xf0\x28\x8c\x28", buf, len); - - tc_string_free(&s); - TEST_ASSERT_NULL(s.ptr); -} - -// borrowed strings echo back their content -static void test_string_borrowed_strings_echo(void) { - TCString s = tc_string_borrow("abcdef"); - TEST_ASSERT_NOT_NULL(s.ptr); - - TEST_ASSERT_EQUAL_STRING("abcdef", tc_string_content(&s)); - - size_t len; - const char *buf = tc_string_content_with_len(&s, &len); - TEST_ASSERT_NOT_NULL(buf); - TEST_ASSERT_EQUAL(6, len); - TEST_ASSERT_EQUAL_MEMORY("abcdef", buf, len); - - tc_string_free(&s); - TEST_ASSERT_NULL(s.ptr); -} - -// cloned strings echo back their content -static void test_string_cloned_strings_echo(void) { - char *orig = strdup("abcdef"); - TCString s = tc_string_clone(orig); - TEST_ASSERT_NOT_NULL(s.ptr); - free(orig); - - TEST_ASSERT_EQUAL_STRING("abcdef", tc_string_content(&s)); - - size_t len; - const char *buf = tc_string_content_with_len(&s, &len); - TEST_ASSERT_NOT_NULL(buf); - TEST_ASSERT_EQUAL(6, len); - TEST_ASSERT_EQUAL_MEMORY("abcdef", buf, len); - - tc_string_free(&s); - TEST_ASSERT_NULL(s.ptr); -} - -// tc_clone_with_len can have NULs, and tc_string_content returns NULL for -// strings containing embedded NULs -static void test_string_content_null_for_embedded_nuls(void) { - TCString s = tc_string_clone_with_len("ab\0de", 5); - TEST_ASSERT_NOT_NULL(s.ptr); - - TEST_ASSERT_NULL(tc_string_content(&s)); - - size_t len; - const char *buf = tc_string_content_with_len(&s, &len); - TEST_ASSERT_NOT_NULL(buf); - TEST_ASSERT_EQUAL(5, len); - TEST_ASSERT_EQUAL_MEMORY("ab\0de", buf, len); - tc_string_free(&s); - TEST_ASSERT_NULL(s.ptr); -} - -// tc_string_clone_with_len will accept invalid utf-8, but then tc_string_content -// returns NULL. -static void test_string_clone_with_len_invalid_utf8(void) { - TCString s = tc_string_clone_with_len("\xf0\x28\x8c\x28", 4); - TEST_ASSERT_NOT_NULL(s.ptr); - - TEST_ASSERT_NULL(tc_string_content(&s)); - - size_t len; - const char *buf = tc_string_content_with_len(&s, &len); - TEST_ASSERT_NOT_NULL(buf); - TEST_ASSERT_EQUAL(4, len); - TEST_ASSERT_EQUAL_MEMORY("\xf0\x28\x8c\x28", buf, len); - tc_string_free(&s); - TEST_ASSERT_NULL(s.ptr); -} - -int string_tests(void) { - UNITY_BEGIN(); - // each test case above should be named here, in order. - RUN_TEST(test_string_creation); - RUN_TEST(test_string_cloning); - RUN_TEST(test_string_cloning_invalid_utf8); - RUN_TEST(test_string_borrowed_strings_echo); - RUN_TEST(test_string_cloned_strings_echo); - RUN_TEST(test_string_content_null_for_embedded_nuls); - RUN_TEST(test_string_clone_with_len_invalid_utf8); - return UNITY_END(); -} diff --git a/taskchampion/integration-tests/src/bindings_tests/task.c b/taskchampion/integration-tests/src/bindings_tests/task.c deleted file mode 100644 index 828775468..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/task.c +++ /dev/null @@ -1,717 +0,0 @@ -#include -#include -#include "unity.h" -#include "taskchampion.h" - -// creating a task succeeds and the resulting task looks good -static void test_task_creation(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); - - TCString desc = tc_task_get_description(task); - TEST_ASSERT_NOT_NULL(desc.ptr); - TEST_ASSERT_EQUAL_STRING("my task", tc_string_content(&desc)); - tc_string_free(&desc); - - tc_task_free(task); - - tc_replica_free(rep); -} - -// freeing a mutable task works, marking it immutable -static void test_task_free_mutable_task(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); - TCUuid uuid = tc_task_get_uuid(task); - - tc_task_to_mut(task, rep); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_status(task, TC_STATUS_DELETED)); - TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); - - tc_task_free(task); // implicitly converts to immut - - task = tc_replica_get_task(rep, uuid); - TEST_ASSERT_NOT_NULL(task); - TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); - tc_task_free(task); - - tc_replica_free(rep); -} - -// updating status on a task works -static void test_task_get_set_status(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); - - tc_task_to_mut(task, rep); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_status(task, TC_STATUS_DELETED)); - TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); // while mut - tc_task_to_immut(task); - TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); // while immut - - tc_task_free(task); - - tc_replica_free(rep); -} - -// updating description on a task works -static void test_task_get_set_description(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - TCString desc; - - tc_task_to_mut(task, rep); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_description(task, tc_string_borrow("updated"))); - - desc = tc_task_get_description(task); - TEST_ASSERT_NOT_NULL(desc.ptr); - TEST_ASSERT_EQUAL_STRING("updated", tc_string_content(&desc)); - tc_string_free(&desc); - - tc_task_to_immut(task); - - desc = tc_task_get_description(task); - TEST_ASSERT_NOT_NULL(desc.ptr); - TEST_ASSERT_EQUAL_STRING("updated", tc_string_content(&desc)); - tc_string_free(&desc); - - tc_task_free(task); - - tc_replica_free(rep); -} - -// updating arbitrary attributes on a task works -static void test_task_get_set_attribute(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - TCString foo; - - foo = tc_task_get_value(task, tc_string_borrow("foo")); - TEST_ASSERT_NULL(foo.ptr); - - tc_task_to_mut(task, rep); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_value(task, - tc_string_borrow("foo"), - tc_string_borrow("updated"))); - - foo = tc_task_get_value(task, tc_string_borrow("foo")); - TEST_ASSERT_NOT_NULL(foo.ptr); - TEST_ASSERT_EQUAL_STRING("updated", tc_string_content(&foo)); - tc_string_free(&foo); - - tc_task_to_immut(task); - - foo = tc_task_get_value(task, tc_string_borrow("foo")); - TEST_ASSERT_NOT_NULL(foo.ptr); - TEST_ASSERT_EQUAL_STRING("updated", tc_string_content(&foo)); - tc_string_free(&foo); - - TCString null = { .ptr = NULL }; - - tc_task_to_mut(task, rep); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_value(task, - tc_string_borrow("foo"), - null)); - - foo = tc_task_get_value(task, tc_string_borrow("foo")); - TEST_ASSERT_NULL(foo.ptr); - - tc_task_free(task); - - tc_replica_free(rep); -} - -// updating entry on a task works -static void test_task_get_set_entry(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - // creation of a task sets entry to current time - TEST_ASSERT_NOT_EQUAL(0, tc_task_get_entry(task)); - - tc_task_to_mut(task, rep); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_entry(task, 1643679997)); - TEST_ASSERT_EQUAL(1643679997, tc_task_get_entry(task)); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_entry(task, 0)); - TEST_ASSERT_EQUAL(0, tc_task_get_entry(task)); - - tc_task_free(task); - - tc_replica_free(rep); -} - -// updating wait on a task works -static void test_task_get_set_wait_and_is_waiting(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - // wait is not set on creation - TEST_ASSERT_EQUAL(0, tc_task_get_wait(task)); - TEST_ASSERT_FALSE(tc_task_is_waiting(task)); - - tc_task_to_mut(task, rep); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_wait(task, 3643679997)); // 2085 - TEST_ASSERT_EQUAL(3643679997, tc_task_get_wait(task)); - TEST_ASSERT_TRUE(tc_task_is_waiting(task)); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_wait(task, 643679997)); // THE PAST! - TEST_ASSERT_EQUAL(643679997, tc_task_get_wait(task)); - TEST_ASSERT_FALSE(tc_task_is_waiting(task)); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_wait(task, 0)); - TEST_ASSERT_EQUAL(0, tc_task_get_wait(task)); - TEST_ASSERT_FALSE(tc_task_is_waiting(task)); - - tc_task_free(task); - - tc_replica_free(rep); -} - -// updating modified on a task works -static void test_task_get_set_modified(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - // creation of a task sets modified to current time - TEST_ASSERT_NOT_EQUAL(0, tc_task_get_modified(task)); - - tc_task_to_mut(task, rep); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_modified(task, 1643679997)); - TEST_ASSERT_EQUAL(1643679997, tc_task_get_modified(task)); - - // zero is not allowed - TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_set_modified(task, 0)); - - tc_task_free(task); - - tc_replica_free(rep); -} - -// starting and stopping a task works, as seen by tc_task_is_active -static void test_task_start_stop_is_active(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - TEST_ASSERT_FALSE(tc_task_is_active(task)); - - tc_task_to_mut(task, rep); - - TEST_ASSERT_FALSE(tc_task_is_active(task)); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_start(task)); - TEST_ASSERT_TRUE(tc_task_is_active(task)); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_stop(task)); - TEST_ASSERT_FALSE(tc_task_is_active(task)); - - tc_task_free(task); - tc_replica_free(rep); -} - -// tc_task_done and delete work and set the status -static void test_task_done_and_delete(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - tc_task_to_mut(task, rep); - - TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_done(task)); - TEST_ASSERT_EQUAL(TC_STATUS_COMPLETED, tc_task_get_status(task)); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_delete(task)); - TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); - - tc_task_free(task); - tc_replica_free(rep); -} - -// adding and removing tags to a task works, and invalid tags are rejected -static void test_task_add_remove_has_tag(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - tc_task_to_mut(task, rep); - - TEST_ASSERT_FALSE(tc_task_has_tag(task, tc_string_borrow("next"))); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_tag(task, tc_string_borrow("next"))); - TEST_ASSERT_NULL(tc_task_error(task).ptr); - - TEST_ASSERT_TRUE(tc_task_has_tag(task, tc_string_borrow("next"))); - - // invalid - synthetic tag - TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_add_tag(task, tc_string_borrow("PENDING"))); - TCString err = tc_task_error(task); - TEST_ASSERT_NOT_NULL(err.ptr); - tc_string_free(&err); - - // invald - not a valid tag string - TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_add_tag(task, tc_string_borrow("my tag"))); - err = tc_task_error(task); - TEST_ASSERT_NOT_NULL(err.ptr); - tc_string_free(&err); - - // invald - not utf-8 - TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_add_tag(task, tc_string_borrow("\xf0\x28\x8c\x28"))); - err = tc_task_error(task); - TEST_ASSERT_NOT_NULL(err.ptr); - tc_string_free(&err); - - TEST_ASSERT_TRUE(tc_task_has_tag(task, tc_string_borrow("next"))); - - // remove the tag - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_remove_tag(task, tc_string_borrow("next"))); - TEST_ASSERT_NULL(tc_task_error(task).ptr); - - TEST_ASSERT_FALSE(tc_task_has_tag(task, tc_string_borrow("next"))); - - tc_task_free(task); - tc_replica_free(rep); -} - -// get_tags returns the list of tags -static void test_task_get_tags(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - tc_task_to_mut(task, rep); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_tag(task, tc_string_borrow("next"))); - - TCStringList tags = tc_task_get_tags(task); - - int found_pending = false, found_next = false; - for (size_t i = 0; i < tags.len; i++) { - if (strcmp("PENDING", tc_string_content(&tags.items[i])) == 0) { - found_pending = true; - } - if (strcmp("next", tc_string_content(&tags.items[i])) == 0) { - found_next = true; - } - } - TEST_ASSERT_TRUE(found_pending); - TEST_ASSERT_TRUE(found_next); - - tc_string_list_free(&tags); - TEST_ASSERT_NULL(tags.items); - - tc_task_free(task); - tc_replica_free(rep); -} - -// annotation manipulation (add, remove, list, free) -static void test_task_annotations(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - TCAnnotationList anns = tc_task_get_annotations(task); - TEST_ASSERT_EQUAL(0, anns.len); - TEST_ASSERT_NOT_NULL(anns.items); - tc_annotation_list_free(&anns); - - tc_task_to_mut(task, rep); - - TCAnnotation ann; - - ann.entry = 1644623411; - ann.description = tc_string_borrow("ann1"); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_annotation(task, &ann)); - TEST_ASSERT_NULL(ann.description.ptr); - - ann.entry = 1644623422; - ann.description = tc_string_borrow("ann2"); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_annotation(task, &ann)); - TEST_ASSERT_NULL(ann.description.ptr); - - anns = tc_task_get_annotations(task); - - int found1 = false, found2 = false; - for (size_t i = 0; i < anns.len; i++) { - if (0 == strcmp("ann1", tc_string_content(&anns.items[i].description))) { - TEST_ASSERT_EQUAL(anns.items[i].entry, 1644623411); - found1 = true; - } - if (0 == strcmp("ann2", tc_string_content(&anns.items[i].description))) { - TEST_ASSERT_EQUAL(anns.items[i].entry, 1644623422); - found2 = true; - } - } - TEST_ASSERT_TRUE(found1); - TEST_ASSERT_TRUE(found2); - - tc_annotation_list_free(&anns); - TEST_ASSERT_NULL(anns.items); - - tc_task_free(task); - tc_replica_free(rep); -} - -// UDA manipulation (add, remove, list, free) -static void test_task_udas(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task( - rep, - TC_STATUS_PENDING, - tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - tc_task_to_mut(task, rep); - - TCString value; - TCUdaList udas; - - TEST_ASSERT_NULL(tc_task_get_uda(task, tc_string_borrow("ns"), tc_string_borrow("u1")).ptr); - TEST_ASSERT_NULL(tc_task_get_legacy_uda(task, tc_string_borrow("leg1")).ptr); - - udas = tc_task_get_udas(task); - TEST_ASSERT_NOT_NULL(udas.items); - TEST_ASSERT_EQUAL(0, udas.len); - tc_uda_list_free(&udas); - - udas = tc_task_get_legacy_udas(task); - TEST_ASSERT_NOT_NULL(udas.items); - TEST_ASSERT_EQUAL(0, udas.len); - tc_uda_list_free(&udas); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, - tc_task_set_uda(task, - tc_string_borrow("ns"), - tc_string_borrow("u1"), - tc_string_borrow("vvv"))); - - value = tc_task_get_uda(task, tc_string_borrow("ns"), tc_string_borrow("u1")); - TEST_ASSERT_NOT_NULL(value.ptr); - TEST_ASSERT_EQUAL_STRING("vvv", tc_string_content(&value)); - tc_string_free(&value); - TEST_ASSERT_NULL(tc_task_get_legacy_uda(task, tc_string_borrow("leg1")).ptr); - - udas = tc_task_get_udas(task); - TEST_ASSERT_NOT_NULL(udas.items); - TEST_ASSERT_EQUAL(1, udas.len); - TEST_ASSERT_EQUAL_STRING("ns", tc_string_content(&udas.items[0].ns)); - TEST_ASSERT_EQUAL_STRING("u1", tc_string_content(&udas.items[0].key)); - TEST_ASSERT_EQUAL_STRING("vvv", tc_string_content(&udas.items[0].value)); - tc_uda_list_free(&udas); - - udas = tc_task_get_legacy_udas(task); - TEST_ASSERT_NOT_NULL(udas.items); - TEST_ASSERT_EQUAL(1, udas.len); - TEST_ASSERT_NULL(udas.items[0].ns.ptr); - TEST_ASSERT_EQUAL_STRING("ns.u1", tc_string_content(&udas.items[0].key)); - TEST_ASSERT_EQUAL_STRING("vvv", tc_string_content(&udas.items[0].value)); - tc_uda_list_free(&udas); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, - tc_task_set_legacy_uda(task, - tc_string_borrow("leg1"), - tc_string_borrow("legv"))); - - value = tc_task_get_uda(task, tc_string_borrow("ns"), tc_string_borrow("u1")); - TEST_ASSERT_NOT_NULL(value.ptr); - TEST_ASSERT_EQUAL_STRING("vvv", tc_string_content(&value)); - tc_string_free(&value); - - value = tc_task_get_legacy_uda(task, tc_string_borrow("leg1")); - TEST_ASSERT_NOT_NULL(value.ptr); - TEST_ASSERT_EQUAL_STRING("legv", tc_string_content(&value)); - tc_string_free(&value); - - udas = tc_task_get_udas(task); - TEST_ASSERT_NOT_NULL(udas.items); - TEST_ASSERT_EQUAL(2, udas.len); - tc_uda_list_free(&udas); - - udas = tc_task_get_legacy_udas(task); - TEST_ASSERT_NOT_NULL(udas.items); - TEST_ASSERT_EQUAL(2, udas.len); - tc_uda_list_free(&udas); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, - tc_task_remove_uda(task, - tc_string_borrow("ns"), - tc_string_borrow("u1"))); - - TEST_ASSERT_NULL(tc_task_get_uda(task, tc_string_borrow("ns"), tc_string_borrow("u1")).ptr); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, - tc_task_remove_uda(task, - tc_string_borrow("ns"), - tc_string_borrow("u1"))); - - udas = tc_task_get_udas(task); - TEST_ASSERT_NOT_NULL(udas.items); - TEST_ASSERT_EQUAL(1, udas.len); - TEST_ASSERT_EQUAL_STRING("", tc_string_content(&udas.items[0].ns)); - TEST_ASSERT_EQUAL_STRING("leg1", tc_string_content(&udas.items[0].key)); - TEST_ASSERT_EQUAL_STRING("legv", tc_string_content(&udas.items[0].value)); - tc_uda_list_free(&udas); - - udas = tc_task_get_legacy_udas(task); - TEST_ASSERT_NOT_NULL(udas.items); - TEST_ASSERT_EQUAL(1, udas.len); - TEST_ASSERT_NULL(udas.items[0].ns.ptr); - TEST_ASSERT_EQUAL_STRING("leg1", tc_string_content(&udas.items[0].key)); - TEST_ASSERT_EQUAL_STRING("legv", tc_string_content(&udas.items[0].value)); - tc_uda_list_free(&udas); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, - tc_task_remove_legacy_uda(task, - tc_string_borrow("leg1"))); - - TEST_ASSERT_NULL(tc_task_get_legacy_uda(task, tc_string_borrow("leg1")).ptr); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, - tc_task_remove_legacy_uda(task, - tc_string_borrow("leg1"))); - - udas = tc_task_get_udas(task); - TEST_ASSERT_NOT_NULL(udas.items); - TEST_ASSERT_EQUAL(0, udas.len); - tc_uda_list_free(&udas); - - udas = tc_task_get_legacy_udas(task); - TEST_ASSERT_NOT_NULL(udas.items); - TEST_ASSERT_EQUAL(0, udas.len); - tc_uda_list_free(&udas); - - tc_task_free(task); - tc_replica_free(rep); -} - -// dependency manipulation -static void test_task_dependencies(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task1 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("task 1")); - TEST_ASSERT_NOT_NULL(task1); - TCTask *task2 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("task 2")); - TEST_ASSERT_NOT_NULL(task2); - - TCUuidList deps; - - deps = tc_task_get_dependencies(task1); - TEST_ASSERT_EQUAL(0, deps.len); - tc_uuid_list_free(&deps); - - tc_task_to_mut(task1, rep); - TEST_ASSERT_EQUAL(TC_RESULT_OK, - tc_task_add_dependency(task1, tc_task_get_uuid(task2))); - - deps = tc_task_get_dependencies(task1); - TEST_ASSERT_EQUAL(1, deps.len); - TEST_ASSERT_EQUAL_MEMORY(tc_task_get_uuid(task2).bytes, deps.items[0].bytes, 16); - tc_uuid_list_free(&deps); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, - tc_task_remove_dependency(task1, tc_task_get_uuid(task2))); - - deps = tc_task_get_dependencies(task1); - TEST_ASSERT_EQUAL(0, deps.len); - tc_uuid_list_free(&deps); - - tc_task_free(task1); - tc_task_free(task2); - tc_replica_free(rep); -} - -static void tckvlist_assert_key(TCKVList *list, char *key, char *value) { - TEST_ASSERT_NOT_NULL(list); - for (size_t i = 0; i < list->len; i++) { - if (0 == strcmp(tc_string_content(&list->items[i].key), key)) { - TEST_ASSERT_EQUAL_STRING(value, tc_string_content(&list->items[i].value)); - return; - } - } - TEST_FAIL_MESSAGE("key not found"); -} - -// get_tags returns the list of tags -static void test_task_taskmap(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("my task")); - TEST_ASSERT_NOT_NULL(task); - - tc_task_to_mut(task, rep); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_tag(task, tc_string_borrow("next"))); - - TCAnnotation ann; - ann.entry = 1644623411; - ann.description = tc_string_borrow("ann1"); - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_annotation(task, &ann)); - - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_wait(task, 3643679997)); // 2085 - - TCKVList taskmap = tc_task_get_taskmap(task); - tckvlist_assert_key(&taskmap, "annotation_1644623411", "ann1"); - tckvlist_assert_key(&taskmap, "tag_next", ""); - tckvlist_assert_key(&taskmap, "status", "pending"); - tckvlist_assert_key(&taskmap, "description", "my task"); - tc_kv_list_free(&taskmap); - - tc_task_free(task); - tc_replica_free(rep); -} - -// taking from a task list behaves correctly -static void test_task_list_take(void) { - TCReplica *rep = tc_replica_new_in_memory(); - TEST_ASSERT_NULL(tc_replica_error(rep).ptr); - - TCTask *task1 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("t")); - TEST_ASSERT_NOT_NULL(task1); - - TCTask *task2 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("t")); - TEST_ASSERT_NOT_NULL(task2); - tc_task_free(task2); - - TCString desc; - TCTaskList tasks = tc_replica_all_tasks(rep); - TEST_ASSERT_NOT_NULL(tasks.items); - TEST_ASSERT_EQUAL(2, tasks.len); - - task1 = tc_task_list_take(&tasks, 5); // out of bounds - TEST_ASSERT_NULL(task1); - - task1 = tc_task_list_take(&tasks, 0); - TEST_ASSERT_NOT_NULL(task1); - desc = tc_task_get_description(task1); - TEST_ASSERT_EQUAL_STRING("t", tc_string_content(&desc)); - tc_string_free(&desc); - - task2 = tc_task_list_take(&tasks, 1); - TEST_ASSERT_NOT_NULL(task2); - desc = tc_task_get_description(task2); - TEST_ASSERT_EQUAL_STRING("t", tc_string_content(&desc)); - tc_string_free(&desc); - - tc_task_free(task1); - tc_task_free(task2); - - task1 = tc_task_list_take(&tasks, 0); // already taken - TEST_ASSERT_NULL(task1); - - task1 = tc_task_list_take(&tasks, 5); // out of bounds - TEST_ASSERT_NULL(task1); - - tc_task_list_free(&tasks); - TEST_ASSERT_NULL(tasks.items); - - tc_replica_free(rep); -} - -int task_tests(void) { - UNITY_BEGIN(); - // each test case above should be named here, in order. - RUN_TEST(test_task_creation); - RUN_TEST(test_task_free_mutable_task); - RUN_TEST(test_task_get_set_status); - RUN_TEST(test_task_get_set_description); - RUN_TEST(test_task_get_set_attribute); - RUN_TEST(test_task_get_set_entry); - RUN_TEST(test_task_get_set_modified); - RUN_TEST(test_task_get_set_wait_and_is_waiting); - RUN_TEST(test_task_start_stop_is_active); - RUN_TEST(test_task_done_and_delete); - RUN_TEST(test_task_add_remove_has_tag); - RUN_TEST(test_task_get_tags); - RUN_TEST(test_task_annotations); - RUN_TEST(test_task_udas); - RUN_TEST(test_task_dependencies); - RUN_TEST(test_task_taskmap); - RUN_TEST(test_task_list_take); - return UNITY_END(); -} diff --git a/taskchampion/integration-tests/src/bindings_tests/test.c b/taskchampion/integration-tests/src/bindings_tests/test.c deleted file mode 100644 index 5afa236ca..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/test.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include "unity.h" - -// these functions are shared between all test "suites" -// and cannot be customized per-suite. -void setUp(void) { } -void tearDown(void) { } - -static FILE *output = NULL; - -// Set up for test_output, writing output to "TEST-OUTPUT" in the -// current directory. The Rust test harness reads this file to get -// the output and display it only on failure. This is called by -// the Rust test harness -void setup_output(void) { - output = fopen("TEST-OUTPUT", "w"); -} - -// Close the output file. Called by the Rust test harness. -void finish_output(void) { - fclose(output); - output = NULL; -} - -// this replaces UNITY_OUTPUT_CHAR, and writes output to -// TEST-OUTPUT in the current directory; the Rust test harness -// will read this data if the test fails. -void test_output(char c) { - fputc(c, output); -} diff --git a/taskchampion/integration-tests/src/bindings_tests/unity/LICENSE.txt b/taskchampion/integration-tests/src/bindings_tests/unity/LICENSE.txt deleted file mode 100644 index b9a329dde..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/unity/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams - -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. diff --git a/taskchampion/integration-tests/src/bindings_tests/unity/README.md b/taskchampion/integration-tests/src/bindings_tests/unity/README.md deleted file mode 100644 index 6f755ced0..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/unity/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Unity - -This directory contains the src from https://github.com/ThrowTheSwitch/Unity, revision 8ba01386008196a92ef4fdbdb0b00f2434c79563. diff --git a/taskchampion/integration-tests/src/bindings_tests/unity/unity.c b/taskchampion/integration-tests/src/bindings_tests/unity/unity.c deleted file mode 100644 index b88024875..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/unity/unity.c +++ /dev/null @@ -1,2119 +0,0 @@ -/* ========================================================================= - Unity Project - A Test Framework for C - Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams - [Released under MIT License. Please refer to license.txt for details] -============================================================================ */ - -#include "unity.h" -#include - -#ifdef AVR -#include -#else -#define PROGMEM -#endif - -/* If omitted from header, declare overrideable prototypes here so they're ready for use */ -#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION -void UNITY_OUTPUT_CHAR(int); -#endif - -/* Helpful macros for us to use here in Assert functions */ -#define UNITY_FAIL_AND_BAIL do { Unity.CurrentTestFailed = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0) -#define UNITY_IGNORE_AND_BAIL do { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0) -#define RETURN_IF_FAIL_OR_IGNORE do { if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) { TEST_ABORT(); } } while (0) - -struct UNITY_STORAGE_T Unity; - -#ifdef UNITY_OUTPUT_COLOR -const char PROGMEM UnityStrOk[] = "\033[42mOK\033[00m"; -const char PROGMEM UnityStrPass[] = "\033[42mPASS\033[00m"; -const char PROGMEM UnityStrFail[] = "\033[41mFAIL\033[00m"; -const char PROGMEM UnityStrIgnore[] = "\033[43mIGNORE\033[00m"; -#else -const char PROGMEM UnityStrOk[] = "OK"; -const char PROGMEM UnityStrPass[] = "PASS"; -const char PROGMEM UnityStrFail[] = "FAIL"; -const char PROGMEM UnityStrIgnore[] = "IGNORE"; -#endif -static const char PROGMEM UnityStrNull[] = "NULL"; -static const char PROGMEM UnityStrSpacer[] = ". "; -static const char PROGMEM UnityStrExpected[] = " Expected "; -static const char PROGMEM UnityStrWas[] = " Was "; -static const char PROGMEM UnityStrGt[] = " to be greater than "; -static const char PROGMEM UnityStrLt[] = " to be less than "; -static const char PROGMEM UnityStrOrEqual[] = "or equal to "; -static const char PROGMEM UnityStrNotEqual[] = " to be not equal to "; -static const char PROGMEM UnityStrElement[] = " Element "; -static const char PROGMEM UnityStrByte[] = " Byte "; -static const char PROGMEM UnityStrMemory[] = " Memory Mismatch."; -static const char PROGMEM UnityStrDelta[] = " Values Not Within Delta "; -static const char PROGMEM UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless."; -static const char PROGMEM UnityStrNullPointerForExpected[] = " Expected pointer to be NULL"; -static const char PROGMEM UnityStrNullPointerForActual[] = " Actual pointer was NULL"; -#ifndef UNITY_EXCLUDE_FLOAT -static const char PROGMEM UnityStrNot[] = "Not "; -static const char PROGMEM UnityStrInf[] = "Infinity"; -static const char PROGMEM UnityStrNegInf[] = "Negative Infinity"; -static const char PROGMEM UnityStrNaN[] = "NaN"; -static const char PROGMEM UnityStrDet[] = "Determinate"; -static const char PROGMEM UnityStrInvalidFloatTrait[] = "Invalid Float Trait"; -#endif -const char PROGMEM UnityStrErrShorthand[] = "Unity Shorthand Support Disabled"; -const char PROGMEM UnityStrErrFloat[] = "Unity Floating Point Disabled"; -const char PROGMEM UnityStrErrDouble[] = "Unity Double Precision Disabled"; -const char PROGMEM UnityStrErr64[] = "Unity 64-bit Support Disabled"; -static const char PROGMEM UnityStrBreaker[] = "-----------------------"; -static const char PROGMEM UnityStrResultsTests[] = " Tests "; -static const char PROGMEM UnityStrResultsFailures[] = " Failures "; -static const char PROGMEM UnityStrResultsIgnored[] = " Ignored "; -#ifndef UNITY_EXCLUDE_DETAILS -static const char PROGMEM UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; -static const char PROGMEM UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; -#endif -/*----------------------------------------------- - * Pretty Printers & Test Result Output Handlers - *-----------------------------------------------*/ - -/*-----------------------------------------------*/ -/* Local helper function to print characters. */ -static void UnityPrintChar(const char* pch) -{ - /* printable characters plus CR & LF are printed */ - if ((*pch <= 126) && (*pch >= 32)) - { - UNITY_OUTPUT_CHAR(*pch); - } - /* write escaped carriage returns */ - else if (*pch == 13) - { - UNITY_OUTPUT_CHAR('\\'); - UNITY_OUTPUT_CHAR('r'); - } - /* write escaped line feeds */ - else if (*pch == 10) - { - UNITY_OUTPUT_CHAR('\\'); - UNITY_OUTPUT_CHAR('n'); - } - /* unprintable characters are shown as codes */ - else - { - UNITY_OUTPUT_CHAR('\\'); - UNITY_OUTPUT_CHAR('x'); - UnityPrintNumberHex((UNITY_UINT)*pch, 2); - } -} - -/*-----------------------------------------------*/ -/* Local helper function to print ANSI escape strings e.g. "\033[42m". */ -#ifdef UNITY_OUTPUT_COLOR -static UNITY_UINT UnityPrintAnsiEscapeString(const char* string) -{ - const char* pch = string; - UNITY_UINT count = 0; - - while (*pch && (*pch != 'm')) - { - UNITY_OUTPUT_CHAR(*pch); - pch++; - count++; - } - UNITY_OUTPUT_CHAR('m'); - count++; - - return count; -} -#endif - -/*-----------------------------------------------*/ -void UnityPrint(const char* string) -{ - const char* pch = string; - - if (pch != NULL) - { - while (*pch) - { -#ifdef UNITY_OUTPUT_COLOR - /* print ANSI escape code */ - if ((*pch == 27) && (*(pch + 1) == '[')) - { - pch += UnityPrintAnsiEscapeString(pch); - continue; - } -#endif - UnityPrintChar(pch); - pch++; - } - } -} -/*-----------------------------------------------*/ -void UnityPrintLen(const char* string, const UNITY_UINT32 length) -{ - const char* pch = string; - - if (pch != NULL) - { - while (*pch && ((UNITY_UINT32)(pch - string) < length)) - { - /* printable characters plus CR & LF are printed */ - if ((*pch <= 126) && (*pch >= 32)) - { - UNITY_OUTPUT_CHAR(*pch); - } - /* write escaped carriage returns */ - else if (*pch == 13) - { - UNITY_OUTPUT_CHAR('\\'); - UNITY_OUTPUT_CHAR('r'); - } - /* write escaped line feeds */ - else if (*pch == 10) - { - UNITY_OUTPUT_CHAR('\\'); - UNITY_OUTPUT_CHAR('n'); - } - /* unprintable characters are shown as codes */ - else - { - UNITY_OUTPUT_CHAR('\\'); - UNITY_OUTPUT_CHAR('x'); - UnityPrintNumberHex((UNITY_UINT)*pch, 2); - } - pch++; - } - } -} - -/*-----------------------------------------------*/ -void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style) -{ - if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) - { - if (style == UNITY_DISPLAY_STYLE_CHAR) - { - /* printable characters plus CR & LF are printed */ - UNITY_OUTPUT_CHAR('\''); - if ((number <= 126) && (number >= 32)) - { - UNITY_OUTPUT_CHAR((int)number); - } - /* write escaped carriage returns */ - else if (number == 13) - { - UNITY_OUTPUT_CHAR('\\'); - UNITY_OUTPUT_CHAR('r'); - } - /* write escaped line feeds */ - else if (number == 10) - { - UNITY_OUTPUT_CHAR('\\'); - UNITY_OUTPUT_CHAR('n'); - } - /* unprintable characters are shown as codes */ - else - { - UNITY_OUTPUT_CHAR('\\'); - UNITY_OUTPUT_CHAR('x'); - UnityPrintNumberHex((UNITY_UINT)number, 2); - } - UNITY_OUTPUT_CHAR('\''); - } - else - { - UnityPrintNumber(number); - } - } - else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT) - { - UnityPrintNumberUnsigned((UNITY_UINT)number); - } - else - { - UNITY_OUTPUT_CHAR('0'); - UNITY_OUTPUT_CHAR('x'); - UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2)); - } -} - -/*-----------------------------------------------*/ -void UnityPrintNumber(const UNITY_INT number_to_print) -{ - UNITY_UINT number = (UNITY_UINT)number_to_print; - - if (number_to_print < 0) - { - /* A negative number, including MIN negative */ - UNITY_OUTPUT_CHAR('-'); - number = (~number) + 1; - } - UnityPrintNumberUnsigned(number); -} - -/*----------------------------------------------- - * basically do an itoa using as little ram as possible */ -void UnityPrintNumberUnsigned(const UNITY_UINT number) -{ - UNITY_UINT divisor = 1; - - /* figure out initial divisor */ - while (number / divisor > 9) - { - divisor *= 10; - } - - /* now mod and print, then divide divisor */ - do - { - UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10))); - divisor /= 10; - } while (divisor > 0); -} - -/*-----------------------------------------------*/ -void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print) -{ - int nibble; - char nibbles = nibbles_to_print; - - if ((unsigned)nibbles > UNITY_MAX_NIBBLES) - { - nibbles = UNITY_MAX_NIBBLES; - } - - while (nibbles > 0) - { - nibbles--; - nibble = (int)(number >> (nibbles * 4)) & 0x0F; - if (nibble <= 9) - { - UNITY_OUTPUT_CHAR((char)('0' + nibble)); - } - else - { - UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble)); - } - } -} - -/*-----------------------------------------------*/ -void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) -{ - UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1); - UNITY_INT32 i; - - for (i = 0; i < UNITY_INT_WIDTH; i++) - { - if (current_bit & mask) - { - if (current_bit & number) - { - UNITY_OUTPUT_CHAR('1'); - } - else - { - UNITY_OUTPUT_CHAR('0'); - } - } - else - { - UNITY_OUTPUT_CHAR('X'); - } - current_bit = current_bit >> 1; - } -} - -/*-----------------------------------------------*/ -#ifndef UNITY_EXCLUDE_FLOAT_PRINT -/* - * This function prints a floating-point value in a format similar to - * printf("%.7g") on a single-precision machine or printf("%.9g") on a - * double-precision machine. The 7th digit won't always be totally correct - * in single-precision operation (for that level of accuracy, a more - * complicated algorithm would be needed). - */ -void UnityPrintFloat(const UNITY_DOUBLE input_number) -{ -#ifdef UNITY_INCLUDE_DOUBLE - static const int sig_digits = 9; - static const UNITY_INT32 min_scaled = 100000000; - static const UNITY_INT32 max_scaled = 1000000000; -#else - static const int sig_digits = 7; - static const UNITY_INT32 min_scaled = 1000000; - static const UNITY_INT32 max_scaled = 10000000; -#endif - - UNITY_DOUBLE number = input_number; - - /* print minus sign (does not handle negative zero) */ - if (number < 0.0f) - { - UNITY_OUTPUT_CHAR('-'); - number = -number; - } - - /* handle zero, NaN, and +/- infinity */ - if (number == 0.0f) - { - UnityPrint("0"); - } - else if (isnan(number)) - { - UnityPrint("nan"); - } - else if (isinf(number)) - { - UnityPrint("inf"); - } - else - { - UNITY_INT32 n_int = 0; - UNITY_INT32 n; - int exponent = 0; - int decimals; - int digits; - char buf[16] = {0}; - - /* - * Scale up or down by powers of 10. To minimize rounding error, - * start with a factor/divisor of 10^10, which is the largest - * power of 10 that can be represented exactly. Finally, compute - * (exactly) the remaining power of 10 and perform one more - * multiplication or division. - */ - if (number < 1.0f) - { - UNITY_DOUBLE factor = 1.0f; - - while (number < (UNITY_DOUBLE)max_scaled / 1e10f) { number *= 1e10f; exponent -= 10; } - while (number * factor < (UNITY_DOUBLE)min_scaled) { factor *= 10.0f; exponent--; } - - number *= factor; - } - else if (number > (UNITY_DOUBLE)max_scaled) - { - UNITY_DOUBLE divisor = 1.0f; - - while (number > (UNITY_DOUBLE)min_scaled * 1e10f) { number /= 1e10f; exponent += 10; } - while (number / divisor > (UNITY_DOUBLE)max_scaled) { divisor *= 10.0f; exponent++; } - - number /= divisor; - } - else - { - /* - * In this range, we can split off the integer part before - * doing any multiplications. This reduces rounding error by - * freeing up significant bits in the fractional part. - */ - UNITY_DOUBLE factor = 1.0f; - n_int = (UNITY_INT32)number; - number -= (UNITY_DOUBLE)n_int; - - while (n_int < min_scaled) { n_int *= 10; factor *= 10.0f; exponent--; } - - number *= factor; - } - - /* round to nearest integer */ - n = ((UNITY_INT32)(number + number) + 1) / 2; - -#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO - /* round to even if exactly between two integers */ - if ((n & 1) && (((UNITY_DOUBLE)n - number) == 0.5f)) - n--; -#endif - - n += n_int; - - if (n >= max_scaled) - { - n = min_scaled; - exponent++; - } - - /* determine where to place decimal point */ - decimals = ((exponent <= 0) && (exponent >= -(sig_digits + 3))) ? (-exponent) : (sig_digits - 1); - exponent += decimals; - - /* truncate trailing zeroes after decimal point */ - while ((decimals > 0) && ((n % 10) == 0)) - { - n /= 10; - decimals--; - } - - /* build up buffer in reverse order */ - digits = 0; - while ((n != 0) || (digits <= decimals)) - { - buf[digits++] = (char)('0' + n % 10); - n /= 10; - } - - /* print out buffer (backwards) */ - while (digits > 0) - { - if (digits == decimals) - { - UNITY_OUTPUT_CHAR('.'); - } - UNITY_OUTPUT_CHAR(buf[--digits]); - } - - /* print exponent if needed */ - if (exponent != 0) - { - UNITY_OUTPUT_CHAR('e'); - - if (exponent < 0) - { - UNITY_OUTPUT_CHAR('-'); - exponent = -exponent; - } - else - { - UNITY_OUTPUT_CHAR('+'); - } - - digits = 0; - while ((exponent != 0) || (digits < 2)) - { - buf[digits++] = (char)('0' + exponent % 10); - exponent /= 10; - } - while (digits > 0) - { - UNITY_OUTPUT_CHAR(buf[--digits]); - } - } - } -} -#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ - -/*-----------------------------------------------*/ -static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line) -{ -#ifdef UNITY_OUTPUT_FOR_ECLIPSE - UNITY_OUTPUT_CHAR('('); - UnityPrint(file); - UNITY_OUTPUT_CHAR(':'); - UnityPrintNumber((UNITY_INT)line); - UNITY_OUTPUT_CHAR(')'); - UNITY_OUTPUT_CHAR(' '); - UnityPrint(Unity.CurrentTestName); - UNITY_OUTPUT_CHAR(':'); -#else -#ifdef UNITY_OUTPUT_FOR_IAR_WORKBENCH - UnityPrint("'); - UnityPrint(Unity.CurrentTestName); - UnityPrint(" "); -#else -#ifdef UNITY_OUTPUT_FOR_QT_CREATOR - UnityPrint("file://"); - UnityPrint(file); - UNITY_OUTPUT_CHAR(':'); - UnityPrintNumber((UNITY_INT)line); - UNITY_OUTPUT_CHAR(' '); - UnityPrint(Unity.CurrentTestName); - UNITY_OUTPUT_CHAR(':'); -#else - UnityPrint(file); - UNITY_OUTPUT_CHAR(':'); - UnityPrintNumber((UNITY_INT)line); - UNITY_OUTPUT_CHAR(':'); - UnityPrint(Unity.CurrentTestName); - UNITY_OUTPUT_CHAR(':'); -#endif -#endif -#endif -} - -/*-----------------------------------------------*/ -static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line) -{ - UnityTestResultsBegin(Unity.TestFile, line); - UnityPrint(UnityStrFail); - UNITY_OUTPUT_CHAR(':'); -} - -/*-----------------------------------------------*/ -void UnityConcludeTest(void) -{ - if (Unity.CurrentTestIgnored) - { - Unity.TestIgnores++; - } - else if (!Unity.CurrentTestFailed) - { - UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber); - UnityPrint(UnityStrPass); - } - else - { - Unity.TestFailures++; - } - - Unity.CurrentTestFailed = 0; - Unity.CurrentTestIgnored = 0; - UNITY_PRINT_EXEC_TIME(); - UNITY_PRINT_EOL(); - UNITY_FLUSH_CALL(); -} - -/*-----------------------------------------------*/ -static void UnityAddMsgIfSpecified(const char* msg) -{ - if (msg) - { - UnityPrint(UnityStrSpacer); - -#ifdef UNITY_PRINT_TEST_CONTEXT - UNITY_PRINT_TEST_CONTEXT(); -#endif -#ifndef UNITY_EXCLUDE_DETAILS - if (Unity.CurrentDetail1) - { - UnityPrint(UnityStrDetail1Name); - UnityPrint(Unity.CurrentDetail1); - if (Unity.CurrentDetail2) - { - UnityPrint(UnityStrDetail2Name); - UnityPrint(Unity.CurrentDetail2); - } - UnityPrint(UnityStrSpacer); - } -#endif - UnityPrint(msg); - } -} - -/*-----------------------------------------------*/ -static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual) -{ - UnityPrint(UnityStrExpected); - if (expected != NULL) - { - UNITY_OUTPUT_CHAR('\''); - UnityPrint(expected); - UNITY_OUTPUT_CHAR('\''); - } - else - { - UnityPrint(UnityStrNull); - } - UnityPrint(UnityStrWas); - if (actual != NULL) - { - UNITY_OUTPUT_CHAR('\''); - UnityPrint(actual); - UNITY_OUTPUT_CHAR('\''); - } - else - { - UnityPrint(UnityStrNull); - } -} - -/*-----------------------------------------------*/ -static void UnityPrintExpectedAndActualStringsLen(const char* expected, - const char* actual, - const UNITY_UINT32 length) -{ - UnityPrint(UnityStrExpected); - if (expected != NULL) - { - UNITY_OUTPUT_CHAR('\''); - UnityPrintLen(expected, length); - UNITY_OUTPUT_CHAR('\''); - } - else - { - UnityPrint(UnityStrNull); - } - UnityPrint(UnityStrWas); - if (actual != NULL) - { - UNITY_OUTPUT_CHAR('\''); - UnityPrintLen(actual, length); - UNITY_OUTPUT_CHAR('\''); - } - else - { - UnityPrint(UnityStrNull); - } -} - -/*----------------------------------------------- - * Assertion & Control Helpers - *-----------------------------------------------*/ - -/*-----------------------------------------------*/ -static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected, - UNITY_INTERNAL_PTR actual, - const UNITY_LINE_TYPE lineNumber, - const char* msg) -{ - /* Both are NULL or same pointer */ - if (expected == actual) { return 0; } - - /* print and return true if just expected is NULL */ - if (expected == NULL) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrNullPointerForExpected); - UnityAddMsgIfSpecified(msg); - return 1; - } - - /* print and return true if just actual is NULL */ - if (actual == NULL) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrNullPointerForActual); - UnityAddMsgIfSpecified(msg); - return 1; - } - - return 0; /* return false if neither is NULL */ -} - -/*----------------------------------------------- - * Assertion Functions - *-----------------------------------------------*/ - -/*-----------------------------------------------*/ -void UnityAssertBits(const UNITY_INT mask, - const UNITY_INT expected, - const UNITY_INT actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber) -{ - RETURN_IF_FAIL_OR_IGNORE; - - if ((mask & expected) != (mask & actual)) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrExpected); - UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected); - UnityPrint(UnityStrWas); - UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } -} - -/*-----------------------------------------------*/ -void UnityAssertEqualNumber(const UNITY_INT expected, - const UNITY_INT actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_DISPLAY_STYLE_T style) -{ - RETURN_IF_FAIL_OR_IGNORE; - - if (expected != actual) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrExpected); - UnityPrintNumberByStyle(expected, style); - UnityPrint(UnityStrWas); - UnityPrintNumberByStyle(actual, style); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } -} - -/*-----------------------------------------------*/ -void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, - const UNITY_INT actual, - const UNITY_COMPARISON_T compare, - const char *msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_DISPLAY_STYLE_T style) -{ - int failed = 0; - RETURN_IF_FAIL_OR_IGNORE; - - if ((threshold == actual) && (compare & UNITY_EQUAL_TO)) { return; } - if ((threshold == actual)) { failed = 1; } - - if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) - { - if ((actual > threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } - if ((actual < threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } - } - else /* UINT or HEX */ - { - if (((UNITY_UINT)actual > (UNITY_UINT)threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } - if (((UNITY_UINT)actual < (UNITY_UINT)threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } - } - - if (failed) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrExpected); - UnityPrintNumberByStyle(actual, style); - if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } - if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } - if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } - if (compare == UNITY_NOT_EQUAL) { UnityPrint(UnityStrNotEqual); } - UnityPrintNumberByStyle(threshold, style); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } -} - -#define UnityPrintPointlessAndBail() \ -do { \ - UnityTestResultsFailBegin(lineNumber); \ - UnityPrint(UnityStrPointless); \ - UnityAddMsgIfSpecified(msg); \ - UNITY_FAIL_AND_BAIL; \ -} while (0) - -/*-----------------------------------------------*/ -void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, - UNITY_INTERNAL_PTR actual, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_DISPLAY_STYLE_T style, - const UNITY_FLAGS_T flags) -{ - UNITY_UINT32 elements = num_elements; - unsigned int length = style & 0xF; - unsigned int increment = 0; - - RETURN_IF_FAIL_OR_IGNORE; - - if (num_elements == 0) - { - UnityPrintPointlessAndBail(); - } - - if (expected == actual) - { - return; /* Both are NULL or same pointer */ - } - - if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) - { - UNITY_FAIL_AND_BAIL; - } - - while ((elements > 0) && (elements--)) - { - UNITY_INT expect_val; - UNITY_INT actual_val; - - switch (length) - { - case 1: - expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; - actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; - increment = sizeof(UNITY_INT8); - break; - - case 2: - expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; - actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; - increment = sizeof(UNITY_INT16); - break; - -#ifdef UNITY_SUPPORT_64 - case 8: - expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; - actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; - increment = sizeof(UNITY_INT64); - break; -#endif - - default: /* default is length 4 bytes */ - case 4: - expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; - actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; - increment = sizeof(UNITY_INT32); - length = 4; - break; - } - - if (expect_val != actual_val) - { - if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) - { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ - UNITY_INT mask = 1; - mask = (mask << 8 * length) - 1; - expect_val &= mask; - actual_val &= mask; - } - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrElement); - UnityPrintNumberUnsigned(num_elements - elements - 1); - UnityPrint(UnityStrExpected); - UnityPrintNumberByStyle(expect_val, style); - UnityPrint(UnityStrWas); - UnityPrintNumberByStyle(actual_val, style); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } - /* Walk through array by incrementing the pointers */ - if (flags == UNITY_ARRAY_TO_ARRAY) - { - expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment); - } - actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment); - } -} - -/*-----------------------------------------------*/ -#ifndef UNITY_EXCLUDE_FLOAT -/* Wrap this define in a function with variable types as float or double */ -#define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff) \ - if (isinf(expected) && isinf(actual) && (((expected) < 0) == ((actual) < 0))) return 1; \ - if (UNITY_NAN_CHECK) return 1; \ - (diff) = (actual) - (expected); \ - if ((diff) < 0) (diff) = -(diff); \ - if ((delta) < 0) (delta) = -(delta); \ - return !(isnan(diff) || isinf(diff) || ((diff) > (delta))) - /* This first part of this condition will catch any NaN or Infinite values */ -#ifndef UNITY_NAN_NOT_EQUAL_NAN - #define UNITY_NAN_CHECK isnan(expected) && isnan(actual) -#else - #define UNITY_NAN_CHECK 0 -#endif - -#ifndef UNITY_EXCLUDE_FLOAT_PRINT - #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ - do { \ - UnityPrint(UnityStrExpected); \ - UnityPrintFloat(expected); \ - UnityPrint(UnityStrWas); \ - UnityPrintFloat(actual); \ - } while (0) -#else - #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ - UnityPrint(UnityStrDelta) -#endif /* UNITY_EXCLUDE_FLOAT_PRINT */ - -/*-----------------------------------------------*/ -static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual) -{ - UNITY_FLOAT diff; - UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); -} - -/*-----------------------------------------------*/ -void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, - UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLAGS_T flags) -{ - UNITY_UINT32 elements = num_elements; - UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected; - UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual; - - RETURN_IF_FAIL_OR_IGNORE; - - if (elements == 0) - { - UnityPrintPointlessAndBail(); - } - - if (expected == actual) - { - return; /* Both are NULL or same pointer */ - } - - if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) - { - UNITY_FAIL_AND_BAIL; - } - - while (elements--) - { - if (!UnityFloatsWithin(*ptr_expected * UNITY_FLOAT_PRECISION, *ptr_expected, *ptr_actual)) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrElement); - UnityPrintNumberUnsigned(num_elements - elements - 1); - UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } - if (flags == UNITY_ARRAY_TO_ARRAY) - { - ptr_expected++; - } - ptr_actual++; - } -} - -/*-----------------------------------------------*/ -void UnityAssertFloatsWithin(const UNITY_FLOAT delta, - const UNITY_FLOAT expected, - const UNITY_FLOAT actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber) -{ - RETURN_IF_FAIL_OR_IGNORE; - - - if (!UnityFloatsWithin(delta, expected, actual)) - { - UnityTestResultsFailBegin(lineNumber); - UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } -} - -/*-----------------------------------------------*/ -void UnityAssertFloatSpecial(const UNITY_FLOAT actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLOAT_TRAIT_T style) -{ - const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; - UNITY_INT should_be_trait = ((UNITY_INT)style & 1); - UNITY_INT is_trait = !should_be_trait; - UNITY_INT trait_index = (UNITY_INT)(style >> 1); - - RETURN_IF_FAIL_OR_IGNORE; - - switch (style) - { - case UNITY_FLOAT_IS_INF: - case UNITY_FLOAT_IS_NOT_INF: - is_trait = isinf(actual) && (actual > 0); - break; - case UNITY_FLOAT_IS_NEG_INF: - case UNITY_FLOAT_IS_NOT_NEG_INF: - is_trait = isinf(actual) && (actual < 0); - break; - - case UNITY_FLOAT_IS_NAN: - case UNITY_FLOAT_IS_NOT_NAN: - is_trait = isnan(actual) ? 1 : 0; - break; - - case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ - case UNITY_FLOAT_IS_NOT_DET: - is_trait = !isinf(actual) && !isnan(actual); - break; - - default: /* including UNITY_FLOAT_INVALID_TRAIT */ - trait_index = 0; - trait_names[0] = UnityStrInvalidFloatTrait; - break; - } - - if (is_trait != should_be_trait) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrExpected); - if (!should_be_trait) - { - UnityPrint(UnityStrNot); - } - UnityPrint(trait_names[trait_index]); - UnityPrint(UnityStrWas); -#ifndef UNITY_EXCLUDE_FLOAT_PRINT - UnityPrintFloat((UNITY_DOUBLE)actual); -#else - if (should_be_trait) - { - UnityPrint(UnityStrNot); - } - UnityPrint(trait_names[trait_index]); -#endif - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } -} - -#endif /* not UNITY_EXCLUDE_FLOAT */ - -/*-----------------------------------------------*/ -#ifndef UNITY_EXCLUDE_DOUBLE -static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual) -{ - UNITY_DOUBLE diff; - UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); -} - -/*-----------------------------------------------*/ -void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, - UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLAGS_T flags) -{ - UNITY_UINT32 elements = num_elements; - UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected; - UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual; - - RETURN_IF_FAIL_OR_IGNORE; - - if (elements == 0) - { - UnityPrintPointlessAndBail(); - } - - if (expected == actual) - { - return; /* Both are NULL or same pointer */ - } - - if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) - { - UNITY_FAIL_AND_BAIL; - } - - while (elements--) - { - if (!UnityDoublesWithin(*ptr_expected * UNITY_DOUBLE_PRECISION, *ptr_expected, *ptr_actual)) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrElement); - UnityPrintNumberUnsigned(num_elements - elements - 1); - UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } - if (flags == UNITY_ARRAY_TO_ARRAY) - { - ptr_expected++; - } - ptr_actual++; - } -} - -/*-----------------------------------------------*/ -void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, - const UNITY_DOUBLE expected, - const UNITY_DOUBLE actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber) -{ - RETURN_IF_FAIL_OR_IGNORE; - - if (!UnityDoublesWithin(delta, expected, actual)) - { - UnityTestResultsFailBegin(lineNumber); - UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } -} - -/*-----------------------------------------------*/ -void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLOAT_TRAIT_T style) -{ - const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; - UNITY_INT should_be_trait = ((UNITY_INT)style & 1); - UNITY_INT is_trait = !should_be_trait; - UNITY_INT trait_index = (UNITY_INT)(style >> 1); - - RETURN_IF_FAIL_OR_IGNORE; - - switch (style) - { - case UNITY_FLOAT_IS_INF: - case UNITY_FLOAT_IS_NOT_INF: - is_trait = isinf(actual) && (actual > 0); - break; - case UNITY_FLOAT_IS_NEG_INF: - case UNITY_FLOAT_IS_NOT_NEG_INF: - is_trait = isinf(actual) && (actual < 0); - break; - - case UNITY_FLOAT_IS_NAN: - case UNITY_FLOAT_IS_NOT_NAN: - is_trait = isnan(actual) ? 1 : 0; - break; - - case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ - case UNITY_FLOAT_IS_NOT_DET: - is_trait = !isinf(actual) && !isnan(actual); - break; - - default: /* including UNITY_FLOAT_INVALID_TRAIT */ - trait_index = 0; - trait_names[0] = UnityStrInvalidFloatTrait; - break; - } - - if (is_trait != should_be_trait) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrExpected); - if (!should_be_trait) - { - UnityPrint(UnityStrNot); - } - UnityPrint(trait_names[trait_index]); - UnityPrint(UnityStrWas); -#ifndef UNITY_EXCLUDE_FLOAT_PRINT - UnityPrintFloat(actual); -#else - if (should_be_trait) - { - UnityPrint(UnityStrNot); - } - UnityPrint(trait_names[trait_index]); -#endif - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } -} - -#endif /* not UNITY_EXCLUDE_DOUBLE */ - -/*-----------------------------------------------*/ -void UnityAssertNumbersWithin(const UNITY_UINT delta, - const UNITY_INT expected, - const UNITY_INT actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_DISPLAY_STYLE_T style) -{ - RETURN_IF_FAIL_OR_IGNORE; - - if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) - { - if (actual > expected) - { - Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); - } - else - { - Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); - } - } - else - { - if ((UNITY_UINT)actual > (UNITY_UINT)expected) - { - Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); - } - else - { - Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); - } - } - - if (Unity.CurrentTestFailed) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrDelta); - UnityPrintNumberByStyle((UNITY_INT)delta, style); - UnityPrint(UnityStrExpected); - UnityPrintNumberByStyle(expected, style); - UnityPrint(UnityStrWas); - UnityPrintNumberByStyle(actual, style); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } -} - -/*-----------------------------------------------*/ -void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, - UNITY_INTERNAL_PTR expected, - UNITY_INTERNAL_PTR actual, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_DISPLAY_STYLE_T style, - const UNITY_FLAGS_T flags) -{ - UNITY_UINT32 elements = num_elements; - unsigned int length = style & 0xF; - unsigned int increment = 0; - - RETURN_IF_FAIL_OR_IGNORE; - - if (num_elements == 0) - { - UnityPrintPointlessAndBail(); - } - - if (expected == actual) - { - return; /* Both are NULL or same pointer */ - } - - if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) - { - UNITY_FAIL_AND_BAIL; - } - - while ((elements > 0) && (elements--)) - { - UNITY_INT expect_val; - UNITY_INT actual_val; - - switch (length) - { - case 1: - expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; - actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; - increment = sizeof(UNITY_INT8); - break; - - case 2: - expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; - actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; - increment = sizeof(UNITY_INT16); - break; - -#ifdef UNITY_SUPPORT_64 - case 8: - expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; - actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; - increment = sizeof(UNITY_INT64); - break; -#endif - - default: /* default is length 4 bytes */ - case 4: - expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; - actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; - increment = sizeof(UNITY_INT32); - length = 4; - break; - } - - if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) - { - if (actual_val > expect_val) - { - Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); - } - else - { - Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); - } - } - else - { - if ((UNITY_UINT)actual_val > (UNITY_UINT)expect_val) - { - Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); - } - else - { - Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); - } - } - - if (Unity.CurrentTestFailed) - { - if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) - { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ - UNITY_INT mask = 1; - mask = (mask << 8 * length) - 1; - expect_val &= mask; - actual_val &= mask; - } - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrDelta); - UnityPrintNumberByStyle((UNITY_INT)delta, style); - UnityPrint(UnityStrElement); - UnityPrintNumberUnsigned(num_elements - elements - 1); - UnityPrint(UnityStrExpected); - UnityPrintNumberByStyle(expect_val, style); - UnityPrint(UnityStrWas); - UnityPrintNumberByStyle(actual_val, style); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } - /* Walk through array by incrementing the pointers */ - if (flags == UNITY_ARRAY_TO_ARRAY) - { - expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment); - } - actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment); - } -} - -/*-----------------------------------------------*/ -void UnityAssertEqualString(const char* expected, - const char* actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber) -{ - UNITY_UINT32 i; - - RETURN_IF_FAIL_OR_IGNORE; - - /* if both pointers not null compare the strings */ - if (expected && actual) - { - for (i = 0; expected[i] || actual[i]; i++) - { - if (expected[i] != actual[i]) - { - Unity.CurrentTestFailed = 1; - break; - } - } - } - else - { /* handle case of one pointers being null (if both null, test should pass) */ - if (expected != actual) - { - Unity.CurrentTestFailed = 1; - } - } - - if (Unity.CurrentTestFailed) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrintExpectedAndActualStrings(expected, actual); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } -} - -/*-----------------------------------------------*/ -void UnityAssertEqualStringLen(const char* expected, - const char* actual, - const UNITY_UINT32 length, - const char* msg, - const UNITY_LINE_TYPE lineNumber) -{ - UNITY_UINT32 i; - - RETURN_IF_FAIL_OR_IGNORE; - - /* if both pointers not null compare the strings */ - if (expected && actual) - { - for (i = 0; (i < length) && (expected[i] || actual[i]); i++) - { - if (expected[i] != actual[i]) - { - Unity.CurrentTestFailed = 1; - break; - } - } - } - else - { /* handle case of one pointers being null (if both null, test should pass) */ - if (expected != actual) - { - Unity.CurrentTestFailed = 1; - } - } - - if (Unity.CurrentTestFailed) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrintExpectedAndActualStringsLen(expected, actual, length); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } -} - -/*-----------------------------------------------*/ -void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, - const char** actual, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLAGS_T flags) -{ - UNITY_UINT32 i = 0; - UNITY_UINT32 j = 0; - const char* expd = NULL; - const char* act = NULL; - - RETURN_IF_FAIL_OR_IGNORE; - - /* if no elements, it's an error */ - if (num_elements == 0) - { - UnityPrintPointlessAndBail(); - } - - if ((const void*)expected == (const void*)actual) - { - return; /* Both are NULL or same pointer */ - } - - if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) - { - UNITY_FAIL_AND_BAIL; - } - - if (flags != UNITY_ARRAY_TO_ARRAY) - { - expd = (const char*)expected; - } - - do - { - act = actual[j]; - if (flags == UNITY_ARRAY_TO_ARRAY) - { - expd = ((const char* const*)expected)[j]; - } - - /* if both pointers not null compare the strings */ - if (expd && act) - { - for (i = 0; expd[i] || act[i]; i++) - { - if (expd[i] != act[i]) - { - Unity.CurrentTestFailed = 1; - break; - } - } - } - else - { /* handle case of one pointers being null (if both null, test should pass) */ - if (expd != act) - { - Unity.CurrentTestFailed = 1; - } - } - - if (Unity.CurrentTestFailed) - { - UnityTestResultsFailBegin(lineNumber); - if (num_elements > 1) - { - UnityPrint(UnityStrElement); - UnityPrintNumberUnsigned(j); - } - UnityPrintExpectedAndActualStrings(expd, act); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } - } while (++j < num_elements); -} - -/*-----------------------------------------------*/ -void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected, - UNITY_INTERNAL_PTR actual, - const UNITY_UINT32 length, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLAGS_T flags) -{ - UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; - UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual; - UNITY_UINT32 elements = num_elements; - UNITY_UINT32 bytes; - - RETURN_IF_FAIL_OR_IGNORE; - - if ((elements == 0) || (length == 0)) - { - UnityPrintPointlessAndBail(); - } - - if (expected == actual) - { - return; /* Both are NULL or same pointer */ - } - - if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) - { - UNITY_FAIL_AND_BAIL; - } - - while (elements--) - { - bytes = length; - while (bytes--) - { - if (*ptr_exp != *ptr_act) - { - UnityTestResultsFailBegin(lineNumber); - UnityPrint(UnityStrMemory); - if (num_elements > 1) - { - UnityPrint(UnityStrElement); - UnityPrintNumberUnsigned(num_elements - elements - 1); - } - UnityPrint(UnityStrByte); - UnityPrintNumberUnsigned(length - bytes - 1); - UnityPrint(UnityStrExpected); - UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8); - UnityPrint(UnityStrWas); - UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8); - UnityAddMsgIfSpecified(msg); - UNITY_FAIL_AND_BAIL; - } - ptr_exp++; - ptr_act++; - } - if (flags == UNITY_ARRAY_TO_VAL) - { - ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; - } - } -} - -/*-----------------------------------------------*/ - -static union -{ - UNITY_INT8 i8; - UNITY_INT16 i16; - UNITY_INT32 i32; -#ifdef UNITY_SUPPORT_64 - UNITY_INT64 i64; -#endif -#ifndef UNITY_EXCLUDE_FLOAT - float f; -#endif -#ifndef UNITY_EXCLUDE_DOUBLE - double d; -#endif -} UnityQuickCompare; - -UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size) -{ - switch(size) - { - case 1: - UnityQuickCompare.i8 = (UNITY_INT8)num; - return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8); - - case 2: - UnityQuickCompare.i16 = (UNITY_INT16)num; - return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16); - -#ifdef UNITY_SUPPORT_64 - case 8: - UnityQuickCompare.i64 = (UNITY_INT64)num; - return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64); -#endif - - default: /* 4 bytes */ - UnityQuickCompare.i32 = (UNITY_INT32)num; - return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32); - } -} - -#ifndef UNITY_EXCLUDE_FLOAT -/*-----------------------------------------------*/ -UNITY_INTERNAL_PTR UnityFloatToPtr(const float num) -{ - UnityQuickCompare.f = num; - return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f); -} -#endif - -#ifndef UNITY_EXCLUDE_DOUBLE -/*-----------------------------------------------*/ -UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) -{ - UnityQuickCompare.d = num; - return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d); -} -#endif - -/*----------------------------------------------- - * printf helper function - *-----------------------------------------------*/ -#ifdef UNITY_INCLUDE_PRINT_FORMATTED -static void UnityPrintFVA(const char* format, va_list va) -{ - const char* pch = format; - if (pch != NULL) - { - while (*pch) - { - /* format identification character */ - if (*pch == '%') - { - pch++; - - if (pch != NULL) - { - switch (*pch) - { - case 'd': - case 'i': - { - const int number = va_arg(va, int); - UnityPrintNumber((UNITY_INT)number); - break; - } -#ifndef UNITY_EXCLUDE_FLOAT_PRINT - case 'f': - case 'g': - { - const double number = va_arg(va, double); - UnityPrintFloat((UNITY_DOUBLE)number); - break; - } -#endif - case 'u': - { - const unsigned int number = va_arg(va, unsigned int); - UnityPrintNumberUnsigned((UNITY_UINT)number); - break; - } - case 'b': - { - const unsigned int number = va_arg(va, unsigned int); - const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1; - UNITY_OUTPUT_CHAR('0'); - UNITY_OUTPUT_CHAR('b'); - UnityPrintMask(mask, (UNITY_UINT)number); - break; - } - case 'x': - case 'X': - case 'p': - { - const unsigned int number = va_arg(va, unsigned int); - UNITY_OUTPUT_CHAR('0'); - UNITY_OUTPUT_CHAR('x'); - UnityPrintNumberHex((UNITY_UINT)number, 8); - break; - } - case 'c': - { - const int ch = va_arg(va, int); - UnityPrintChar((const char *)&ch); - break; - } - case 's': - { - const char * string = va_arg(va, const char *); - UnityPrint(string); - break; - } - case '%': - { - UnityPrintChar(pch); - break; - } - default: - { - /* print the unknown format character */ - UNITY_OUTPUT_CHAR('%'); - UnityPrintChar(pch); - break; - } - } - } - } -#ifdef UNITY_OUTPUT_COLOR - /* print ANSI escape code */ - else if ((*pch == 27) && (*(pch + 1) == '[')) - { - pch += UnityPrintAnsiEscapeString(pch); - continue; - } -#endif - else if (*pch == '\n') - { - UNITY_PRINT_EOL(); - } - else - { - UnityPrintChar(pch); - } - - pch++; - } - } -} - -void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...) -{ - UnityTestResultsBegin(Unity.TestFile, line); - UnityPrint("INFO"); - if(format != NULL) - { - UnityPrint(": "); - va_list va; - va_start(va, format); - UnityPrintFVA(format, va); - va_end(va); - } - UNITY_PRINT_EOL(); -} -#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */ - - -/*----------------------------------------------- - * Control Functions - *-----------------------------------------------*/ - -/*-----------------------------------------------*/ -void UnityFail(const char* msg, const UNITY_LINE_TYPE line) -{ - RETURN_IF_FAIL_OR_IGNORE; - - UnityTestResultsBegin(Unity.TestFile, line); - UnityPrint(UnityStrFail); - if (msg != NULL) - { - UNITY_OUTPUT_CHAR(':'); - -#ifdef UNITY_PRINT_TEST_CONTEXT - UNITY_PRINT_TEST_CONTEXT(); -#endif -#ifndef UNITY_EXCLUDE_DETAILS - if (Unity.CurrentDetail1) - { - UnityPrint(UnityStrDetail1Name); - UnityPrint(Unity.CurrentDetail1); - if (Unity.CurrentDetail2) - { - UnityPrint(UnityStrDetail2Name); - UnityPrint(Unity.CurrentDetail2); - } - UnityPrint(UnityStrSpacer); - } -#endif - if (msg[0] != ' ') - { - UNITY_OUTPUT_CHAR(' '); - } - UnityPrint(msg); - } - - UNITY_FAIL_AND_BAIL; -} - -/*-----------------------------------------------*/ -void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line) -{ - RETURN_IF_FAIL_OR_IGNORE; - - UnityTestResultsBegin(Unity.TestFile, line); - UnityPrint(UnityStrIgnore); - if (msg != NULL) - { - UNITY_OUTPUT_CHAR(':'); - UNITY_OUTPUT_CHAR(' '); - UnityPrint(msg); - } - UNITY_IGNORE_AND_BAIL; -} - -/*-----------------------------------------------*/ -void UnityMessage(const char* msg, const UNITY_LINE_TYPE line) -{ - UnityTestResultsBegin(Unity.TestFile, line); - UnityPrint("INFO"); - if (msg != NULL) - { - UNITY_OUTPUT_CHAR(':'); - UNITY_OUTPUT_CHAR(' '); - UnityPrint(msg); - } - UNITY_PRINT_EOL(); -} - -/*-----------------------------------------------*/ -/* If we have not defined our own test runner, then include our default test runner to make life easier */ -#ifndef UNITY_SKIP_DEFAULT_RUNNER -void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) -{ - Unity.CurrentTestName = FuncName; - Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; - Unity.NumberOfTests++; - UNITY_CLR_DETAILS(); - UNITY_EXEC_TIME_START(); - if (TEST_PROTECT()) - { - setUp(); - Func(); - } - if (TEST_PROTECT()) - { - tearDown(); - } - UNITY_EXEC_TIME_STOP(); - UnityConcludeTest(); -} -#endif - -/*-----------------------------------------------*/ -void UnitySetTestFile(const char* filename) -{ - Unity.TestFile = filename; -} - -/*-----------------------------------------------*/ -void UnityBegin(const char* filename) -{ - Unity.TestFile = filename; - Unity.CurrentTestName = NULL; - Unity.CurrentTestLineNumber = 0; - Unity.NumberOfTests = 0; - Unity.TestFailures = 0; - Unity.TestIgnores = 0; - Unity.CurrentTestFailed = 0; - Unity.CurrentTestIgnored = 0; - - UNITY_CLR_DETAILS(); - UNITY_OUTPUT_START(); -} - -/*-----------------------------------------------*/ -int UnityEnd(void) -{ - UNITY_PRINT_EOL(); - UnityPrint(UnityStrBreaker); - UNITY_PRINT_EOL(); - UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests)); - UnityPrint(UnityStrResultsTests); - UnityPrintNumber((UNITY_INT)(Unity.TestFailures)); - UnityPrint(UnityStrResultsFailures); - UnityPrintNumber((UNITY_INT)(Unity.TestIgnores)); - UnityPrint(UnityStrResultsIgnored); - UNITY_PRINT_EOL(); - if (Unity.TestFailures == 0U) - { - UnityPrint(UnityStrOk); - } - else - { - UnityPrint(UnityStrFail); -#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL - UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D'); -#endif - } - UNITY_PRINT_EOL(); - UNITY_FLUSH_CALL(); - UNITY_OUTPUT_COMPLETE(); - return (int)(Unity.TestFailures); -} - -/*----------------------------------------------- - * Command Line Argument Support - *-----------------------------------------------*/ -#ifdef UNITY_USE_COMMAND_LINE_ARGS - -char* UnityOptionIncludeNamed = NULL; -char* UnityOptionExcludeNamed = NULL; -int UnityVerbosity = 1; - -/*-----------------------------------------------*/ -int UnityParseOptions(int argc, char** argv) -{ - int i; - UnityOptionIncludeNamed = NULL; - UnityOptionExcludeNamed = NULL; - - for (i = 1; i < argc; i++) - { - if (argv[i][0] == '-') - { - switch (argv[i][1]) - { - case 'l': /* list tests */ - return -1; - case 'n': /* include tests with name including this string */ - case 'f': /* an alias for -n */ - if (argv[i][2] == '=') - { - UnityOptionIncludeNamed = &argv[i][3]; - } - else if (++i < argc) - { - UnityOptionIncludeNamed = argv[i]; - } - else - { - UnityPrint("ERROR: No Test String to Include Matches For"); - UNITY_PRINT_EOL(); - return 1; - } - break; - case 'q': /* quiet */ - UnityVerbosity = 0; - break; - case 'v': /* verbose */ - UnityVerbosity = 2; - break; - case 'x': /* exclude tests with name including this string */ - if (argv[i][2] == '=') - { - UnityOptionExcludeNamed = &argv[i][3]; - } - else if (++i < argc) - { - UnityOptionExcludeNamed = argv[i]; - } - else - { - UnityPrint("ERROR: No Test String to Exclude Matches For"); - UNITY_PRINT_EOL(); - return 1; - } - break; - default: - UnityPrint("ERROR: Unknown Option "); - UNITY_OUTPUT_CHAR(argv[i][1]); - UNITY_PRINT_EOL(); - return 1; - } - } - } - - return 0; -} - -/*-----------------------------------------------*/ -int IsStringInBiggerString(const char* longstring, const char* shortstring) -{ - const char* lptr = longstring; - const char* sptr = shortstring; - const char* lnext = lptr; - - if (*sptr == '*') - { - return 1; - } - - while (*lptr) - { - lnext = lptr + 1; - - /* If they current bytes match, go on to the next bytes */ - while (*lptr && *sptr && (*lptr == *sptr)) - { - lptr++; - sptr++; - - /* We're done if we match the entire string or up to a wildcard */ - if (*sptr == '*') - return 1; - if (*sptr == ',') - return 1; - if (*sptr == '"') - return 1; - if (*sptr == '\'') - return 1; - if (*sptr == ':') - return 2; - if (*sptr == 0) - return 1; - } - - /* Otherwise we start in the long pointer 1 character further and try again */ - lptr = lnext; - sptr = shortstring; - } - - return 0; -} - -/*-----------------------------------------------*/ -int UnityStringArgumentMatches(const char* str) -{ - int retval; - const char* ptr1; - const char* ptr2; - const char* ptrf; - - /* Go through the options and get the substrings for matching one at a time */ - ptr1 = str; - while (ptr1[0] != 0) - { - if ((ptr1[0] == '"') || (ptr1[0] == '\'')) - { - ptr1++; - } - - /* look for the start of the next partial */ - ptr2 = ptr1; - ptrf = 0; - do - { - ptr2++; - if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')) - { - ptrf = &ptr2[1]; - } - } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')); - - while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ','))) - { - ptr2++; - } - - /* done if complete filename match */ - retval = IsStringInBiggerString(Unity.TestFile, ptr1); - if (retval == 1) - { - return retval; - } - - /* done if testname match after filename partial match */ - if ((retval == 2) && (ptrf != 0)) - { - if (IsStringInBiggerString(Unity.CurrentTestName, ptrf)) - { - return 1; - } - } - - /* done if complete testname match */ - if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1) - { - return 1; - } - - ptr1 = ptr2; - } - - /* we couldn't find a match for any substrings */ - return 0; -} - -/*-----------------------------------------------*/ -int UnityTestMatches(void) -{ - /* Check if this test name matches the included test pattern */ - int retval; - if (UnityOptionIncludeNamed) - { - retval = UnityStringArgumentMatches(UnityOptionIncludeNamed); - } - else - { - retval = 1; - } - - /* Check if this test name matches the excluded test pattern */ - if (UnityOptionExcludeNamed) - { - if (UnityStringArgumentMatches(UnityOptionExcludeNamed)) - { - retval = 0; - } - } - - return retval; -} - -#endif /* UNITY_USE_COMMAND_LINE_ARGS */ -/*-----------------------------------------------*/ diff --git a/taskchampion/integration-tests/src/bindings_tests/unity/unity.h b/taskchampion/integration-tests/src/bindings_tests/unity/unity.h deleted file mode 100644 index 14225a354..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/unity/unity.h +++ /dev/null @@ -1,661 +0,0 @@ -/* ========================================== - Unity Project - A Test Framework for C - Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams - [Released under MIT License. Please refer to license.txt for details] -========================================== */ - -#ifndef UNITY_FRAMEWORK_H -#define UNITY_FRAMEWORK_H -#define UNITY - -#define UNITY_VERSION_MAJOR 2 -#define UNITY_VERSION_MINOR 5 -#define UNITY_VERSION_BUILD 4 -#define UNITY_VERSION ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD) - -#ifdef __cplusplus -extern "C" -{ -#endif - -#include "unity_internals.h" - -/*------------------------------------------------------- - * Test Setup / Teardown - *-------------------------------------------------------*/ - -/* These functions are intended to be called before and after each test. - * If using unity directly, these will need to be provided for each test - * executable built. If you are using the test runner generator and/or - * Ceedling, these are optional. */ -void setUp(void); -void tearDown(void); - -/* These functions are intended to be called at the beginning and end of an - * entire test suite. suiteTearDown() is passed the number of tests that - * failed, and its return value becomes the exit code of main(). If using - * Unity directly, you're in charge of calling these if they are desired. - * If using Ceedling or the test runner generator, these will be called - * automatically if they exist. */ -void suiteSetUp(void); -int suiteTearDown(int num_failures); - -/*------------------------------------------------------- - * Test Reset and Verify - *-------------------------------------------------------*/ - -/* These functions are intended to be called before during tests in order - * to support complex test loops, etc. Both are NOT built into Unity. Instead - * the test runner generator will create them. resetTest will run teardown and - * setup again, verifying any end-of-test needs between. verifyTest will only - * run the verification. */ -void resetTest(void); -void verifyTest(void); - -/*------------------------------------------------------- - * Configuration Options - *------------------------------------------------------- - * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above. - - * Integers/longs/pointers - * - Unity attempts to automatically discover your integer sizes - * - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in - * - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in - * - If you cannot use the automatic methods above, you can force Unity by using these options: - * - define UNITY_SUPPORT_64 - * - set UNITY_INT_WIDTH - * - set UNITY_LONG_WIDTH - * - set UNITY_POINTER_WIDTH - - * Floats - * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons - * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT - * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats - * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons - * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default) - * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE - * - define UNITY_DOUBLE_TYPE to specify something other than double - * - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors - - * Output - * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired - * - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure - - * Optimization - * - by default, line numbers are stored in unsigned shorts. Define UNITY_LINE_TYPE with a different type if your files are huge - * - by default, test and failure counters are unsigned shorts. Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests. - - * Test Cases - * - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script - - * Parameterized Tests - * - you'll want to create a define of TEST_CASE(...) which basically evaluates to nothing - - * Tests with Arguments - * - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity - - *------------------------------------------------------- - * Basic Fail and Ignore - *-------------------------------------------------------*/ - -#define TEST_FAIL_MESSAGE(message) UNITY_TEST_FAIL(__LINE__, (message)) -#define TEST_FAIL() UNITY_TEST_FAIL(__LINE__, NULL) -#define TEST_IGNORE_MESSAGE(message) UNITY_TEST_IGNORE(__LINE__, (message)) -#define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) -#define TEST_MESSAGE(message) UnityMessage((message), __LINE__) -#define TEST_ONLY() -#ifdef UNITY_INCLUDE_PRINT_FORMATTED -#define TEST_PRINTF(message, ...) UnityPrintF(__LINE__, (message), __VA_ARGS__) -#endif - -/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. - * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ -#define TEST_PASS() TEST_ABORT() -#define TEST_PASS_MESSAGE(message) do { UnityMessage((message), __LINE__); TEST_ABORT(); } while (0) - -/* This macro does nothing, but it is useful for build tools (like Ceedling) to make use of this to figure out - * which files should be linked to in order to perform a test. Use it like TEST_FILE("sandwiches.c") */ -#define TEST_FILE(a) - -/*------------------------------------------------------- - * Test Asserts (simple) - *-------------------------------------------------------*/ - -/* Boolean */ -#define TEST_ASSERT(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expression Evaluated To FALSE") -#define TEST_ASSERT_TRUE(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expected TRUE Was FALSE") -#define TEST_ASSERT_UNLESS(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expression Evaluated To TRUE") -#define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") -#define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") -#define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") -#define TEST_ASSERT_EMPTY(pointer) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, " Expected Empty") -#define TEST_ASSERT_NOT_EMPTY(pointer) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, " Expected Non-Empty") - -/* Integers (of all sizes) */ -#define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_INT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_INT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_INT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_INT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_UINT(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_UINT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_UINT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_UINT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_UINT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_size_t(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_HEX(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_HEX8(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_HEX16(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_HEX32(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_CHAR(expected, actual) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(-1), (actual), __LINE__, NULL) -#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(0), (actual), __LINE__, NULL) -#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(-1), (actual), __LINE__, NULL) -#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(0), (actual), __LINE__, NULL) - -/* Integer Not Equal To (of all sizes) */ -#define TEST_ASSERT_NOT_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) - -/* Integer Greater Than/ Less Than (of all sizes) */ -#define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, NULL) - -#define TEST_ASSERT_LESS_THAN(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, NULL) - -#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) - -#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) -#define TEST_ASSERT_LESS_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) - -/* Integer Ranges (of all sizes) */ -#define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_size_t_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_CHAR_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, NULL) - -/* Integer Array Ranges (of all sizes) */ -#define TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_size_t_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_HEX_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) -#define TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) - - -/* Structs and Strings */ -#define TEST_ASSERT_EQUAL_PTR(expected, actual) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_STRING(expected, actual) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL) - -/* Arrays */ -#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_size_t_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) - -/* Arrays Compared To Single Value */ -#define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_size_t(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, NULL) - -/* Floating Point (If Enabled) */ -#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_FLOAT_IS_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL) -#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL) -#define TEST_ASSERT_FLOAT_IS_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL) -#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL) -#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL) -#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL) -#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL) -#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL) - -/* Double (If Enabled) */ -#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL) -#define TEST_ASSERT_DOUBLE_IS_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL) -#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL) -#define TEST_ASSERT_DOUBLE_IS_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL) -#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL) -#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL) -#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL) -#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL) -#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL) - -/* Shorthand */ -#ifdef UNITY_SHORTHAND_AS_OLD -#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") -#endif -#ifdef UNITY_SHORTHAND_AS_INT -#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) -#endif -#ifdef UNITY_SHORTHAND_AS_MEM -#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, NULL) -#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) -#endif -#ifdef UNITY_SHORTHAND_AS_RAW -#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, " Expected Equal") -#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") -#endif -#ifdef UNITY_SHORTHAND_AS_NONE -#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) -#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) -#endif - -/*------------------------------------------------------- - * Test Asserts (with additional messages) - *-------------------------------------------------------*/ - -/* Boolean */ -#define TEST_ASSERT_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) -#define TEST_ASSERT_TRUE_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) -#define TEST_ASSERT_UNLESS_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) -#define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) -#define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) -#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) -#define TEST_ASSERT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, (message)) -#define TEST_ASSERT_NOT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, (message)) - -/* Integers (of all sizes) */ -#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_size_t_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) -#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message)) -#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) -#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message)) - -/* Integer Not Equal To (of all sizes) */ -#define TEST_ASSERT_NOT_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) - - -/* Integer Greater Than/ Less Than (of all sizes) */ -#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, (message)) - -#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, (message)) - -#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) - -#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) -#define TEST_ASSERT_LESS_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) - -/* Integer Ranges (of all sizes) */ -#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_size_t_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_CHAR_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, (message)) - -/* Integer Array Ranges (of all sizes) */ -#define TEST_ASSERT_INT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_INT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_INT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_INT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_INT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_UINT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_UINT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_UINT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_UINT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_UINT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_size_t_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_HEX_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_HEX8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_HEX16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_HEX32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_HEX64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) -#define TEST_ASSERT_CHAR_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) - - -/* Structs and Strings */ -#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message)) - -/* Arrays */ -#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_size_t_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_CHAR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) - -/* Arrays Compared To Single Value*/ -#define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_size_t_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_CHAR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, (message)) - -/* Floating Point (If Enabled) */ -#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message)) -#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message)) -#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message)) -#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message)) -#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message)) -#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message)) -#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message)) -#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message)) - -/* Double (If Enabled) */ -#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message)) -#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message)) -#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message)) -#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message)) -#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message)) -#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message)) -#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message)) -#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message)) - -/* Shorthand */ -#ifdef UNITY_SHORTHAND_AS_OLD -#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) -#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message)) -#endif -#ifdef UNITY_SHORTHAND_AS_INT -#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, message) -#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) -#endif -#ifdef UNITY_SHORTHAND_AS_MEM -#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, message) -#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) -#endif -#ifdef UNITY_SHORTHAND_AS_RAW -#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, message) -#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, message) -#endif -#ifdef UNITY_SHORTHAND_AS_NONE -#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) -#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) -#endif - -/* end of UNITY_FRAMEWORK_H */ -#ifdef __cplusplus -} -#endif -#endif diff --git a/taskchampion/integration-tests/src/bindings_tests/unity/unity_internals.h b/taskchampion/integration-tests/src/bindings_tests/unity/unity_internals.h deleted file mode 100644 index d303e8fe7..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/unity/unity_internals.h +++ /dev/null @@ -1,1053 +0,0 @@ -/* ========================================== - Unity Project - A Test Framework for C - Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams - [Released under MIT License. Please refer to license.txt for details] -========================================== */ - -#ifndef UNITY_INTERNALS_H -#define UNITY_INTERNALS_H - -#ifdef UNITY_INCLUDE_CONFIG_H -#include "unity_config.h" -#endif - -#ifndef UNITY_EXCLUDE_SETJMP_H -#include -#endif - -#ifndef UNITY_EXCLUDE_MATH_H -#include -#endif - -#ifndef UNITY_EXCLUDE_STDDEF_H -#include -#endif - -#ifdef UNITY_INCLUDE_PRINT_FORMATTED -#include -#endif - -/* Unity Attempts to Auto-Detect Integer Types - * Attempt 1: UINT_MAX, ULONG_MAX in , or default to 32 bits - * Attempt 2: UINTPTR_MAX in , or default to same size as long - * The user may override any of these derived constants: - * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */ -#ifndef UNITY_EXCLUDE_STDINT_H -#include -#endif - -#ifndef UNITY_EXCLUDE_LIMITS_H -#include -#endif - -#if defined(__GNUC__) || defined(__clang__) - #define UNITY_FUNCTION_ATTR(a) __attribute__((a)) -#else - #define UNITY_FUNCTION_ATTR(a) /* ignore */ -#endif - -#ifndef UNITY_NORETURN - #if defined(__cplusplus) - #if __cplusplus >= 201103L - #define UNITY_NORETURN [[ noreturn ]] - #endif - #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L - #include - #define UNITY_NORETURN noreturn - #endif -#endif -#ifndef UNITY_NORETURN - #define UNITY_NORETURN UNITY_FUNCTION_ATTR(noreturn) -#endif - -/*------------------------------------------------------- - * Guess Widths If Not Specified - *-------------------------------------------------------*/ - -/* Determine the size of an int, if not already specified. - * We cannot use sizeof(int), because it is not yet defined - * at this stage in the translation of the C program. - * Also sizeof(int) does return the size in addressable units on all platforms, - * which may not necessarily be the size in bytes. - * Therefore, infer it from UINT_MAX if possible. */ -#ifndef UNITY_INT_WIDTH - #ifdef UINT_MAX - #if (UINT_MAX == 0xFFFF) - #define UNITY_INT_WIDTH (16) - #elif (UINT_MAX == 0xFFFFFFFF) - #define UNITY_INT_WIDTH (32) - #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF) - #define UNITY_INT_WIDTH (64) - #endif - #else /* Set to default */ - #define UNITY_INT_WIDTH (32) - #endif /* UINT_MAX */ -#endif - -/* Determine the size of a long, if not already specified. */ -#ifndef UNITY_LONG_WIDTH - #ifdef ULONG_MAX - #if (ULONG_MAX == 0xFFFF) - #define UNITY_LONG_WIDTH (16) - #elif (ULONG_MAX == 0xFFFFFFFF) - #define UNITY_LONG_WIDTH (32) - #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF) - #define UNITY_LONG_WIDTH (64) - #endif - #else /* Set to default */ - #define UNITY_LONG_WIDTH (32) - #endif /* ULONG_MAX */ -#endif - -/* Determine the size of a pointer, if not already specified. */ -#ifndef UNITY_POINTER_WIDTH - #ifdef UINTPTR_MAX - #if (UINTPTR_MAX <= 0xFFFF) - #define UNITY_POINTER_WIDTH (16) - #elif (UINTPTR_MAX <= 0xFFFFFFFF) - #define UNITY_POINTER_WIDTH (32) - #elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF) - #define UNITY_POINTER_WIDTH (64) - #endif - #else /* Set to default */ - #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH - #endif /* UINTPTR_MAX */ -#endif - -/*------------------------------------------------------- - * Int Support (Define types based on detected sizes) - *-------------------------------------------------------*/ - -#if (UNITY_INT_WIDTH == 32) - typedef unsigned char UNITY_UINT8; - typedef unsigned short UNITY_UINT16; - typedef unsigned int UNITY_UINT32; - typedef signed char UNITY_INT8; - typedef signed short UNITY_INT16; - typedef signed int UNITY_INT32; -#elif (UNITY_INT_WIDTH == 16) - typedef unsigned char UNITY_UINT8; - typedef unsigned int UNITY_UINT16; - typedef unsigned long UNITY_UINT32; - typedef signed char UNITY_INT8; - typedef signed int UNITY_INT16; - typedef signed long UNITY_INT32; -#else - #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported) -#endif - -/*------------------------------------------------------- - * 64-bit Support - *-------------------------------------------------------*/ - -/* Auto-detect 64 Bit Support */ -#ifndef UNITY_SUPPORT_64 - #if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64 - #define UNITY_SUPPORT_64 - #endif -#endif - -/* 64-Bit Support Dependent Configuration */ -#ifndef UNITY_SUPPORT_64 - /* No 64-bit Support */ - typedef UNITY_UINT32 UNITY_UINT; - typedef UNITY_INT32 UNITY_INT; - #define UNITY_MAX_NIBBLES (8) /* Maximum number of nibbles in a UNITY_(U)INT */ -#else - /* 64-bit Support */ - #if (UNITY_LONG_WIDTH == 32) - typedef unsigned long long UNITY_UINT64; - typedef signed long long UNITY_INT64; - #elif (UNITY_LONG_WIDTH == 64) - typedef unsigned long UNITY_UINT64; - typedef signed long UNITY_INT64; - #else - #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported) - #endif - typedef UNITY_UINT64 UNITY_UINT; - typedef UNITY_INT64 UNITY_INT; - #define UNITY_MAX_NIBBLES (16) /* Maximum number of nibbles in a UNITY_(U)INT */ -#endif - -/*------------------------------------------------------- - * Pointer Support - *-------------------------------------------------------*/ - -#if (UNITY_POINTER_WIDTH == 32) - #define UNITY_PTR_TO_INT UNITY_INT32 - #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32 -#elif (UNITY_POINTER_WIDTH == 64) - #define UNITY_PTR_TO_INT UNITY_INT64 - #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64 -#elif (UNITY_POINTER_WIDTH == 16) - #define UNITY_PTR_TO_INT UNITY_INT16 - #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16 -#else - #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported) -#endif - -#ifndef UNITY_PTR_ATTRIBUTE - #define UNITY_PTR_ATTRIBUTE -#endif - -#ifndef UNITY_INTERNAL_PTR - #define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void* -#endif - -/*------------------------------------------------------- - * Float Support - *-------------------------------------------------------*/ - -#ifdef UNITY_EXCLUDE_FLOAT - -/* No Floating Point Support */ -#ifndef UNITY_EXCLUDE_DOUBLE -#define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */ -#endif -#ifndef UNITY_EXCLUDE_FLOAT_PRINT -#define UNITY_EXCLUDE_FLOAT_PRINT -#endif - -#else - -/* Floating Point Support */ -#ifndef UNITY_FLOAT_PRECISION -#define UNITY_FLOAT_PRECISION (0.00001f) -#endif -#ifndef UNITY_FLOAT_TYPE -#define UNITY_FLOAT_TYPE float -#endif -typedef UNITY_FLOAT_TYPE UNITY_FLOAT; - -/* isinf & isnan macros should be provided by math.h */ -#ifndef isinf -/* The value of Inf - Inf is NaN */ -#define isinf(n) (isnan((n) - (n)) && !isnan(n)) -#endif - -#ifndef isnan -/* NaN is the only floating point value that does NOT equal itself. - * Therefore if n != n, then it is NaN. */ -#define isnan(n) ((n != n) ? 1 : 0) -#endif - -#endif - -/*------------------------------------------------------- - * Double Float Support - *-------------------------------------------------------*/ - -/* unlike float, we DON'T include by default */ -#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE) - - /* No Floating Point Support */ - #ifndef UNITY_EXCLUDE_DOUBLE - #define UNITY_EXCLUDE_DOUBLE - #else - #undef UNITY_INCLUDE_DOUBLE - #endif - - #ifndef UNITY_EXCLUDE_FLOAT - #ifndef UNITY_DOUBLE_TYPE - #define UNITY_DOUBLE_TYPE double - #endif - typedef UNITY_FLOAT UNITY_DOUBLE; - /* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */ - #endif - -#else - - /* Double Floating Point Support */ - #ifndef UNITY_DOUBLE_PRECISION - #define UNITY_DOUBLE_PRECISION (1e-12) - #endif - - #ifndef UNITY_DOUBLE_TYPE - #define UNITY_DOUBLE_TYPE double - #endif - typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE; - -#endif - -/*------------------------------------------------------- - * Output Method: stdout (DEFAULT) - *-------------------------------------------------------*/ -#ifndef UNITY_OUTPUT_CHAR - /* Default to using putchar, which is defined in stdio.h */ - #include - #define UNITY_OUTPUT_CHAR(a) (void)putchar(a) -#else - /* If defined as something else, make sure we declare it here so it's ready for use */ - #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION - extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION; - #endif -#endif - -#ifndef UNITY_OUTPUT_FLUSH - #ifdef UNITY_USE_FLUSH_STDOUT - /* We want to use the stdout flush utility */ - #include - #define UNITY_OUTPUT_FLUSH() (void)fflush(stdout) - #else - /* We've specified nothing, therefore flush should just be ignored */ - #define UNITY_OUTPUT_FLUSH() (void)0 - #endif -#else - /* If defined as something else, make sure we declare it here so it's ready for use */ - #ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION - extern void UNITY_OUTPUT_FLUSH_HEADER_DECLARATION; - #endif -#endif - -#ifndef UNITY_OUTPUT_FLUSH -#define UNITY_FLUSH_CALL() -#else -#define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH() -#endif - -#ifndef UNITY_PRINT_EOL -#define UNITY_PRINT_EOL() UNITY_OUTPUT_CHAR('\n') -#endif - -#ifndef UNITY_OUTPUT_START -#define UNITY_OUTPUT_START() -#endif - -#ifndef UNITY_OUTPUT_COMPLETE -#define UNITY_OUTPUT_COMPLETE() -#endif - -#ifdef UNITY_INCLUDE_EXEC_TIME - #if !defined(UNITY_EXEC_TIME_START) && \ - !defined(UNITY_EXEC_TIME_STOP) && \ - !defined(UNITY_PRINT_EXEC_TIME) && \ - !defined(UNITY_TIME_TYPE) - /* If none any of these macros are defined then try to provide a default implementation */ - - #if defined(UNITY_CLOCK_MS) - /* This is a simple way to get a default implementation on platforms that support getting a millisecond counter */ - #define UNITY_TIME_TYPE UNITY_UINT - #define UNITY_EXEC_TIME_START() Unity.CurrentTestStartTime = UNITY_CLOCK_MS() - #define UNITY_EXEC_TIME_STOP() Unity.CurrentTestStopTime = UNITY_CLOCK_MS() - #define UNITY_PRINT_EXEC_TIME() { \ - UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \ - UnityPrint(" ("); \ - UnityPrintNumberUnsigned(execTimeMs); \ - UnityPrint(" ms)"); \ - } - #elif defined(_WIN32) - #include - #define UNITY_TIME_TYPE clock_t - #define UNITY_GET_TIME(t) t = (clock_t)((clock() * 1000) / CLOCKS_PER_SEC) - #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime) - #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime) - #define UNITY_PRINT_EXEC_TIME() { \ - UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \ - UnityPrint(" ("); \ - UnityPrintNumberUnsigned(execTimeMs); \ - UnityPrint(" ms)"); \ - } - #elif defined(__unix__) || defined(__APPLE__) - #include - #define UNITY_TIME_TYPE struct timespec - #define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t) - #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime) - #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime) - #define UNITY_PRINT_EXEC_TIME() { \ - UNITY_UINT execTimeMs = ((Unity.CurrentTestStopTime.tv_sec - Unity.CurrentTestStartTime.tv_sec) * 1000L); \ - execTimeMs += ((Unity.CurrentTestStopTime.tv_nsec - Unity.CurrentTestStartTime.tv_nsec) / 1000000L); \ - UnityPrint(" ("); \ - UnityPrintNumberUnsigned(execTimeMs); \ - UnityPrint(" ms)"); \ - } - #endif - #endif -#endif - -#ifndef UNITY_EXEC_TIME_START -#define UNITY_EXEC_TIME_START() do { /* nothing*/ } while (0) -#endif - -#ifndef UNITY_EXEC_TIME_STOP -#define UNITY_EXEC_TIME_STOP() do { /* nothing*/ } while (0) -#endif - -#ifndef UNITY_TIME_TYPE -#define UNITY_TIME_TYPE UNITY_UINT -#endif - -#ifndef UNITY_PRINT_EXEC_TIME -#define UNITY_PRINT_EXEC_TIME() do { /* nothing*/ } while (0) -#endif - -/*------------------------------------------------------- - * Footprint - *-------------------------------------------------------*/ - -#ifndef UNITY_LINE_TYPE -#define UNITY_LINE_TYPE UNITY_UINT -#endif - -#ifndef UNITY_COUNTER_TYPE -#define UNITY_COUNTER_TYPE UNITY_UINT -#endif - -/*------------------------------------------------------- - * Internal Structs Needed - *-------------------------------------------------------*/ - -typedef void (*UnityTestFunction)(void); - -#define UNITY_DISPLAY_RANGE_INT (0x10) -#define UNITY_DISPLAY_RANGE_UINT (0x20) -#define UNITY_DISPLAY_RANGE_HEX (0x40) -#define UNITY_DISPLAY_RANGE_CHAR (0x80) - -typedef enum -{ - UNITY_DISPLAY_STYLE_INT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_INT, - UNITY_DISPLAY_STYLE_INT8 = 1 + UNITY_DISPLAY_RANGE_INT, - UNITY_DISPLAY_STYLE_INT16 = 2 + UNITY_DISPLAY_RANGE_INT, - UNITY_DISPLAY_STYLE_INT32 = 4 + UNITY_DISPLAY_RANGE_INT, -#ifdef UNITY_SUPPORT_64 - UNITY_DISPLAY_STYLE_INT64 = 8 + UNITY_DISPLAY_RANGE_INT, -#endif - - UNITY_DISPLAY_STYLE_UINT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_UINT, - UNITY_DISPLAY_STYLE_UINT8 = 1 + UNITY_DISPLAY_RANGE_UINT, - UNITY_DISPLAY_STYLE_UINT16 = 2 + UNITY_DISPLAY_RANGE_UINT, - UNITY_DISPLAY_STYLE_UINT32 = 4 + UNITY_DISPLAY_RANGE_UINT, -#ifdef UNITY_SUPPORT_64 - UNITY_DISPLAY_STYLE_UINT64 = 8 + UNITY_DISPLAY_RANGE_UINT, -#endif - - UNITY_DISPLAY_STYLE_HEX8 = 1 + UNITY_DISPLAY_RANGE_HEX, - UNITY_DISPLAY_STYLE_HEX16 = 2 + UNITY_DISPLAY_RANGE_HEX, - UNITY_DISPLAY_STYLE_HEX32 = 4 + UNITY_DISPLAY_RANGE_HEX, -#ifdef UNITY_SUPPORT_64 - UNITY_DISPLAY_STYLE_HEX64 = 8 + UNITY_DISPLAY_RANGE_HEX, -#endif - - UNITY_DISPLAY_STYLE_CHAR = 1 + UNITY_DISPLAY_RANGE_CHAR + UNITY_DISPLAY_RANGE_INT, - - UNITY_DISPLAY_STYLE_UNKNOWN -} UNITY_DISPLAY_STYLE_T; - -typedef enum -{ - UNITY_WITHIN = 0x0, - UNITY_EQUAL_TO = 0x1, - UNITY_GREATER_THAN = 0x2, - UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO, - UNITY_SMALLER_THAN = 0x4, - UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO, - UNITY_NOT_EQUAL = 0x0, - UNITY_UNKNOWN -} UNITY_COMPARISON_T; - -#ifndef UNITY_EXCLUDE_FLOAT -typedef enum UNITY_FLOAT_TRAIT -{ - UNITY_FLOAT_IS_NOT_INF = 0, - UNITY_FLOAT_IS_INF, - UNITY_FLOAT_IS_NOT_NEG_INF, - UNITY_FLOAT_IS_NEG_INF, - UNITY_FLOAT_IS_NOT_NAN, - UNITY_FLOAT_IS_NAN, - UNITY_FLOAT_IS_NOT_DET, - UNITY_FLOAT_IS_DET, - UNITY_FLOAT_INVALID_TRAIT -} UNITY_FLOAT_TRAIT_T; -#endif - -typedef enum -{ - UNITY_ARRAY_TO_VAL = 0, - UNITY_ARRAY_TO_ARRAY, - UNITY_ARRAY_UNKNOWN -} UNITY_FLAGS_T; - -struct UNITY_STORAGE_T -{ - const char* TestFile; - const char* CurrentTestName; -#ifndef UNITY_EXCLUDE_DETAILS - const char* CurrentDetail1; - const char* CurrentDetail2; -#endif - UNITY_LINE_TYPE CurrentTestLineNumber; - UNITY_COUNTER_TYPE NumberOfTests; - UNITY_COUNTER_TYPE TestFailures; - UNITY_COUNTER_TYPE TestIgnores; - UNITY_COUNTER_TYPE CurrentTestFailed; - UNITY_COUNTER_TYPE CurrentTestIgnored; -#ifdef UNITY_INCLUDE_EXEC_TIME - UNITY_TIME_TYPE CurrentTestStartTime; - UNITY_TIME_TYPE CurrentTestStopTime; -#endif -#ifndef UNITY_EXCLUDE_SETJMP_H - jmp_buf AbortFrame; -#endif -}; - -extern struct UNITY_STORAGE_T Unity; - -/*------------------------------------------------------- - * Test Suite Management - *-------------------------------------------------------*/ - -void UnityBegin(const char* filename); -int UnityEnd(void); -void UnitySetTestFile(const char* filename); -void UnityConcludeTest(void); - -#ifndef RUN_TEST -void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum); -#else -#define UNITY_SKIP_DEFAULT_RUNNER -#endif - -/*------------------------------------------------------- - * Details Support - *-------------------------------------------------------*/ - -#ifdef UNITY_EXCLUDE_DETAILS -#define UNITY_CLR_DETAILS() -#define UNITY_SET_DETAIL(d1) -#define UNITY_SET_DETAILS(d1,d2) -#else -#define UNITY_CLR_DETAILS() do { Unity.CurrentDetail1 = 0; Unity.CurrentDetail2 = 0; } while (0) -#define UNITY_SET_DETAIL(d1) do { Unity.CurrentDetail1 = (d1); Unity.CurrentDetail2 = 0; } while (0) -#define UNITY_SET_DETAILS(d1,d2) do { Unity.CurrentDetail1 = (d1); Unity.CurrentDetail2 = (d2); } while (0) - -#ifndef UNITY_DETAIL1_NAME -#define UNITY_DETAIL1_NAME "Function" -#endif - -#ifndef UNITY_DETAIL2_NAME -#define UNITY_DETAIL2_NAME "Argument" -#endif -#endif - -#ifdef UNITY_PRINT_TEST_CONTEXT -void UNITY_PRINT_TEST_CONTEXT(void); -#endif - -/*------------------------------------------------------- - * Test Output - *-------------------------------------------------------*/ - -void UnityPrint(const char* string); - -#ifdef UNITY_INCLUDE_PRINT_FORMATTED -void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...); -#endif - -void UnityPrintLen(const char* string, const UNITY_UINT32 length); -void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number); -void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style); -void UnityPrintNumber(const UNITY_INT number_to_print); -void UnityPrintNumberUnsigned(const UNITY_UINT number); -void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print); - -#ifndef UNITY_EXCLUDE_FLOAT_PRINT -void UnityPrintFloat(const UNITY_DOUBLE input_number); -#endif - -/*------------------------------------------------------- - * Test Assertion Functions - *------------------------------------------------------- - * Use the macros below this section instead of calling - * these directly. The macros have a consistent naming - * convention and will pull in file and line information - * for you. */ - -void UnityAssertEqualNumber(const UNITY_INT expected, - const UNITY_INT actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_DISPLAY_STYLE_T style); - -void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, - const UNITY_INT actual, - const UNITY_COMPARISON_T compare, - const char *msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_DISPLAY_STYLE_T style); - -void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, - UNITY_INTERNAL_PTR actual, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_DISPLAY_STYLE_T style, - const UNITY_FLAGS_T flags); - -void UnityAssertBits(const UNITY_INT mask, - const UNITY_INT expected, - const UNITY_INT actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber); - -void UnityAssertEqualString(const char* expected, - const char* actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber); - -void UnityAssertEqualStringLen(const char* expected, - const char* actual, - const UNITY_UINT32 length, - const char* msg, - const UNITY_LINE_TYPE lineNumber); - -void UnityAssertEqualStringArray( UNITY_INTERNAL_PTR expected, - const char** actual, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLAGS_T flags); - -void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected, - UNITY_INTERNAL_PTR actual, - const UNITY_UINT32 length, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLAGS_T flags); - -void UnityAssertNumbersWithin(const UNITY_UINT delta, - const UNITY_INT expected, - const UNITY_INT actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_DISPLAY_STYLE_T style); - -void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, - UNITY_INTERNAL_PTR expected, - UNITY_INTERNAL_PTR actual, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_DISPLAY_STYLE_T style, - const UNITY_FLAGS_T flags); - -#ifndef UNITY_EXCLUDE_SETJMP_H -UNITY_NORETURN void UnityFail(const char* message, const UNITY_LINE_TYPE line); -UNITY_NORETURN void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); -#else -void UnityFail(const char* message, const UNITY_LINE_TYPE line); -void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); -#endif - -void UnityMessage(const char* message, const UNITY_LINE_TYPE line); - -#ifndef UNITY_EXCLUDE_FLOAT -void UnityAssertFloatsWithin(const UNITY_FLOAT delta, - const UNITY_FLOAT expected, - const UNITY_FLOAT actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber); - -void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, - UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLAGS_T flags); - -void UnityAssertFloatSpecial(const UNITY_FLOAT actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLOAT_TRAIT_T style); -#endif - -#ifndef UNITY_EXCLUDE_DOUBLE -void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, - const UNITY_DOUBLE expected, - const UNITY_DOUBLE actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber); - -void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, - UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, - const UNITY_UINT32 num_elements, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLAGS_T flags); - -void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, - const char* msg, - const UNITY_LINE_TYPE lineNumber, - const UNITY_FLOAT_TRAIT_T style); -#endif - -/*------------------------------------------------------- - * Helpers - *-------------------------------------------------------*/ - -UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size); -#ifndef UNITY_EXCLUDE_FLOAT -UNITY_INTERNAL_PTR UnityFloatToPtr(const float num); -#endif -#ifndef UNITY_EXCLUDE_DOUBLE -UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num); -#endif - -/*------------------------------------------------------- - * Error Strings We Might Need - *-------------------------------------------------------*/ - -extern const char UnityStrOk[]; -extern const char UnityStrPass[]; -extern const char UnityStrFail[]; -extern const char UnityStrIgnore[]; - -extern const char UnityStrErrFloat[]; -extern const char UnityStrErrDouble[]; -extern const char UnityStrErr64[]; -extern const char UnityStrErrShorthand[]; - -/*------------------------------------------------------- - * Test Running Macros - *-------------------------------------------------------*/ - -#ifndef UNITY_EXCLUDE_SETJMP_H -#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0) -#define TEST_ABORT() longjmp(Unity.AbortFrame, 1) -#else -#define TEST_PROTECT() 1 -#define TEST_ABORT() return -#endif - -/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */ -#ifndef RUN_TEST -#ifdef __STDC_VERSION__ -#if __STDC_VERSION__ >= 199901L -#define UNITY_SUPPORT_VARIADIC_MACROS -#endif -#endif -#ifdef UNITY_SUPPORT_VARIADIC_MACROS -#define RUN_TEST(...) RUN_TEST_AT_LINE(__VA_ARGS__, __LINE__, throwaway) -#define RUN_TEST_AT_LINE(func, line, ...) UnityDefaultTestRun(func, #func, line) -#endif -#endif - -/* If we can't do the tricky version, we'll just have to require them to always include the line number */ -#ifndef RUN_TEST -#ifdef CMOCK -#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num) -#else -#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__) -#endif -#endif - -#define TEST_LINE_NUM (Unity.CurrentTestLineNumber) -#define TEST_IS_IGNORED (Unity.CurrentTestIgnored) -#define UNITY_NEW_TEST(a) \ - Unity.CurrentTestName = (a); \ - Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \ - Unity.NumberOfTests++; - -#ifndef UNITY_BEGIN -#define UNITY_BEGIN() UnityBegin(__FILE__) -#endif - -#ifndef UNITY_END -#define UNITY_END() UnityEnd() -#endif - -#ifndef UNITY_SHORTHAND_AS_INT -#ifndef UNITY_SHORTHAND_AS_MEM -#ifndef UNITY_SHORTHAND_AS_NONE -#ifndef UNITY_SHORTHAND_AS_RAW -#define UNITY_SHORTHAND_AS_OLD -#endif -#endif -#endif -#endif - -/*----------------------------------------------- - * Command Line Argument Support - *-----------------------------------------------*/ - -#ifdef UNITY_USE_COMMAND_LINE_ARGS -int UnityParseOptions(int argc, char** argv); -int UnityTestMatches(void); -#endif - -/*------------------------------------------------------- - * Basic Fail and Ignore - *-------------------------------------------------------*/ - -#define UNITY_TEST_FAIL(line, message) UnityFail( (message), (UNITY_LINE_TYPE)(line)) -#define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line)) - -/*------------------------------------------------------- - * Test Asserts - *-------------------------------------------------------*/ - -#define UNITY_TEST_ASSERT(condition, line, message) do { if (condition) { /* nothing*/ } else { UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message)); } } while (0) -#define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message)) -#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message)) -#define UNITY_TEST_ASSERT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) == 0), (UNITY_LINE_TYPE)(line), (message)) -#define UNITY_TEST_ASSERT_NOT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) != 0), (UNITY_LINE_TYPE)(line), (message)) - -#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) -#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) -#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) -#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) -#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) -#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) -#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) -#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) -#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) -#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) -#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) -#define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) -#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) - -#define UNITY_TEST_ASSERT_NOT_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) -#define UNITY_TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) -#define UNITY_TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) -#define UNITY_TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) -#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) -#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) -#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) -#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) -#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) -#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) -#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) -#define UNITY_TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) - -#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) -#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) -#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) -#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) -#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) -#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) -#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) -#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) -#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) -#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) -#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) -#define UNITY_TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) - -#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) -#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) -#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) -#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) -#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) -#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) -#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) -#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) -#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) -#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) -#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) -#define UNITY_TEST_ASSERT_SMALLER_THAN_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) - -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16) (threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32) (threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) - -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) - -#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin( (delta), (UNITY_INT) (expected), (UNITY_INT) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) -#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 ) (expected), (UNITY_INT)(UNITY_INT8 ) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) -#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16) (expected), (UNITY_INT)(UNITY_INT16) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) -#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_INT32) (expected), (UNITY_INT)(UNITY_INT32) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) -#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin( (delta), (UNITY_INT) (expected), (UNITY_INT) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) -#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) -#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) -#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) -#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) -#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) -#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) -#define UNITY_TEST_ASSERT_CHAR_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 ) (expected), (UNITY_INT)(UNITY_INT8 ) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) - -#define UNITY_TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY) - - -#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER) -#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message) UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line)) -#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message) UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line)) -#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) - -#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY) - -#define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) (expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) (expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT) (expected), (UNITY_POINTER_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_VAL) - -#ifdef UNITY_SUPPORT_64 -#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) -#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) -#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) -#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) -#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) -#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) -#define UNITY_TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) -#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) -#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) -#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) -#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) -#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) -#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) -#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) -#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) -#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) -#else -#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) -#endif - -#ifdef UNITY_EXCLUDE_FLOAT -#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) -#else -#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line)) -#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message)) -#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray((UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray(UnityFloatToPtr(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) -#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) -#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) -#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) -#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) -#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) -#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) -#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) -#endif - -#ifdef UNITY_EXCLUDE_DOUBLE -#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) -#else -#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line)) -#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message)) -#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray((UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray(UnityDoubleToPtr(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) -#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) -#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) -#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) -#endif - -/* End of UNITY_INTERNALS_H */ -#endif diff --git a/taskchampion/integration-tests/src/bindings_tests/uuid.c b/taskchampion/integration-tests/src/bindings_tests/uuid.c deleted file mode 100644 index 83b02482d..000000000 --- a/taskchampion/integration-tests/src/bindings_tests/uuid.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include "unity.h" -#include "taskchampion.h" - -// creating UUIDs does not crash -static void test_uuid_creation(void) { - tc_uuid_new_v4(); - tc_uuid_nil(); -} - -// converting UUIDs to a buf works -static void test_uuid_to_buf(void) { - TEST_ASSERT_EQUAL(TC_UUID_STRING_BYTES, 36); - - TCUuid u2 = tc_uuid_nil(); - - char u2str[TC_UUID_STRING_BYTES]; - tc_uuid_to_buf(u2, u2str); - TEST_ASSERT_EQUAL_MEMORY("00000000-0000-0000-0000-000000000000", u2str, TC_UUID_STRING_BYTES); -} - -// converting UUIDs to a buf works -static void test_uuid_to_str(void) { - TCUuid u = tc_uuid_nil(); - TCString s = tc_uuid_to_str(u); - TEST_ASSERT_EQUAL_STRING( - "00000000-0000-0000-0000-000000000000", - tc_string_content(&s)); - tc_string_free(&s); -} - -// converting valid UUIDs from string works -static void test_uuid_valid_from_str(void) { - TCUuid u; - char *ustr = "23cb25e0-5d1a-4932-8131-594ac6d3a843"; - TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_uuid_from_str(tc_string_borrow(ustr), &u)); - TEST_ASSERT_EQUAL(0x23, u.bytes[0]); - TEST_ASSERT_EQUAL(0x43, u.bytes[15]); -} - -// converting invalid UUIDs from string fails as expected -static void test_uuid_invalid_string_fails(void) { - TCUuid u; - char *ustr = "not-a-valid-uuid"; - TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_uuid_from_str(tc_string_borrow(ustr), &u)); -} - -// converting invalid UTF-8 UUIDs from string fails as expected -static void test_uuid_bad_utf8(void) { - TCUuid u; - char *ustr = "\xf0\x28\x8c\xbc"; - TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_uuid_from_str(tc_string_borrow(ustr), &u)); -} - -// converting a string with embedded NUL fails as expected -static void test_uuid_embedded_nul(void) { - TCUuid u; - TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_uuid_from_str(tc_string_clone_with_len("ab\0de", 5), &u)); -} - -int uuid_tests(void) { - UNITY_BEGIN(); - // each test case above should be named here, in order. - RUN_TEST(test_uuid_creation); - RUN_TEST(test_uuid_valid_from_str); - RUN_TEST(test_uuid_to_buf); - RUN_TEST(test_uuid_to_str); - RUN_TEST(test_uuid_invalid_string_fails); - RUN_TEST(test_uuid_bad_utf8); - RUN_TEST(test_uuid_embedded_nul); - return UNITY_END(); -} diff --git a/taskchampion/integration-tests/src/lib.rs b/taskchampion/integration-tests/src/lib.rs deleted file mode 100644 index 3d76f3008..000000000 --- a/taskchampion/integration-tests/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod bindings_tests; -pub use taskchampion_lib::*; diff --git a/taskchampion/integration-tests/tests/bindings.rs b/taskchampion/integration-tests/tests/bindings.rs deleted file mode 100644 index a121dd721..000000000 --- a/taskchampion/integration-tests/tests/bindings.rs +++ /dev/null @@ -1,31 +0,0 @@ -use lazy_static::lazy_static; -use std::sync::Mutex; -use tempfile::TempDir; - -lazy_static! { - // the C library running the tests is not reentrant, so we use a mutex to ensure that only one - // test runs at a time. - static ref MUTEX: Mutex<()> = Mutex::new(()); -} - -macro_rules! suite( - { $s:ident } => { - #[test] - fn $s() { - let tmp_dir = TempDir::new().expect("TempDir failed"); - let (res, output) = { - let _guard = MUTEX.lock().unwrap(); - // run the tests in the temp dir (NOTE: this must be inside - // the mutex guard!) - std::env::set_current_dir(tmp_dir.as_ref()).unwrap(); - integration_tests::bindings_tests::$s() - }; - println!("{}", output); - if res != 0 { - assert!(false, "test failed"); - } - } - }; -); - -include!(concat!(env!("OUT_DIR"), "/bindings_test_suites.rs")); diff --git a/taskchampion/integration-tests/tests/cross-sync.rs b/taskchampion/integration-tests/tests/cross-sync.rs deleted file mode 100644 index 6cac3c293..000000000 --- a/taskchampion/integration-tests/tests/cross-sync.rs +++ /dev/null @@ -1,66 +0,0 @@ -use pretty_assertions::assert_eq; -use taskchampion::{Replica, ServerConfig, Status, StorageConfig}; -use tempfile::TempDir; - -#[test] -fn cross_sync() -> anyhow::Result<()> { - // set up two replicas, and demonstrate replication between them - let mut rep1 = Replica::new(StorageConfig::InMemory.into_storage()?); - let mut rep2 = Replica::new(StorageConfig::InMemory.into_storage()?); - - let tmp_dir = TempDir::new().expect("TempDir failed"); - let server_config = ServerConfig::Local { - server_dir: tmp_dir.path().to_path_buf(), - }; - let mut server = server_config.into_server()?; - - // add some tasks on rep1 - let t1 = rep1.new_task(Status::Pending, "test 1".into())?; - let t2 = rep1.new_task(Status::Pending, "test 2".into())?; - - // modify t1 - let mut t1 = t1.into_mut(&mut rep1); - t1.start()?; - let t1 = t1.into_immut(); - - rep1.sync(&mut server, false)?; - rep2.sync(&mut server, false)?; - - // those tasks should exist on rep2 now - let t12 = rep2 - .get_task(t1.get_uuid())? - .expect("expected task 1 on rep2"); - let t22 = rep2 - .get_task(t2.get_uuid())? - .expect("expected task 2 on rep2"); - - assert_eq!(t12.get_description(), "test 1"); - assert_eq!(t12.is_active(), true); - assert_eq!(t22.get_description(), "test 2"); - assert_eq!(t22.is_active(), false); - - // make non-conflicting changes on the two replicas - let mut t2 = t2.into_mut(&mut rep1); - t2.set_status(Status::Completed)?; - let t2 = t2.into_immut(); - - let mut t12 = t12.into_mut(&mut rep2); - t12.set_status(Status::Completed)?; - - // sync those changes back and forth - rep1.sync(&mut server, false)?; // rep1 -> server - rep2.sync(&mut server, false)?; // server -> rep2, rep2 -> server - rep1.sync(&mut server, false)?; // server -> rep1 - - let t1 = rep1 - .get_task(t1.get_uuid())? - .expect("expected task 1 on rep1"); - assert_eq!(t1.get_status(), Status::Completed); - - let t22 = rep2 - .get_task(t2.get_uuid())? - .expect("expected task 2 on rep2"); - assert_eq!(t22.get_status(), Status::Completed); - - Ok(()) -} diff --git a/taskchampion/integration-tests/tests/update-and-delete-sync.rs b/taskchampion/integration-tests/tests/update-and-delete-sync.rs deleted file mode 100644 index 91727c7e1..000000000 --- a/taskchampion/integration-tests/tests/update-and-delete-sync.rs +++ /dev/null @@ -1,72 +0,0 @@ -use taskchampion::chrono::{TimeZone, Utc}; -use taskchampion::{Replica, ServerConfig, Status, StorageConfig}; -use tempfile::TempDir; - -#[test] -fn update_and_delete_sync_delete_first() -> anyhow::Result<()> { - update_and_delete_sync(true) -} - -#[test] -fn update_and_delete_sync_update_first() -> anyhow::Result<()> { - update_and_delete_sync(false) -} - -/// Test what happens when an update is sync'd into a repo after a task is deleted. -/// If delete_first, then the deletion is sync'd to the server first; otherwise -/// the update is sync'd first. Either way, the task is gone. -fn update_and_delete_sync(delete_first: bool) -> anyhow::Result<()> { - // set up two replicas, and demonstrate replication between them - let mut rep1 = Replica::new(StorageConfig::InMemory.into_storage()?); - let mut rep2 = Replica::new(StorageConfig::InMemory.into_storage()?); - - let tmp_dir = TempDir::new().expect("TempDir failed"); - let mut server = ServerConfig::Local { - server_dir: tmp_dir.path().to_path_buf(), - } - .into_server()?; - - // add a task on rep1, and sync it to rep2 - let t = rep1.new_task(Status::Pending, "test task".into())?; - let u = t.get_uuid(); - - rep1.sync(&mut server, false)?; - rep2.sync(&mut server, false)?; - - // mark the task as deleted, long in the past, on rep2 - { - let mut t = rep2.get_task(u)?.unwrap().into_mut(&mut rep2); - t.delete()?; - t.set_modified(Utc.ymd(1980, 1, 1).and_hms(0, 0, 0))?; - } - - // sync it back to rep1 - rep2.sync(&mut server, false)?; - rep1.sync(&mut server, false)?; - - // expire the task on rep1 and check that it is gone locally - rep1.expire_tasks()?; - assert!(rep1.get_task(u)?.is_none()); - - // modify the task on rep2 - { - let mut t = rep2.get_task(u)?.unwrap().into_mut(&mut rep2); - t.set_description("modified".to_string())?; - } - - // sync back and forth - if delete_first { - rep1.sync(&mut server, false)?; - } - rep2.sync(&mut server, false)?; - rep1.sync(&mut server, false)?; - if !delete_first { - rep2.sync(&mut server, false)?; - } - - // check that the task is gone on both replicas - assert!(rep1.get_task(u)?.is_none()); - assert!(rep2.get_task(u)?.is_none()); - - Ok(()) -} diff --git a/taskchampion/scripts/changelog.py b/taskchampion/scripts/changelog.py deleted file mode 100755 index 0eac4fa35..000000000 --- a/taskchampion/scripts/changelog.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -import os -import argparse -import datetime -import subprocess -from typing import List - -def ymd(): - return datetime.datetime.now().strftime("%Y-%m-%d") - -def git_current_branch() -> str : - out = subprocess.check_output(["git", "branch", "--show-current"]) - return out.strip().decode("utf-8") - -def get_dir() -> str: - here = os.path.dirname(os.path.abspath(__file__)) - return os.path.join( - here, - "../.changelogs") - -def get_changefiles() -> List[str]: - changedir = get_dir() - changefiles = [] - for f in os.listdir(changedir): - if f.endswith(".md") and not f.startswith("."): - changefiles.append(os.path.join(changedir, f)) - - return changefiles - -def cmd_add(args): - text = args.text.strip() - if not text.startswith("- "): - text = "- %s" % text - - timestamp = ymd() - branchname = git_current_branch() - fname = os.path.join(get_dir(), "%s-%s.md" % (timestamp, branchname)) - with open(fname, "a") as f: - f.write(text) - f.write("\n") - -def cmd_build(args): - print("## x.y.z - %s" % (ymd())) - for e in get_changefiles(): - print(open(e).read().strip()) - -def main() -> None: - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(title='Sub commands', dest='command') - subparsers.required = True - - parser_add = subparsers.add_parser('add') - parser_add.add_argument("text") - parser_add.set_defaults(func=cmd_add) - - parser_build = subparsers.add_parser('build') - parser_build.set_defaults(func=cmd_build) - - args = parser.parse_args() - args.func(args) - - -if __name__ == "__main__": - main() diff --git a/taskchampion/taskchampion/Cargo.toml b/taskchampion/taskchampion/Cargo.toml deleted file mode 100644 index 7ff582dd7..000000000 --- a/taskchampion/taskchampion/Cargo.toml +++ /dev/null @@ -1,58 +0,0 @@ -[package] -name = "taskchampion" -version = "0.4.1" -authors = ["Dustin J. Mitchell "] -description = "Personal task-tracking" -homepage = "https://gothenburgbitfactory.github.io/taskwarrior/taskchampion/" -documentation = "https://docs.rs/crate/taskchampion" -repository = "https://github.com/GothenburgBitFactory/taskwarrior" -readme = "../README.md" -license = "MIT" -edition = "2021" -rust-version = "1.70.0" - -[features] -default = ["server-sync", "server-gcp"] - -# Support for sync to a server -server-sync = ["encryption", "dep:ureq", "dep:url"] -# Support for sync to GCP -server-gcp = ["cloud", "encryption", "dep:google-cloud-storage", "dep:tokio"] -# (private) Support for sync protocol encryption -encryption = ["dep:ring"] -# (private) Generic support for cloud sync -cloud = [] - -[package.metadata.docs.rs] -all-features = true - -[dependencies] -uuid.workspace = true -serde.workspace = true -serde_json.workspace = true -chrono.workspace = true -anyhow.workspace = true -thiserror.workspace = true -ureq.workspace = true -log.workspace = true -rusqlite.workspace = true -strum.workspace = true -strum_macros.workspace = true -flate2.workspace = true -byteorder.workspace = true -ring.workspace = true -google-cloud-storage.workspace = true -tokio.workspace = true -url.workspace = true - -google-cloud-storage.optional = true -tokio.optional = true -ureq.optional = true -url.optional = true -ring.optional = true - -[dev-dependencies] -proptest.workspace = true -tempfile.workspace = true -rstest.workspace = true -pretty_assertions.workspace = true diff --git a/taskchampion/taskchampion/src/depmap.rs b/taskchampion/taskchampion/src/depmap.rs deleted file mode 100644 index fe4d5df6f..000000000 --- a/taskchampion/taskchampion/src/depmap.rs +++ /dev/null @@ -1,81 +0,0 @@ -use uuid::Uuid; - -/// DependencyMap stores information on task dependencies between pending tasks. -/// -/// This information requires a scan of the working set to generate, so it is -/// typically calculated once and re-used. -#[derive(Debug, PartialEq, Eq)] -pub struct DependencyMap { - /// Edges of the dependency graph. If (a, b) is in this array, then task a depends on tsak b. - edges: Vec<(Uuid, Uuid)>, -} - -impl DependencyMap { - /// Create a new, empty DependencyMap. - pub(super) fn new() -> Self { - Self { edges: Vec::new() } - } - - /// Add a dependency of a on b. - pub(super) fn add_dependency(&mut self, a: Uuid, b: Uuid) { - self.edges.push((a, b)); - } - - /// Return an iterator of Uuids on which task `deps_of` depends. This is equivalent to - /// `task.get_dependencies()`. - pub fn dependencies(&self, dep_of: Uuid) -> impl Iterator + '_ { - self.edges - .iter() - .filter_map(move |(a, b)| if a == &dep_of { Some(*b) } else { None }) - } - - /// Return an iterator of Uuids of tasks that depend on `dep_on` - /// `task.get_dependencies()`. - pub fn dependents(&self, dep_on: Uuid) -> impl Iterator + '_ { - self.edges - .iter() - .filter_map(move |(a, b)| if b == &dep_on { Some(*a) } else { None }) - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - use std::collections::HashSet; - - #[test] - fn dependencies() { - let t = Uuid::new_v4(); - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - let mut dm = DependencyMap::new(); - - dm.add_dependency(t, uuid1); - dm.add_dependency(t, uuid2); - dm.add_dependency(Uuid::new_v4(), t); - dm.add_dependency(Uuid::new_v4(), uuid1); - dm.add_dependency(uuid2, Uuid::new_v4()); - - assert_eq!( - dm.dependencies(t).collect::>(), - set![uuid1, uuid2] - ); - } - - #[test] - fn dependents() { - let t = Uuid::new_v4(); - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - let mut dm = DependencyMap::new(); - - dm.add_dependency(uuid1, t); - dm.add_dependency(uuid2, t); - dm.add_dependency(t, Uuid::new_v4()); - dm.add_dependency(Uuid::new_v4(), uuid1); - dm.add_dependency(uuid2, Uuid::new_v4()); - - assert_eq!(dm.dependents(t).collect::>(), set![uuid1, uuid2]); - } -} diff --git a/taskchampion/taskchampion/src/errors.rs b/taskchampion/taskchampion/src/errors.rs deleted file mode 100644 index 031c6ce93..000000000 --- a/taskchampion/taskchampion/src/errors.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::io; -use thiserror::Error; - -#[derive(Debug, Error)] -#[non_exhaustive] -/// Errors returned from taskchampion operations -pub enum Error { - /// A server-related error - #[error("Server Error: {0}")] - Server(String), - /// A task-database-related error - #[error("Task Database Error: {0}")] - Database(String), - /// An error specifically indicating that the local replica cannot - /// be synchronized with the sever, due to being out of date or some - /// other irrecoverable error. - #[error("Local replica is out of sync with the server")] - OutOfSync, - /// A usage error - #[error("Usage Error: {0}")] - Usage(String), - /// A general error. - #[error(transparent)] - Other(#[from] anyhow::Error), -} - -/// Convert private and third party errors into Error::Other. -macro_rules! other_error { - ( $error:ty ) => { - impl From<$error> for Error { - fn from(err: $error) -> Self { - Self::Other(err.into()) - } - } - }; -} -#[cfg(feature = "server-sync")] -other_error!(ureq::Error); -other_error!(io::Error); -other_error!(serde_json::Error); -other_error!(rusqlite::Error); -other_error!(crate::storage::sqlite::SqliteError); -#[cfg(feature = "server-gcp")] -other_error!(google_cloud_storage::http::Error); -#[cfg(feature = "server-gcp")] -other_error!(google_cloud_storage::client::google_cloud_auth::error::Error); - -pub type Result = std::result::Result; diff --git a/taskchampion/taskchampion/src/lib.rs b/taskchampion/taskchampion/src/lib.rs deleted file mode 100644 index c9408586f..000000000 --- a/taskchampion/taskchampion/src/lib.rs +++ /dev/null @@ -1,82 +0,0 @@ -#![deny(clippy::all)] -/*! - -This crate implements the core of TaskChampion, the [replica](crate::Replica). - -Users of this crate can manipulate a task database using this API, including synchronizing that task database with others via a synchronization server. - -Example uses of this crate: - * user interfaces for task management, such as mobile apps, web apps, or command-line interfaces - * integrations for task management, such as synchronization with ticket-tracking systems or - request forms. - -# Replica - -A TaskChampion replica is a local copy of a user's task data. As the name suggests, several -replicas of the same data can exist (such as on a user's laptop and on their phone) and can -synchronize with one another. - -Replicas are accessed using the [`Replica`](crate::Replica) type. - -# Task Storage - -Replicas access the task database via a [storage object](crate::storage::Storage). -Create a storage object with [`StorageConfig`](crate::storage::StorageConfig). - -The [`storage`](crate::storage) module supports pluggable storage for a replica's data. -An implementation is provided, but users of this crate can provide their own implementation as well. - -# Server - -Replica synchronization takes place against a server. -Create a server with [`ServerConfig`](crate::ServerConfig). - -The [`server`](crate::server) module defines the interface a server must meet. -Users can define their own server impelementations. - -# Feature Flags - -Support for some optional functionality is controlled by feature flags. - -Sync server client support: - - * `server-gcp` - sync to Google Cloud Platform - * `server-sync` - sync to the taskchampion-sync-server - -# See Also - -See the [TaskChampion Book](http://taskchampion.github.com/taskchampion) -for more information about the design and usage of the tool. - -# Minimum Supported Rust Version (MSRV) - -This crate supports Rust version 1.70.0 and higher. - - */ - -// NOTE: it's important that this 'mod' comes first so that the macros can be used in other modules -mod macros; - -mod depmap; -mod errors; -mod replica; -pub mod server; -pub mod storage; -mod task; -mod taskdb; -mod utils; -mod workingset; - -pub use depmap::DependencyMap; -pub use errors::Error; -pub use replica::Replica; -pub use server::{Server, ServerConfig}; -pub use storage::StorageConfig; -pub use task::{utc_timestamp, Annotation, Status, Tag, Task, TaskMut}; -pub use workingset::WorkingSet; - -/// Re-exported type from the `uuid` crate, for ease of compatibility for consumers of this crate. -pub use uuid::Uuid; - -/// Re-exported chrono module. -pub use chrono; diff --git a/taskchampion/taskchampion/src/macros.rs b/taskchampion/taskchampion/src/macros.rs deleted file mode 100644 index eb34b4640..000000000 --- a/taskchampion/taskchampion/src/macros.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![macro_use] - -/// Create a hashset, similar to vec! -// NOTE: in Rust 1.56.0, this can be changed to HashSet::from([..]) -#[cfg(test)] -macro_rules! set( - { $($key:expr),* $(,)? } => { - { - #[allow(unused_mut)] - let mut s = ::std::collections::HashSet::new(); - $( - s.insert($key); - )* - s - } - }; -); diff --git a/taskchampion/taskchampion/src/replica.rs b/taskchampion/taskchampion/src/replica.rs deleted file mode 100644 index bad6cceba..000000000 --- a/taskchampion/taskchampion/src/replica.rs +++ /dev/null @@ -1,677 +0,0 @@ -use crate::depmap::DependencyMap; -use crate::errors::Result; -use crate::server::{Server, SyncOp}; -use crate::storage::{ReplicaOp, Storage, TaskMap}; -use crate::task::{Status, Task}; -use crate::taskdb::TaskDb; -use crate::workingset::WorkingSet; -use anyhow::Context; -use chrono::{Duration, Utc}; -use log::trace; -use std::collections::HashMap; -use std::rc::Rc; -use uuid::Uuid; - -/// A replica represents an instance of a user's task data, providing an easy interface -/// for querying and modifying that data. -/// -/// ## Tasks -/// -/// Tasks are uniquely identified by UUIDs. -/// Most task modifications are performed via the [`Task`](crate::Task) and -/// [`TaskMut`](crate::TaskMut) types. Use of two types for tasks allows easy -/// read-only manipulation of lots of tasks, with exclusive access required only -/// for modifications. -/// -/// ## Working Set -/// -/// A replica maintains a "working set" of tasks that are of current concern to the user, -/// specifically pending tasks. These are indexed with small, easy-to-type integers. Newly -/// pending tasks are automatically added to the working set, and the working set is "renumbered" -/// during the garbage-collection process. -pub struct Replica { - taskdb: TaskDb, - - /// If true, this replica has already added an undo point. - added_undo_point: bool, - - /// The dependency map for this replica, if it has been calculated. - depmap: Option>, -} - -impl Replica { - pub fn new(storage: Box) -> Replica { - Replica { - taskdb: TaskDb::new(storage), - added_undo_point: false, - depmap: None, - } - } - - #[cfg(test)] - pub fn new_inmemory() -> Replica { - Replica::new(Box::new(crate::storage::InMemoryStorage::new())) - } - - /// Update an existing task. If the value is Some, the property is added or updated. If the - /// value is None, the property is deleted. It is not an error to delete a nonexistent - /// property. - /// - /// This is a low-level method, and requires knowledge of the Task data model. Prefer to - /// use the [`TaskMut`] methods to modify tasks, where possible. - pub fn update_task( - &mut self, - uuid: Uuid, - property: S1, - value: Option, - ) -> Result - where - S1: Into, - S2: Into, - { - self.add_undo_point(false)?; - self.taskdb.apply(SyncOp::Update { - uuid, - property: property.into(), - value: value.map(|v| v.into()), - timestamp: Utc::now(), - }) - } - - /// Add the given uuid to the working set, returning its index. - pub(crate) fn add_to_working_set(&mut self, uuid: Uuid) -> Result { - self.taskdb.add_to_working_set(uuid) - } - - /// Get all tasks represented as a map keyed by UUID - pub fn all_tasks(&mut self) -> Result> { - let depmap = self.dependency_map(false)?; - let mut res = HashMap::new(); - for (uuid, tm) in self.taskdb.all_tasks()?.drain(..) { - res.insert(uuid, Task::new(uuid, tm, depmap.clone())); - } - Ok(res) - } - - /// Get the UUIDs of all tasks - pub fn all_task_uuids(&mut self) -> Result> { - self.taskdb.all_task_uuids() - } - - /// Get the "working set" for this replica. This is a snapshot of the current state, - /// and it is up to the caller to decide how long to store this value. - pub fn working_set(&mut self) -> Result { - Ok(WorkingSet::new(self.taskdb.working_set()?)) - } - - /// Get the dependency map for all pending tasks. - /// - /// A task dependency is recognized when a task in the working set depends on a task with - /// status equal to Pending. - /// - /// The data in this map is cached when it is first requested and may not contain modifications - /// made locally in this Replica instance. The result is reference-counted and may - /// outlive the Replica. - /// - /// If `force` is true, then the result is re-calculated from the current state of the replica, - /// although previously-returned dependency maps are not updated. - pub fn dependency_map(&mut self, force: bool) -> Result> { - if force || self.depmap.is_none() { - // note: we can't use self.get_task here, as that depends on a - // DependencyMap - - let mut dm = DependencyMap::new(); - // temporary cache tracking whether tasks are considered Pending or not. - let mut is_pending_cache: HashMap = HashMap::new(); - let ws = self.working_set()?; - // for each task in the working set - for i in 1..=ws.largest_index() { - // get the task UUID - if let Some(u) = ws.by_index(i) { - // get the task - if let Some(taskmap) = self.taskdb.get_task(u)? { - // search the task's keys - for p in taskmap.keys() { - // for one matching `dep_..` - if let Some(dep_str) = p.strip_prefix("dep_") { - // and extract the UUID from the key - if let Ok(dep) = Uuid::parse_str(dep_str) { - // the dependency is pending if - let dep_pending = { - // we've determined this before and cached the result - if let Some(dep_pending) = is_pending_cache.get(&dep) { - *dep_pending - } else if let Some(dep_taskmap) = - // or if we get the task - self.taskdb.get_task(dep)? - { - // and its status is "pending" - let dep_pending = matches!( - dep_taskmap - .get("status") - .map(|tm| Status::from_taskmap(tm)), - Some(Status::Pending) - ); - is_pending_cache.insert(dep, dep_pending); - dep_pending - } else { - false - } - }; - if dep_pending { - dm.add_dependency(u, dep); - } - } - } - } - } - } - } - self.depmap = Some(Rc::new(dm)); - } - - // at this point self.depmap is guaranteed to be Some(_) - Ok(self.depmap.as_ref().unwrap().clone()) - } - - /// Get an existing task by its UUID - pub fn get_task(&mut self, uuid: Uuid) -> Result> { - let depmap = self.dependency_map(false)?; - Ok(self - .taskdb - .get_task(uuid)? - .map(move |tm| Task::new(uuid, tm, depmap))) - } - - /// Create a new task. - pub fn new_task(&mut self, status: Status, description: String) -> Result { - let uuid = Uuid::new_v4(); - self.add_undo_point(false)?; - let taskmap = self.taskdb.apply(SyncOp::Create { uuid })?; - let depmap = self.dependency_map(false)?; - let mut task = Task::new(uuid, taskmap, depmap).into_mut(self); - task.set_description(description)?; - task.set_status(status)?; - task.set_entry(Some(Utc::now()))?; - trace!("task {} created", uuid); - Ok(task.into_immut()) - } - - /// Create a new, empty task with the given UUID. This is useful for importing tasks, but - /// otherwise should be avoided in favor of `new_task`. If the task already exists, this - /// does nothing and returns the existing task. - pub fn import_task_with_uuid(&mut self, uuid: Uuid) -> Result { - self.add_undo_point(false)?; - let taskmap = self.taskdb.apply(SyncOp::Create { uuid })?; - let depmap = self.dependency_map(false)?; - Ok(Task::new(uuid, taskmap, depmap)) - } - - /// Delete a task. The task must exist. Note that this is different from setting status to - /// Deleted; this is the final purge of the task. This is not a public method as deletion - /// should only occur through expiration. - fn delete_task(&mut self, uuid: Uuid) -> Result<()> { - self.add_undo_point(false)?; - self.taskdb.apply(SyncOp::Delete { uuid })?; - trace!("task {} deleted", uuid); - Ok(()) - } - - /// Synchronize this replica against the given server. The working set is rebuilt after - /// this occurs, but without renumbering, so any newly-pending tasks should appear in - /// the working set. - /// - /// If `avoid_snapshots` is true, the sync operations produces a snapshot only when the server - /// indicate it is urgent (snapshot urgency "high"). This allows time for other replicas to - /// create a snapshot before this one does. - /// - /// Set this to true on systems more constrained in CPU, memory, or bandwidth than a typical desktop - /// system - pub fn sync(&mut self, server: &mut Box, avoid_snapshots: bool) -> Result<()> { - self.taskdb - .sync(server, avoid_snapshots) - .context("Failed to synchronize with server")?; - self.rebuild_working_set(false) - .context("Failed to rebuild working set after sync")?; - Ok(()) - } - - /// Return undo local operations until the most recent UndoPoint, returning an empty Vec if there are no - /// local operations to undo. - pub fn get_undo_ops(&mut self) -> Result> { - self.taskdb.get_undo_ops() - } - - /// Undo local operations in storage, returning a boolean indicating success. - pub fn commit_undo_ops(&mut self, undo_ops: Vec) -> Result { - self.taskdb.commit_undo_ops(undo_ops) - } - - /// Rebuild this replica's working set, based on whether tasks are pending or not. If - /// `renumber` is true, then existing tasks may be moved to new working-set indices; in any - /// case, on completion all pending and recurring tasks are in the working set and all tasks - /// with other statuses are not. - pub fn rebuild_working_set(&mut self, renumber: bool) -> Result<()> { - let pending = String::from(Status::Pending.to_taskmap()); - let recurring = String::from(Status::Recurring.to_taskmap()); - self.taskdb.rebuild_working_set( - |t| { - if let Some(st) = t.get("status") { - st == &pending || st == &recurring - } else { - false - } - }, - renumber, - )?; - Ok(()) - } - - /// Expire old, deleted tasks. - /// - /// Expiration entails removal of tasks from the replica. Any modifications that occur after - /// the deletion (such as operations synchronized from other replicas) will do nothing. - /// - /// Tasks are eligible for expiration when they have status Deleted and have not been modified - /// for 180 days (about six months). Note that completed tasks are not eligible. - pub fn expire_tasks(&mut self) -> Result<()> { - let six_mos_ago = Utc::now() - Duration::days(180); - self.all_tasks()? - .iter() - .filter(|(_, t)| t.get_status() == Status::Deleted) - .filter(|(_, t)| { - if let Some(m) = t.get_modified() { - m < six_mos_ago - } else { - false - } - }) - .try_for_each(|(u, _)| self.delete_task(*u))?; - Ok(()) - } - - /// Add an UndoPoint, if one has not already been added by this Replica. This occurs - /// automatically when a change is made. The `force` flag allows forcing a new UndoPoint - /// even if one has already been created by this Replica, and may be useful when a Replica - /// instance is held for a long time and used to apply more than one user-visible change. - pub fn add_undo_point(&mut self, force: bool) -> Result<()> { - if force || !self.added_undo_point { - self.taskdb.add_undo_point()?; - self.added_undo_point = true; - } - Ok(()) - } - - /// Get the number of operations local to this replica and not yet synchronized to the server. - pub fn num_local_operations(&mut self) -> Result { - self.taskdb.num_operations() - } - - /// Get the number of undo points available (number of times `undo` will succeed). - pub fn num_undo_points(&mut self) -> Result { - self.taskdb.num_undo_points() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::storage::ReplicaOp; - use crate::task::Status; - use chrono::TimeZone; - use pretty_assertions::assert_eq; - use std::collections::HashSet; - use uuid::Uuid; - - #[test] - fn new_task() { - let mut rep = Replica::new_inmemory(); - - let t = rep.new_task(Status::Pending, "a task".into()).unwrap(); - assert_eq!(t.get_description(), String::from("a task")); - assert_eq!(t.get_status(), Status::Pending); - assert!(t.get_modified().is_some()); - } - - #[test] - fn modify_task() { - let mut rep = Replica::new_inmemory(); - - let t = rep.new_task(Status::Pending, "a task".into()).unwrap(); - - let mut t = t.into_mut(&mut rep); - t.set_description(String::from("past tense")).unwrap(); - t.set_status(Status::Completed).unwrap(); - // check that values have changed on the TaskMut - assert_eq!(t.get_description(), "past tense"); - assert_eq!(t.get_status(), Status::Completed); - - // check that values have changed after into_immut - let t = t.into_immut(); - assert_eq!(t.get_description(), "past tense"); - assert_eq!(t.get_status(), Status::Completed); - - // check that values have changed in storage, too - let t = rep.get_task(t.get_uuid()).unwrap().unwrap(); - assert_eq!(t.get_description(), "past tense"); - assert_eq!(t.get_status(), Status::Completed); - - // and check for the corresponding operations, cleaning out the timestamps - // and modified properties as these are based on the current time - let now = Utc::now(); - let clean_op = |op: ReplicaOp| { - if let ReplicaOp::Update { - uuid, - property, - mut old_value, - mut value, - .. - } = op - { - // rewrite automatically-created dates to "just-now" for ease - // of testing - if property == "modified" || property == "end" || property == "entry" { - if value.is_some() { - value = Some("just-now".into()); - } - if old_value.is_some() { - old_value = Some("just-now".into()); - } - } - ReplicaOp::Update { - uuid, - property, - old_value, - value, - timestamp: now, - } - } else { - op - } - }; - assert_eq!( - rep.taskdb - .operations() - .drain(..) - .map(clean_op) - .collect::>(), - vec![ - ReplicaOp::UndoPoint, - ReplicaOp::Create { uuid: t.get_uuid() }, - ReplicaOp::Update { - uuid: t.get_uuid(), - property: "modified".into(), - old_value: None, - value: Some("just-now".into()), - timestamp: now, - }, - ReplicaOp::Update { - uuid: t.get_uuid(), - property: "description".into(), - old_value: None, - value: Some("a task".into()), - timestamp: now, - }, - ReplicaOp::Update { - uuid: t.get_uuid(), - property: "status".into(), - old_value: None, - value: Some("pending".into()), - timestamp: now, - }, - ReplicaOp::Update { - uuid: t.get_uuid(), - property: "entry".into(), - old_value: None, - value: Some("just-now".into()), - timestamp: now, - }, - ReplicaOp::Update { - uuid: t.get_uuid(), - property: "modified".into(), - old_value: Some("just-now".into()), - value: Some("just-now".into()), - timestamp: now, - }, - ReplicaOp::Update { - uuid: t.get_uuid(), - property: "description".into(), - old_value: Some("a task".into()), - value: Some("past tense".into()), - timestamp: now, - }, - ReplicaOp::Update { - uuid: t.get_uuid(), - property: "end".into(), - old_value: None, - value: Some("just-now".into()), - timestamp: now, - }, - ReplicaOp::Update { - uuid: t.get_uuid(), - property: "status".into(), - old_value: Some("pending".into()), - value: Some("completed".into()), - timestamp: now, - }, - ] - ); - - // num_local_operations includes all but the undo point - assert_eq!(rep.num_local_operations().unwrap(), 9); - - // num_undo_points includes only the undo point - assert_eq!(rep.num_undo_points().unwrap(), 1); - } - - #[test] - fn delete_task() { - let mut rep = Replica::new_inmemory(); - - let t = rep.new_task(Status::Pending, "a task".into()).unwrap(); - let uuid = t.get_uuid(); - - rep.delete_task(uuid).unwrap(); - assert_eq!(rep.get_task(uuid).unwrap(), None); - } - - #[test] - fn get_and_modify() { - let mut rep = Replica::new_inmemory(); - - let t = rep - .new_task(Status::Pending, "another task".into()) - .unwrap(); - let uuid = t.get_uuid(); - - let t = rep.get_task(uuid).unwrap().unwrap(); - assert_eq!(t.get_description(), String::from("another task")); - - let mut t = t.into_mut(&mut rep); - t.set_status(Status::Deleted).unwrap(); - t.set_description("gone".into()).unwrap(); - - let t = rep.get_task(uuid).unwrap().unwrap(); - assert_eq!(t.get_status(), Status::Deleted); - assert_eq!(t.get_description(), "gone"); - - rep.rebuild_working_set(true).unwrap(); - - let ws = rep.working_set().unwrap(); - assert!(ws.by_uuid(t.get_uuid()).is_none()); - } - - #[test] - fn rebuild_working_set_includes_recurring() { - let mut rep = Replica::new_inmemory(); - - let t = rep - .new_task(Status::Completed, "another task".into()) - .unwrap(); - let uuid = t.get_uuid(); - - let t = rep.get_task(uuid).unwrap().unwrap(); - { - let mut t = t.into_mut(&mut rep); - t.set_status(Status::Recurring).unwrap(); - } - - rep.rebuild_working_set(true).unwrap(); - - let ws = rep.working_set().unwrap(); - assert!(ws.by_uuid(uuid).is_some()); - } - - #[test] - fn new_pending_adds_to_working_set() { - let mut rep = Replica::new_inmemory(); - - let t = rep - .new_task(Status::Pending, "to-be-pending".into()) - .unwrap(); - let uuid = t.get_uuid(); - - let ws = rep.working_set().unwrap(); - assert_eq!(ws.len(), 1); // only one non-none value - assert!(ws.by_index(0).is_none()); - assert_eq!(ws.by_index(1), Some(uuid)); - - let ws = rep.working_set().unwrap(); - assert_eq!(ws.by_uuid(t.get_uuid()), Some(1)); - } - - #[test] - fn new_recurring_adds_to_working_set() { - let mut rep = Replica::new_inmemory(); - - let t = rep - .new_task(Status::Recurring, "to-be-recurring".into()) - .unwrap(); - let uuid = t.get_uuid(); - - let ws = rep.working_set().unwrap(); - assert_eq!(ws.len(), 1); // only one non-none value - assert!(ws.by_index(0).is_none()); - assert_eq!(ws.by_index(1), Some(uuid)); - - let ws = rep.working_set().unwrap(); - assert_eq!(ws.by_uuid(t.get_uuid()), Some(1)); - } - - #[test] - fn get_does_not_exist() { - let mut rep = Replica::new_inmemory(); - let uuid = Uuid::new_v4(); - assert_eq!(rep.get_task(uuid).unwrap(), None); - } - - #[test] - fn expire() { - let mut rep = Replica::new_inmemory(); - let mut t; - - rep.new_task(Status::Pending, "keeper 1".into()).unwrap(); - rep.new_task(Status::Completed, "keeper 2".into()).unwrap(); - - t = rep.new_task(Status::Deleted, "keeper 3".into()).unwrap(); - { - let mut t = t.into_mut(&mut rep); - // set entry, with modification set as a side-effect - t.set_entry(Some(Utc::now())).unwrap(); - } - - t = rep.new_task(Status::Deleted, "goner".into()).unwrap(); - { - let mut t = t.into_mut(&mut rep); - t.set_modified(Utc.ymd(1980, 1, 1).and_hms(0, 0, 0)) - .unwrap(); - } - - rep.expire_tasks().unwrap(); - - for (_, t) in rep.all_tasks().unwrap() { - println!("got task {}", t.get_description()); - assert!(t.get_description().starts_with("keeper")); - } - } - - #[test] - fn dependency_map() { - let mut rep = Replica::new_inmemory(); - - let mut tasks = vec![]; - for _ in 0..4 { - tasks.push(rep.new_task(Status::Pending, "t".into()).unwrap()); - } - - let uuids: Vec<_> = tasks.iter().map(|t| t.get_uuid()).collect(); - - // t[3] depends on t[2], and t[1] - { - let mut t = tasks.pop().unwrap().into_mut(&mut rep); - t.add_dependency(uuids[2]).unwrap(); - t.add_dependency(uuids[1]).unwrap(); - } - - // t[2] depends on t[0] - { - let mut t = tasks.pop().unwrap().into_mut(&mut rep); - t.add_dependency(uuids[0]).unwrap(); - } - - // t[1] depends on t[0] - { - let mut t = tasks.pop().unwrap().into_mut(&mut rep); - t.add_dependency(uuids[0]).unwrap(); - } - - // generate the dependency map, forcing an update based on the newly-added - // dependencies - let dm = rep.dependency_map(true).unwrap(); - - assert_eq!( - dm.dependencies(uuids[3]).collect::>(), - set![uuids[1], uuids[2]] - ); - assert_eq!( - dm.dependencies(uuids[2]).collect::>(), - set![uuids[0]] - ); - assert_eq!( - dm.dependencies(uuids[1]).collect::>(), - set![uuids[0]] - ); - assert_eq!(dm.dependencies(uuids[0]).collect::>(), set![]); - - assert_eq!(dm.dependents(uuids[3]).collect::>(), set![]); - assert_eq!( - dm.dependents(uuids[2]).collect::>(), - set![uuids[3]] - ); - assert_eq!( - dm.dependents(uuids[1]).collect::>(), - set![uuids[3]] - ); - assert_eq!( - dm.dependents(uuids[0]).collect::>(), - set![uuids[1], uuids[2]] - ); - - // mark t[0] as done, removing it from the working set - rep.get_task(uuids[0]) - .unwrap() - .unwrap() - .into_mut(&mut rep) - .done() - .unwrap(); - let dm = rep.dependency_map(true).unwrap(); - - assert_eq!( - dm.dependencies(uuids[3]).collect::>(), - set![uuids[1], uuids[2]] - ); - assert_eq!(dm.dependencies(uuids[2]).collect::>(), set![]); - assert_eq!(dm.dependencies(uuids[1]).collect::>(), set![]); - assert_eq!(dm.dependents(uuids[0]).collect::>(), set![]); - } -} diff --git a/taskchampion/taskchampion/src/server/cloud/gcp.rs b/taskchampion/taskchampion/src/server/cloud/gcp.rs deleted file mode 100644 index a53ad6e48..000000000 --- a/taskchampion/taskchampion/src/server/cloud/gcp.rs +++ /dev/null @@ -1,407 +0,0 @@ -use super::service::{ObjectInfo, Service}; -use crate::errors::Result; -use google_cloud_storage::client::google_cloud_auth::credentials::CredentialsFile; -use google_cloud_storage::client::{Client, ClientConfig}; -use google_cloud_storage::http::error::ErrorResponse; -use google_cloud_storage::http::Error as GcsError; -use google_cloud_storage::http::{self, objects}; -use tokio::runtime::Runtime; - -/// A [`Service`] implementation based on the Google Cloud Storage service. -pub(in crate::server) struct GcpService { - client: Client, - rt: Runtime, - bucket: String, -} - -/// Determine whether the given result contains an HTTP error with the given code. -fn is_http_error(query: u16, res: &std::result::Result) -> bool { - match res { - // Errors from RPC's. - Err(GcsError::Response(ErrorResponse { code, .. })) => *code == query, - // Errors from reqwest (downloads, uploads). - Err(GcsError::HttpClient(e)) => e.status().map(|s| s.as_u16()) == Some(query), - _ => false, - } -} - -impl GcpService { - pub(in crate::server) fn new(bucket: String, credential_path: Option) -> Result { - let rt = Runtime::new()?; - - let credentialpathstring = credential_path.clone().unwrap(); - let config: ClientConfig = if credential_path.unwrap() == "" { - rt.block_on(ClientConfig::default().with_auth())? - } else { - let credentials = rt.block_on(CredentialsFile::new_from_file(credentialpathstring))?; - rt.block_on(ClientConfig::default().with_credentials(credentials))? - }; - - Ok(Self { - client: Client::new(config), - rt, - bucket, - }) - } -} - -impl Service for GcpService { - fn put(&mut self, name: &[u8], value: &[u8]) -> Result<()> { - let name = String::from_utf8(name.to_vec()).expect("non-UTF8 object name"); - let upload_type = objects::upload::UploadType::Simple(objects::upload::Media::new(name)); - self.rt.block_on(self.client.upload_object( - &objects::upload::UploadObjectRequest { - bucket: self.bucket.clone(), - ..Default::default() - }, - value.to_vec(), - &upload_type, - ))?; - Ok(()) - } - - fn get(&mut self, name: &[u8]) -> Result>> { - let name = String::from_utf8(name.to_vec()).expect("non-UTF8 object name"); - let download_res = self.rt.block_on(self.client.download_object( - &objects::get::GetObjectRequest { - bucket: self.bucket.clone(), - object: name, - ..Default::default() - }, - &objects::download::Range::default(), - )); - if is_http_error(404, &download_res) { - Ok(None) - } else { - Ok(Some(download_res?)) - } - } - - fn del(&mut self, name: &[u8]) -> Result<()> { - let name = String::from_utf8(name.to_vec()).expect("non-UTF8 object name"); - let del_res = self.rt.block_on(self.client.delete_object( - &objects::delete::DeleteObjectRequest { - bucket: self.bucket.clone(), - object: name, - ..Default::default() - }, - )); - if !is_http_error(404, &del_res) { - del_res?; - } - Ok(()) - } - - fn list<'a>(&'a mut self, prefix: &[u8]) -> Box> + 'a> { - let prefix = String::from_utf8(prefix.to_vec()).expect("non-UTF8 object prefix"); - Box::new(ObjectIterator { - service: self, - prefix, - last_response: None, - next_index: 0, - }) - } - - fn compare_and_swap( - &mut self, - name: &[u8], - existing_value: Option>, - new_value: Vec, - ) -> Result { - let name = String::from_utf8(name.to_vec()).expect("non-UTF8 object name"); - let get_res = self - .rt - .block_on(self.client.get_object(&objects::get::GetObjectRequest { - bucket: self.bucket.clone(), - object: name.clone(), - ..Default::default() - })); - // Determine the object's generation. See https://cloud.google.com/storage/docs/metadata#generation-number - let generation = if is_http_error(404, &get_res) { - // If a value was expected, that expectation has not been met. - if existing_value.is_some() { - return Ok(false); - } - // Generation 0 indicates that the object does not yet exist. - 0 - } else { - get_res?.generation - }; - - // If the file existed, then verify its contents. - if generation > 0 { - let data = self.rt.block_on(self.client.download_object( - &objects::get::GetObjectRequest { - bucket: self.bucket.clone(), - object: name.clone(), - // Fetch the same generation. - generation: Some(generation), - ..Default::default() - }, - &objects::download::Range::default(), - ))?; - if Some(data) != existing_value { - return Ok(false); - } - } - - // Finally, put the new value with a condition that the generation hasn't changed. - let upload_type = objects::upload::UploadType::Simple(objects::upload::Media::new(name)); - let upload_res = self.rt.block_on(self.client.upload_object( - &objects::upload::UploadObjectRequest { - bucket: self.bucket.clone(), - if_generation_match: Some(generation), - ..Default::default() - }, - new_value.to_vec(), - &upload_type, - )); - if is_http_error(412, &upload_res) { - // A 412 indicates the precondition was not satisfied: the given generation - // is no longer the latest. - Ok(false) - } else { - upload_res?; - Ok(true) - } - } -} - -/// An Iterator returning names of objects from `list_objects`. -/// -/// This handles response pagination by fetching one page at a time. -struct ObjectIterator<'a> { - service: &'a mut GcpService, - prefix: String, - last_response: Option, - next_index: usize, -} - -impl<'a> ObjectIterator<'a> { - fn fetch_batch(&mut self) -> Result<()> { - let mut page_token = None; - if let Some(ref resp) = self.last_response { - page_token = resp.next_page_token.clone(); - } - self.last_response = Some(self.service.rt.block_on(self.service.client.list_objects( - &objects::list::ListObjectsRequest { - bucket: self.service.bucket.clone(), - prefix: Some(self.prefix.clone()), - page_token, - #[cfg(test)] // For testing, use a small page size. - max_results: Some(6), - ..Default::default() - }, - ))?); - self.next_index = 0; - Ok(()) - } -} - -impl<'a> Iterator for ObjectIterator<'a> { - type Item = Result; - fn next(&mut self) -> Option { - // If the iterator is just starting, fetch the first response. - if self.last_response.is_none() { - if let Err(e) = self.fetch_batch() { - return Some(Err(e)); - } - } - if let Some(ref result) = self.last_response { - if let Some(ref items) = result.items { - if self.next_index < items.len() { - // Return a result from the existing response. - let obj = &items[self.next_index]; - self.next_index += 1; - // It's unclear when `time_created` would be None, so default to 0 in that case - // or when the timestamp is not a valid u64 (before 1970). - let creation = obj.time_created.map(|t| t.unix_timestamp()).unwrap_or(0); - let creation: u64 = creation.try_into().unwrap_or(0); - return Some(Ok(ObjectInfo { - name: obj.name.as_bytes().to_vec(), - creation, - })); - } else if result.next_page_token.is_some() { - // Fetch the next page and try again. - if let Err(e) = self.fetch_batch() { - return Some(Err(e)); - } - return self.next(); - } - } - } - None - } -} - -#[cfg(test)] -mod tests { - use super::*; - use uuid::Uuid; - - /// Make a service if `GCP_TEST_BUCKET` is set, as well as a function to put a unique prefix on - /// an object name, so that tests do not interfere with one another. - /// - /// Set up this bucket with a lifecyle policy to delete objects with age > 1 day. While passing - /// tests should correctly clean up after themselves, failing tests may leave objects in the - /// bucket. - /// - /// When the environment variable is not set, this returns false and the test does not run. - /// Note that the Rust test runner will still show "ok" for the test, as there is no way to - /// indicate anything else. - fn make_service() -> Option<(GcpService, impl Fn(&str) -> Vec)> { - let Ok(bucket) = std::env::var("GCP_TEST_BUCKET") else { - return None; - }; - - let Ok(credential_path) = std::env::var("GCP_TEST_CREDENTIAL_PATH") else { - return None; - }; - - let prefix = Uuid::new_v4(); - Some(( - GcpService::new(bucket, Some(credential_path)).unwrap(), - move |n: &_| format!("{}-{}", prefix.as_simple(), n).into_bytes(), - )) - } - - #[test] - fn put_and_get() { - let Some((mut svc, pfx)) = make_service() else { - return; - }; - svc.put(&pfx("testy"), b"foo").unwrap(); - let got = svc.get(&pfx("testy")).unwrap(); - assert_eq!(got, Some(b"foo".to_vec())); - - // Clean up. - svc.del(&pfx("testy")).unwrap(); - } - - #[test] - fn get_missing() { - let Some((mut svc, pfx)) = make_service() else { - return; - }; - let got = svc.get(&pfx("testy")).unwrap(); - assert_eq!(got, None); - } - - #[test] - fn del() { - let Some((mut svc, pfx)) = make_service() else { - return; - }; - svc.put(&pfx("testy"), b"data").unwrap(); - svc.del(&pfx("testy")).unwrap(); - let got = svc.get(&pfx("testy")).unwrap(); - assert_eq!(got, None); - } - - #[test] - fn del_missing() { - // Deleting an object that does not exist is not an error. - let Some((mut svc, pfx)) = make_service() else { - return; - }; - - assert!(svc.del(&pfx("testy")).is_ok()); - } - - #[test] - fn list() { - let Some((mut svc, pfx)) = make_service() else { - return; - }; - let mut names: Vec<_> = (0..20).map(|i| pfx(&format!("pp-{i:02}"))).collect(); - names.sort(); - // Create 20 objects that will be listed. - for n in &names { - svc.put(n, b"data").unwrap(); - } - // And another object that should not be included in the list. - svc.put(&pfx("xxx"), b"data").unwrap(); - - let got_objects: Vec<_> = svc.list(&pfx("pp-")).collect::>().unwrap(); - let mut got_names: Vec<_> = got_objects.into_iter().map(|oi| oi.name).collect(); - got_names.sort(); - assert_eq!(got_names, names); - - // Clean up. - for n in got_names { - svc.del(&n).unwrap(); - } - svc.del(&pfx("xxx")).unwrap(); - } - - #[test] - fn compare_and_swap_create() { - let Some((mut svc, pfx)) = make_service() else { - return; - }; - - assert!(svc - .compare_and_swap(&pfx("testy"), None, b"bar".to_vec()) - .unwrap()); - let got = svc.get(&pfx("testy")).unwrap(); - assert_eq!(got, Some(b"bar".to_vec())); - - // Clean up. - svc.del(&pfx("testy")).unwrap(); - } - - #[test] - fn compare_and_swap_matches() { - let Some((mut svc, pfx)) = make_service() else { - return; - }; - - // Create the existing file, with two generations. - svc.put(&pfx("testy"), b"foo1").unwrap(); - svc.put(&pfx("testy"), b"foo2").unwrap(); - assert!(svc - .compare_and_swap(&pfx("testy"), Some(b"foo2".to_vec()), b"bar".to_vec()) - .unwrap()); - let got = svc.get(&pfx("testy")).unwrap(); - assert_eq!(got, Some(b"bar".to_vec())); - - // Clean up. - svc.del(&pfx("testy")).unwrap(); - } - - #[test] - fn compare_and_swap_expected_no_file() { - let Some((mut svc, pfx)) = make_service() else { - return; - }; - - svc.put(&pfx("testy"), b"foo1").unwrap(); - assert!(!svc - .compare_and_swap(&pfx("testy"), None, b"bar".to_vec()) - .unwrap()); - let got = svc.get(&pfx("testy")).unwrap(); - assert_eq!(got, Some(b"foo1".to_vec())); - - // Clean up. - svc.del(&pfx("testy")).unwrap(); - } - - #[test] - fn compare_and_swap_mismatch() { - let Some((mut svc, pfx)) = make_service() else { - return; - }; - - // Create the existing file, with two generations. - svc.put(&pfx("testy"), b"foo1").unwrap(); - svc.put(&pfx("testy"), b"foo2").unwrap(); - assert!(!svc - .compare_and_swap(&pfx("testy"), Some(b"foo1".to_vec()), b"bar".to_vec()) - .unwrap()); - let got = svc.get(&pfx("testy")).unwrap(); - assert_eq!(got, Some(b"foo2".to_vec())); - - // Clean up. - svc.del(&pfx("testy")).unwrap(); - } -} diff --git a/taskchampion/taskchampion/src/server/cloud/mod.rs b/taskchampion/taskchampion/src/server/cloud/mod.rs deleted file mode 100644 index 970ced75c..000000000 --- a/taskchampion/taskchampion/src/server/cloud/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -/*! -* Support for cloud-service-backed sync. -* -* All of these operate using a similar approach, with specific patterns of object names. The -* process of adding a new version requires a compare-and-swap operation that sets a new version -* as the "latest" only if the existing "latest" has the expected value. This ensures a continuous -* chain of versions, even if multiple replicas attempt to sync at the same time. -*/ - -mod server; -mod service; - -pub(in crate::server) use server::CloudServer; - -#[cfg(feature = "server-gcp")] -pub(in crate::server) mod gcp; diff --git a/taskchampion/taskchampion/src/server/cloud/server.rs b/taskchampion/taskchampion/src/server/cloud/server.rs deleted file mode 100644 index 6aaee585b..000000000 --- a/taskchampion/taskchampion/src/server/cloud/server.rs +++ /dev/null @@ -1,1183 +0,0 @@ -use super::service::{ObjectInfo, Service}; -use crate::errors::{Error, Result}; -use crate::server::encryption::{Cryptor, Sealed, Unsealed}; -use crate::server::{ - AddVersionResult, GetVersionResult, HistorySegment, Server, Snapshot, SnapshotUrgency, - VersionId, -}; -use ring::rand; -use std::collections::{HashMap, HashSet}; -#[cfg(not(test))] -use std::time::{SystemTime, UNIX_EPOCH}; -use uuid::Uuid; - -/// Implement the Server trait for a cloud service implemented by [`Service`]. -/// -/// This type implements a TaskChampion server over a basic object-storage service. It encapsulates -/// all of the logic to ensure a linear sequence of versions, encrypt and decrypt data, and clean -/// up old data so that this can be supported on a variety of cloud services. -/// -/// ## Encryption -/// -/// The encryption scheme is described in `sync-protocol.md`. The salt value used for key -/// derivation is stored in "salt", which is created if it does not exist. Object names are not -/// encrypted, by the nature of key/value stores. Since the content of the "latest" object can -/// usually be inferred from object names, it, too, is not encrypted. -/// -/// ## Object Organization -/// -/// UUIDs emebedded in names and values appear in their "simple" form: lower-case hexadecimal with -/// no hyphens. -/// -/// Versions are stored as objects with name `v-PARENT-VERSION` where `PARENT` is the parent -/// version's UUID and `VERSION` is the version's UUID. The object value is the raw history -/// segment. These objects are created with simple `put` requests, as the name uniquely identifies -/// the content. -/// -/// The latest version is stored as an object with name "latest", containing the UUID of the latest -/// version. This file is updated with `compare_and_swap`. After a successful update of this -/// object, the version is considered committed. -/// -/// Since there are no strong constraints on creation of version objects, it is possible -/// to have multiple such files with the same `PARENT`. However, only one such object will be -/// contained in the chain of parent-child relationships beginning with the value in "latest". -/// All other objects are invalid and not visible outside this type. -/// -/// Snapshots are stored as objects with name `s-VERSION` where `VERSION` is the version at which -/// the snapshot was made. These objects are created with simple `put` requests, as any snapshot -/// for a given version is functionally equivalent to any other. -/// -/// ## Cleanup -/// -/// Cleanup of unnecessary data is performed probabalistically after `add_version`, although any -/// errors are ignored. -/// -/// - Any versions not reachable from "latest" and which cannot become "latest" are deleted. -/// - Any snapshots older than the most recent are deleted. -/// - Any versions older than [`MAX_VERSION_AGE_SECS`] which are incorporated into a snapshot -/// are deleted. -pub(in crate::server) struct CloudServer { - service: SVC, - - /// The Cryptor supporting encryption and decryption of objects in this server. - cryptor: Cryptor, - - /// The probability (0..255) that this run will perform cleanup. - cleanup_probability: u8, - - /// For testing, a function that is called in the middle of `add_version` to simulate - /// a concurrent change in the service. - #[cfg(test)] - add_version_intercept: Option, -} - -const LATEST: &[u8] = b"latest"; -const DEFAULT_CLEANUP_PROBABILITY: u8 = 13; // about 5% - -#[cfg(not(test))] -const MAX_VERSION_AGE_SECS: u64 = 3600 * 24 * 180; // about half a year - -fn version_to_bytes(v: VersionId) -> Vec { - v.as_simple().to_string().into_bytes() -} - -impl CloudServer { - pub(in crate::server) fn new(mut service: SVC, encryption_secret: Vec) -> Result { - let salt = Self::get_salt(&mut service)?; - let cryptor = Cryptor::new(salt, &encryption_secret.into())?; - Ok(Self { - service, - cryptor, - cleanup_probability: DEFAULT_CLEANUP_PROBABILITY, - #[cfg(test)] - add_version_intercept: None, - }) - } - - /// Get the salt value stored in the service, creating a new random one if necessary. - fn get_salt(service: &mut SVC) -> Result> { - const SALT_NAME: &[u8] = b"salt"; - loop { - if let Some(salt) = service.get(SALT_NAME)? { - return Ok(salt); - } - service.compare_and_swap(SALT_NAME, None, Cryptor::gen_salt()?)?; - } - } - - /// Generate an object name for the given parent and child versions. - fn version_name(parent_version_id: &VersionId, child_version_id: &VersionId) -> Vec { - format!( - "v-{}-{}", - parent_version_id.as_simple(), - child_version_id.as_simple() - ) - .into_bytes() - } - - /// Parse a version name as generated by `version_name`, returning None if the name does not - /// have a valid format. - fn parse_version_name(name: &[u8]) -> Option<(VersionId, VersionId)> { - if name.len() != 2 + 32 + 1 + 32 || !name.starts_with(b"v-") || name[2 + 32] != b'-' { - return None; - } - let Ok(parent_version_id) = VersionId::try_parse_ascii(&name[2..2 + 32]) else { - return None; - }; - let Ok(child_version_id) = VersionId::try_parse_ascii(&name[2 + 32 + 1..]) else { - return None; - }; - Some((parent_version_id, child_version_id)) - } - - /// Generate an object name for a snapshot at the given version. - fn snapshot_name(version_id: &VersionId) -> Vec { - format!("s-{}", version_id.as_simple()).into_bytes() - } - - /// Parse a snapshot name as generated by `snapshot_name`, returning None if the name does not - /// have a valid format. - fn parse_snapshot_name(name: &[u8]) -> Option { - if name.len() != 2 + 32 || !name.starts_with(b"s-") { - return None; - } - let Ok(version_id) = VersionId::try_parse_ascii(&name[2..2 + 32]) else { - return None; - }; - Some(version_id) - } - - /// Generate a random integer in (0..255) for use in probabalistic decisions. - fn randint(&self) -> Result { - use rand::SecureRandom; - let mut randint = [0u8]; - rand::SystemRandom::new() - .fill(&mut randint) - .map_err(|_| Error::Server("Random number generator failure".into()))?; - Ok(randint[0]) - } - - /// Get the version from "latest", or None if the object does not exist. This always fetches a fresh - /// value from storage. - fn get_latest(&mut self) -> Result> { - let Some(latest) = self.service.get(LATEST)? else { - return Ok(None); - }; - let latest = VersionId::try_parse_ascii(&latest) - .map_err(|_| Error::Server("'latest' object contains invalid data".into()))?; - Ok(Some(latest)) - } - - /// Get the possible child versions of the given parent version, based only on the object - /// names. - fn get_child_versions(&mut self, parent_version_id: &VersionId) -> Result> { - self.service - .list(format!("v-{}-", parent_version_id.as_simple()).as_bytes()) - .filter_map(|res| match res { - Ok(ObjectInfo { name, .. }) => { - if let Some((_, c)) = Self::parse_version_name(&name) { - Some(Ok(c)) - } else { - None - } - } - Err(e) => Some(Err(e)), - }) - .collect::>>() - } - - /// Determine the snapshot urgency. This is done probabalistically: - /// - High urgency approximately 1% of the time. - /// - Low urgency approximately 10% of the time. - fn snapshot_urgency(&self) -> Result { - let r = self.randint()?; - if r < 2 { - Ok(SnapshotUrgency::High) - } else if r < 25 { - Ok(SnapshotUrgency::Low) - } else { - Ok(SnapshotUrgency::None) - } - } - - /// Maybe call `cleanup` depending on `cleanup_probability`. - fn maybe_cleanup(&mut self) -> Result<()> { - if self.randint()? < self.cleanup_probability { - self.cleanup_probability = DEFAULT_CLEANUP_PROBABILITY; - self.cleanup() - } else { - Ok(()) - } - } - - /// Perform cleanup, deleting unnecessary data. - fn cleanup(&mut self) -> Result<()> { - // Construct a vector containing all (child, parent, creation) tuples - let mut versions = self - .service - .list(b"v-") - .filter_map(|res| match res { - Ok(ObjectInfo { name, creation }) => { - if let Some((p, c)) = Self::parse_version_name(&name) { - Some(Ok((c, p, creation))) - } else { - None - } - } - Err(e) => Some(Err(e)), - }) - .collect::>>()?; - versions.sort(); - - // Function to find the parent of a given child version in `versions`, taking - // advantage of having sorted the vector by child version ID. - let parent_of = |c| match versions.binary_search_by_key(&c, |tup| tup.0) { - Ok(idx) => Some(versions[idx].1), - Err(_) => None, - }; - - // Create chains mapping forward (parent -> child) and backward (child -> parent), starting - // at "latest". - let mut rev_chain = HashMap::new(); - let mut iterations = versions.len() + 1; // For cycle detection. - let latest = self.get_latest()?; - if let Some(mut c) = latest { - while let Some(p) = parent_of(c) { - rev_chain.insert(c, p); - c = p; - iterations -= 1; - if iterations == 0 { - return Err(Error::Server("Version cycle detected".into())); - } - } - } - - // Collect all versions older than MAX_VERSION_AGE_SECS - #[cfg(not(test))] - let age_threshold = { - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map(|t| t.as_secs()) - .unwrap_or(0); - now.saturating_sub(MAX_VERSION_AGE_SECS) - }; - - // In testing, cutoff age is 1000. - #[cfg(test)] - let age_threshold = 1000; - - let old_versions: HashSet = versions - .iter() - .filter_map(|(c, _, creation)| { - if *creation < age_threshold { - Some(*c) - } else { - None - } - }) - .collect(); - - // Now, any pair not present in that chain can be deleted. However, another replica - // may be in the state where it has uploaded a version but not changed "latest" yet, - // so any pair with parent equal to latest is allowed to stay. - for (c, p, _) in versions { - if rev_chain.get(&c) != Some(&p) && Some(p) != latest { - self.service.del(&Self::version_name(&p, &c))?; - } - } - - // Collect a set of all snapshots. - let snapshots = self - .service - .list(b"s-") - .filter_map(|res| match res { - Ok(ObjectInfo { name, .. }) => Self::parse_snapshot_name(&name).map(Ok), - Err(e) => Some(Err(e)), - }) - .collect::>>()?; - - // Find the latest snapshot by iterating back from "latest". Note that this iteration is - // guaranteed not to be cyclical, as that was checked above. - let mut latest_snapshot = None; - if let Some(mut version) = latest { - loop { - if snapshots.contains(&version) { - latest_snapshot = Some(version); - break; - } - if let Some(v) = rev_chain.get(&version) { - version = *v; - } else { - break; - } - } - } - - // If there's a latest snapshot, delete all other snapshots. - let Some(latest_snapshot) = latest_snapshot else { - // If there's no snapshot, no further cleanup is possible. - return Ok(()); - }; - for version in snapshots { - if version != latest_snapshot { - self.service.del(&Self::snapshot_name(&version))?; - } - } - - // Now continue iterating backward from that version; any version in `old_versions` can be - // deleted. - let mut version = latest_snapshot; - while let Some(parent) = rev_chain.get(&version) { - if old_versions.contains(&version) { - self.service.del(&Self::version_name(parent, &version))?; - } - version = *parent; - } - - Ok(()) - } -} - -impl Server for CloudServer { - fn add_version( - &mut self, - parent_version_id: VersionId, - history_segment: HistorySegment, - ) -> Result<(AddVersionResult, SnapshotUrgency)> { - let latest = self.get_latest()?; - if let Some(l) = latest { - if l != parent_version_id { - return Ok(( - AddVersionResult::ExpectedParentVersion(l), - self.snapshot_urgency()?, - )); - } - } - - // Invent a new version ID and upload the version data. - let version_id = VersionId::new_v4(); - let new_name = Self::version_name(&parent_version_id, &version_id); - let sealed = self.cryptor.seal(Unsealed { - version_id, - payload: history_segment, - })?; - self.service.put(&new_name, sealed.as_ref())?; - - #[cfg(test)] - if let Some(f) = self.add_version_intercept { - f(&mut self.service); - } - - // Try to compare-and-swap this value into LATEST - let old_value = latest.map(version_to_bytes); - let new_value = version_to_bytes(version_id); - if !self - .service - .compare_and_swap(LATEST, old_value, new_value)? - { - // Delete the version data, since it was not latest. - self.service.del(&new_name)?; - let latest = self.get_latest()?; - let latest = latest.unwrap_or(Uuid::nil()); - return Ok(( - AddVersionResult::ExpectedParentVersion(latest), - self.snapshot_urgency()?, - )); - } - - // Attempt a cleanup, but ignore errors. - let _ = self.maybe_cleanup(); - - Ok((AddVersionResult::Ok(version_id), self.snapshot_urgency()?)) - } - - fn get_child_version(&mut self, parent_version_id: VersionId) -> Result { - // The `get_child_versions` function will usually return only one child version for a - // parent, in which case the work is easy. Otherwise, if there are several possible - // children, only one of those will lead to `latest`, and importantly the others will not - // have their own children. So we can detect the "true" child as the one that is equal to - // "latest" or has children. - let version_id = match &(self.get_child_versions(&parent_version_id)?)[..] { - [] => return Ok(GetVersionResult::NoSuchVersion), - [child] => *child, - children => { - // There are some extra version objects, so a cleanup is warranted. - self.cleanup_probability = 255; - let latest = self.get_latest()?; - let mut true_child = None; - for child in children { - if Some(*child) == latest { - true_child = Some(*child); - break; - } - } - if true_child.is_none() { - for child in children { - if !self.get_child_versions(child)?.is_empty() { - true_child = Some(*child) - } - } - } - match true_child { - Some(true_child) => true_child, - None => return Ok(GetVersionResult::NoSuchVersion), - } - } - }; - - let Some(sealed) = self - .service - .get(&Self::version_name(&parent_version_id, &version_id))? - else { - // This really shouldn't happen, since the chain was derived from object names, but - // perhaps the object was deleted. - return Ok(GetVersionResult::NoSuchVersion); - }; - let unsealed = self.cryptor.unseal(Sealed { - version_id, - payload: sealed, - })?; - Ok(GetVersionResult::Version { - version_id, - parent_version_id, - history_segment: unsealed.into(), - }) - } - - fn add_snapshot(&mut self, version_id: VersionId, snapshot: Snapshot) -> Result<()> { - let name = Self::snapshot_name(&version_id); - let sealed = self.cryptor.seal(Unsealed { - version_id, - payload: snapshot, - })?; - self.service.put(&name, sealed.as_ref())?; - Ok(()) - } - - fn get_snapshot(&mut self) -> Result> { - // Pick the first snapshot we find. - let Some(name) = self.service.list(b"s-").next() else { - return Ok(None); - }; - let ObjectInfo { name, .. } = name?; - let Some(version_id) = Self::parse_snapshot_name(&name) else { - return Ok(None); - }; - let Some(payload) = self.service.get(&name)? else { - return Ok(None); - }; - let unsealed = self.cryptor.unseal(Sealed { - version_id, - payload, - })?; - Ok(Some((version_id, unsealed.payload))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::server::NIL_VERSION_ID; - - /// A simple in-memory service for testing. All insertions via Service methods occur at time - /// `INSERTION_TIME`. All versions older that 1000 are considered "old". - #[derive(Clone)] - struct MockService(HashMap, (u64, Vec)>); - - const INSERTION_TIME: u64 = 9999999999; - - impl MockService { - fn new() -> Self { - let mut map = HashMap::new(); - // Use a fixed salt for consistent results - map.insert(b"salt".to_vec(), (0, b"abcdefghabcdefgh".to_vec())); - Self(map) - } - } - - impl Service for MockService { - fn put(&mut self, name: &[u8], value: &[u8]) -> Result<()> { - self.0 - .insert(name.to_vec(), (INSERTION_TIME, value.to_vec())); - Ok(()) - } - - fn get(&mut self, name: &[u8]) -> Result>> { - Ok(self.0.get(name).map(|(_, data)| data.clone())) - } - - fn del(&mut self, name: &[u8]) -> Result<()> { - self.0.remove(name); - Ok(()) - } - - fn compare_and_swap( - &mut self, - name: &[u8], - existing_value: Option>, - new_value: Vec, - ) -> Result { - if self.0.get(name).map(|(_, d)| d) == existing_value.as_ref() { - self.0.insert(name.to_vec(), (INSERTION_TIME, new_value)); - return Ok(true); - } - Ok(false) - } - - fn list<'a>( - &'a mut self, - prefix: &[u8], - ) -> Box> + 'a> { - let prefix = prefix.to_vec(); - Box::new( - self.0 - .iter() - .filter(move |(k, _)| k.starts_with(&prefix)) - .map(|(k, (t, _))| { - Ok(ObjectInfo { - name: k.to_vec(), - creation: *t, - }) - }), - ) - } - } - - impl std::fmt::Debug for MockService { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_map() - .entries( - self.0 - .iter() - .map(|(k, v)| (std::str::from_utf8(k).unwrap(), v)), - ) - .finish() - } - } - - // Add some testing utilities to CloudServer. - impl CloudServer { - fn mock_add_version( - &mut self, - parent: VersionId, - child: VersionId, - creation: u64, - data: &[u8], - ) { - let name = Self::version_name(&parent, &child); - let sealed = self - .cryptor - .seal(Unsealed { - version_id: child, - payload: data.into(), - }) - .unwrap(); - self.service.0.insert(name, (creation, sealed.into())); - } - - fn mock_add_snapshot(&mut self, version: VersionId, creation: u64, snapshot: &[u8]) { - let name = Self::snapshot_name(&version); - let sealed = self - .cryptor - .seal(Unsealed { - version_id: version, - payload: snapshot.into(), - }) - .unwrap(); - self.service.0.insert(name, (creation, sealed.into())); - } - - fn mock_set_latest(&mut self, latest: VersionId) { - let latest = version_to_bytes(latest); - self.service - .0 - .insert(LATEST.to_vec(), (INSERTION_TIME, latest)); - } - - /// Create a copy of this server without any data; used for creating a MockService - /// to compare to with `assert_eq!` - fn empty_clone(&self) -> Self { - Self { - cryptor: self.cryptor.clone(), - cleanup_probability: 0, - service: MockService::new(), - add_version_intercept: None, - } - } - - /// Get a decrypted, string-y copy of the data in the HashMap. - fn unencrypted(&self) -> HashMap { - self.service - .0 - .iter() - .map(|(k, v)| { - let kstr = String::from_utf8(k.to_vec()).unwrap(); - if kstr == "latest" { - return (kstr, (v.0, String::from_utf8(v.1.to_vec()).unwrap())); - } - - let version_id; - if let Some((_, v)) = Self::parse_version_name(k) { - version_id = v; - } else if let Some(v) = Self::parse_snapshot_name(k) { - version_id = v; - } else { - return (kstr, (v.0, format!("{:?}", v.1))); - } - - let unsealed = self - .cryptor - .unseal(Sealed { - version_id, - payload: v.1.to_vec(), - }) - .unwrap(); - let vstr = String::from_utf8(unsealed.into()).unwrap(); - (kstr, (v.0, vstr)) - }) - .collect() - } - } - impl Clone for CloudServer { - fn clone(&self) -> Self { - Self { - cryptor: self.cryptor.clone(), - cleanup_probability: self.cleanup_probability, - service: self.service.clone(), - add_version_intercept: None, - } - } - } - - const SECRET: &[u8] = b"testing"; - - fn make_server() -> CloudServer { - let mut server = CloudServer::new(MockService::new(), SECRET.into()).unwrap(); - // Prevent cleanup during tests. - server.cleanup_probability = 0; - server - } - - #[test] - fn version_name() { - let p = Uuid::parse_str("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8").unwrap(); - let c = Uuid::parse_str("adcf4e350fa54e4aaf9d3f20f3ba5a32").unwrap(); - assert_eq!( - CloudServer::::version_name(&p, &c), - b"v-a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8-adcf4e350fa54e4aaf9d3f20f3ba5a32" - ); - } - - #[test] - fn version_name_round_trip() { - let p = Uuid::new_v4(); - let c = Uuid::new_v4(); - assert_eq!( - CloudServer::::parse_version_name( - &CloudServer::::version_name(&p, &c) - ), - Some((p, c)) - ); - } - - #[test] - fn parse_version_name_bad_prefix() { - assert_eq!( - CloudServer::::parse_version_name( - b"X-a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8-adcf4e350fa54e4aaf9d3f20f3ba5a32" - ), - None - ); - } - - #[test] - fn parse_version_name_bad_separator() { - assert_eq!( - CloudServer::::parse_version_name( - b"v-a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8xadcf4e350fa54e4aaf9d3f20f3ba5a32" - ), - None - ); - } - - #[test] - fn parse_version_name_too_short() { - assert_eq!( - CloudServer::::parse_version_name( - b"v-a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8-adcf4e350fa54e4aaf9d3f20f3ba5a3" - ), - None - ); - } - - #[test] - fn parse_version_name_too_long() { - assert_eq!( - CloudServer::::parse_version_name( - b"v-a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8-adcf4e350fa54e4aaf9d3f20f3ba5a320" - ), - None - ); - } - - #[test] - fn snapshot_name_round_trip() { - let v = Uuid::new_v4(); - assert_eq!( - CloudServer::::parse_snapshot_name( - &CloudServer::::snapshot_name(&v) - ), - Some(v) - ); - } - - #[test] - fn parse_snapshot_name_invalid() { - assert_eq!( - CloudServer::::parse_snapshot_name(b"s-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"), - None - ); - } - - #[test] - fn parse_snapshot_name_bad_prefix() { - assert_eq!( - CloudServer::::parse_snapshot_name(b"s:a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"), - None - ); - } - - #[test] - fn parse_snapshot_name_too_short() { - assert_eq!( - CloudServer::::parse_snapshot_name(b"s-a1a2a3a4b1b2c1c2d1d2d3d4d5d6"), - None - ); - } - - #[test] - fn parse_snapshot_name_too_long() { - assert_eq!( - CloudServer::::parse_snapshot_name( - b"s-a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8000" - ), - None - ); - } - - #[test] - fn get_salt_existing() { - let mut service = MockService::new(); - assert_eq!( - CloudServer::::get_salt(&mut service).unwrap(), - b"abcdefghabcdefgh".to_vec() - ); - } - - #[test] - fn get_salt_create() { - let mut service = MockService::new(); - service.del(b"salt").unwrap(); - let got_salt = CloudServer::::get_salt(&mut service).unwrap(); - let salt_obj = service.get(b"salt").unwrap().unwrap(); - assert_eq!(got_salt, salt_obj); - } - - #[test] - fn get_latest_empty() { - let mut server = make_server(); - assert_eq!(server.get_latest().unwrap(), None); - } - - #[test] - fn get_latest_exists() { - let mut server = make_server(); - let latest = Uuid::new_v4(); - server.mock_set_latest(latest); - assert_eq!(server.get_latest().unwrap(), Some(latest)); - } - - #[test] - fn get_latest_invalid() { - let mut server = make_server(); - server - .service - .0 - .insert(LATEST.to_vec(), (999, b"not-a-uuid".to_vec())); - assert!(server.get_latest().is_err()); - } - - #[test] - fn get_child_versions_empty() { - let mut server = make_server(); - assert_eq!(server.get_child_versions(&Uuid::new_v4()).unwrap(), vec![]); - } - - #[test] - fn get_child_versions_single() { - let mut server = make_server(); - let (v1, v2) = (Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v2, v1, 1000, b"first"); - assert_eq!(server.get_child_versions(&v1).unwrap(), vec![]); - assert_eq!(server.get_child_versions(&v2).unwrap(), vec![v1]); - } - - #[test] - fn get_child_versions_multiple() { - let mut server = make_server(); - let (v1, v2, v3) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v3, v1, 1000, b"first"); - server.mock_add_version(v3, v2, 1000, b"second"); - assert_eq!(server.get_child_versions(&v1).unwrap(), vec![]); - assert_eq!(server.get_child_versions(&v2).unwrap(), vec![]); - let versions = server.get_child_versions(&v3).unwrap(); - assert!(versions == vec![v1, v2] || versions == vec![v2, v1]); - } - - #[test] - fn add_version_empty() { - let mut server = make_server(); - let parent = Uuid::new_v4(); - let (res, _) = server.add_version(parent, b"history".to_vec()).unwrap(); - assert!(matches!(res, AddVersionResult::Ok(_))); - } - - #[test] - fn add_version_good() { - let mut server = make_server(); - let (v1, v2) = (Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v1, v2, 1000, b"first"); - server.mock_set_latest(v2); - - let (res, _) = server.add_version(v2, b"history".to_vec()).unwrap(); - let AddVersionResult::Ok(new_version) = res else { - panic!("expected OK"); - }; - - let mut expected = server.empty_clone(); - expected.mock_add_version(v1, v2, 1000, b"first"); - expected.mock_add_version(v2, new_version, INSERTION_TIME, b"history"); - expected.mock_set_latest(new_version); - - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn add_version_not_latest() { - // The `add_version` method does nothing if the version is not latest. - let mut server = make_server(); - let (v1, v2) = (Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v1, v2, 1000, b"first"); - server.mock_set_latest(v2); - - let expected = server.clone(); - - let (res, _) = server.add_version(v1, b"history".to_vec()).unwrap(); - assert_eq!(res, AddVersionResult::ExpectedParentVersion(v2)); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn add_version_not_latest_race() { - // The `add_version` function effectively checks twice for a conflict: once by just - // fetching "latest", returning early if the value is not as expected; and once in the - // compare-and-swap. This test uses `add_version_intercept` to force the first check to - // succeed and the second test to fail. - let mut server = make_server(); - let (v1, v2) = (Uuid::new_v4(), Uuid::new_v4()); - const V3: Uuid = Uuid::max(); - server.mock_add_version(v1, v2, 1000, b"first"); - server.mock_add_version(v2, V3, 1000, b"second"); - server.mock_set_latest(v2); - server.add_version_intercept = Some(|service| { - service.put(LATEST, &version_to_bytes(V3)).unwrap(); - }); - - let mut expected = server.empty_clone(); - expected.mock_add_version(v1, v2, 1000, b"first"); - expected.mock_add_version(v2, V3, 1000, b"second"); - expected.mock_set_latest(V3); // updated by the intercept - - assert_ne!(server.unencrypted(), expected.unencrypted()); - let (res, _) = server.add_version(v2, b"history".to_vec()).unwrap(); - assert_eq!(res, AddVersionResult::ExpectedParentVersion(V3)); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn add_version_unknown() { - let mut server = make_server(); - let (v1, v2) = (Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v1, v2, 1000, b"first"); - server.mock_set_latest(v2); - - let expected = server.clone(); - - let (res, _) = server - .add_version(Uuid::new_v4(), b"history".to_vec()) - .unwrap(); - assert_eq!(res, AddVersionResult::ExpectedParentVersion(v2)); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn get_child_version_empty() { - let mut server = make_server(); - assert_eq!( - server.get_child_version(Uuid::new_v4()).unwrap(), - GetVersionResult::NoSuchVersion - ); - } - - #[test] - fn get_child_version_single() { - let mut server = make_server(); - let (v1, v2) = (Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v2, v1, 1000, b"first"); - assert_eq!( - server.get_child_version(v1).unwrap(), - GetVersionResult::NoSuchVersion - ); - assert_eq!( - server.get_child_version(v2).unwrap(), - GetVersionResult::Version { - version_id: v1, - parent_version_id: v2, - history_segment: b"first".to_vec(), - } - ); - } - - #[test] - fn get_child_version_multiple() { - let mut server = make_server(); - let (v1, v2, v3) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - let (vx, vy, vz) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v1, v2, 1000, b"second"); - server.mock_add_version(v1, vx, 1000, b"false start x"); - server.mock_add_version(v1, vy, 1000, b"false start y"); - server.mock_add_version(v2, v3, 1000, b"third"); - server.mock_add_version(v2, vz, 1000, b"false start z"); - server.mock_set_latest(v3); - assert_eq!( - server.get_child_version(v1).unwrap(), - GetVersionResult::Version { - version_id: v2, - parent_version_id: v1, - history_segment: b"second".to_vec(), - } - ); - assert_eq!( - server.get_child_version(v2).unwrap(), - GetVersionResult::Version { - version_id: v3, - parent_version_id: v2, - history_segment: b"third".to_vec(), - } - ); - assert_eq!( - server.get_child_version(v3).unwrap(), - GetVersionResult::NoSuchVersion - ); - } - - #[test] - fn cleanup_empty() { - let mut server = make_server(); - server.cleanup().unwrap(); - } - - #[test] - fn cleanup_linear() { - // Test that cleanup does nothing for a linear version history with a snapshot at the - // oldest version. - let mut server = make_server(); - let (v1, v2, v3) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(NIL_VERSION_ID, v1, 1000, b"first"); - server.mock_add_version(v1, v2, 1000, b"second"); - server.mock_add_version(v2, v3, 1000, b"third"); - server.mock_add_snapshot(v1, 1000, b"snap 1"); - server.mock_set_latest(v3); - - let expected = server.clone(); - - server.cleanup().unwrap(); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn cleanup_cycle() { - // When a cycle is present, cleanup succeeds and makes no changes. - let mut server = make_server(); - let (v1, v2, v3) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v3, v1, 1000, b"first"); - server.mock_add_version(v1, v2, 1000, b"second"); - server.mock_add_version(v2, v3, 1000, b"third"); - server.mock_set_latest(v3); - - let expected = server.clone(); - - assert!(server.cleanup().is_err()); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn cleanup_extra_branches() { - // Cleanup deletes extra branches in the versions. - let mut server = make_server(); - let (v1, v2, v3) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - let (vx, vy) = (Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v1, v2, 1000, b"second"); - server.mock_add_version(v1, vx, 1000, b"false start x"); - server.mock_add_version(v2, v3, 1000, b"third"); - server.mock_add_version(v2, vy, 1000, b"false start y"); - server.mock_set_latest(v3); - - let mut expected = server.empty_clone(); - expected.mock_add_version(v1, v2, 1000, b"second"); - expected.mock_add_version(v2, v3, 1000, b"third"); - expected.mock_set_latest(v3); - - assert_ne!(server.unencrypted(), expected.unencrypted()); - server.cleanup().unwrap(); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn cleanup_extra_snapshots() { - let mut server = make_server(); - let (v1, v2, v3) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - let vy = Uuid::new_v4(); - server.mock_add_version(v1, v2, 1000, b"second"); - server.mock_add_version(v2, v3, 1000, b"third"); - server.mock_add_version(v2, vy, 1000, b"false start y"); - server.mock_add_snapshot(v1, 1000, b"snap 1"); - server.mock_add_snapshot(v2, 1000, b"snap 2"); - server.mock_add_snapshot(vy, 1000, b"snap y"); - server.mock_set_latest(v3); - - let mut expected = server.empty_clone(); - expected.mock_add_version(v1, v2, 1000, b"second"); - expected.mock_add_version(v2, v3, 1000, b"third"); - expected.mock_add_snapshot(v2, 1000, b"snap 2"); - expected.mock_set_latest(v3); - - assert_ne!(server.unencrypted(), expected.unencrypted()); - server.cleanup().unwrap(); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn cleanup_old_versions_no_snapshot() { - // If there are old versions ,but no snapshot, nothing is cleaned up. - let mut server = make_server(); - let (v1, v2, v3) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v1, v2, 200, b"second"); - server.mock_add_version(v2, v3, 300, b"third"); - server.mock_set_latest(v3); - - let expected = server.clone(); - - server.cleanup().unwrap(); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn cleanup_old_versions_with_snapshot() { - // If there are old versions that are also older than a snapshot, they are - // cleaned up. - let mut server = make_server(); - let (v1, v2, v3) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - let (v4, v5, v6) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v1, v2, 200, b"second"); - server.mock_add_version(v2, v3, 300, b"third"); - server.mock_add_version(v3, v4, 1400, b"fourth"); - server.mock_add_version(v4, v5, 1500, b"fifth"); - server.mock_add_snapshot(v5, 1501, b"snap 1"); - server.mock_add_version(v5, v6, 1600, b"sixth"); - server.mock_set_latest(v6); - - let mut expected = server.empty_clone(); - expected.mock_add_version(v3, v4, 1400, b"fourth"); // Not old enough to be deleted. - expected.mock_add_version(v4, v5, 1500, b"fifth"); - expected.mock_add_snapshot(v5, 1501, b"snap 1"); - expected.mock_add_version(v5, v6, 1600, b"sixth"); - expected.mock_set_latest(v6); - - assert_ne!(server.unencrypted(), expected.unencrypted()); - server.cleanup().unwrap(); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn cleanup_old_versions_newer_than_snapshot() { - // Old versions that are newer than the latest snapshot are not cleaned up. - let mut server = make_server(); - let (v1, v2, v3) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - let (v4, v5, v6) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v1, v2, 200, b"second"); - server.mock_add_version(v2, v3, 300, b"third"); - server.mock_add_snapshot(v3, 301, b"snap 1"); - server.mock_add_version(v3, v4, 400, b"fourth"); - server.mock_add_version(v4, v5, 500, b"fifth"); - server.mock_add_version(v5, v6, 600, b"sixth"); - server.mock_set_latest(v6); - - let mut expected = server.empty_clone(); - expected.mock_add_snapshot(v3, 301, b"snap 1"); - expected.mock_add_version(v3, v4, 400, b"fourth"); - expected.mock_add_version(v4, v5, 500, b"fifth"); - expected.mock_add_version(v5, v6, 600, b"sixth"); - expected.mock_set_latest(v6); - - assert_ne!(server.unencrypted(), expected.unencrypted()); - server.cleanup().unwrap(); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn cleanup_children_of_latest() { - // New versions that are children of the latest version are not cleaned up. - let mut server = make_server(); - let (v1, v2, v3) = (Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()); - let (vnew1, vnew2) = (Uuid::new_v4(), Uuid::new_v4()); - server.mock_add_version(v1, v2, 1000, b"second"); - server.mock_add_version(v2, v3, 1000, b"third"); - server.mock_add_version(v3, vnew1, 1000, b"new 1"); - server.mock_add_version(v3, vnew2, 1000, b"new 2"); - // Two replicas are adding new versions, but v3 is still latest. - server.mock_set_latest(v3); - - let expected = server.clone(); - - server.cleanup().unwrap(); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn add_snapshot() { - let mut server = make_server(); - let v = Uuid::new_v4(); - - let mut expected = server.empty_clone(); - expected.mock_add_snapshot(v, INSERTION_TIME, b"SNAP"); - - assert_ne!(server.unencrypted(), expected.unencrypted()); - server.add_snapshot(v, b"SNAP".to_vec()).unwrap(); - assert_eq!(server.unencrypted(), expected.unencrypted()); - } - - #[test] - fn get_snapshot_missing() { - let mut server = make_server(); - assert_eq!(server.get_snapshot().unwrap(), None); - } - - #[test] - fn get_snapshot_present() { - let mut server = make_server(); - let v = Uuid::new_v4(); - server.mock_add_snapshot(v, 1000, b"SNAP"); - assert_eq!(server.get_snapshot().unwrap(), Some((v, b"SNAP".to_vec()))); - } -} diff --git a/taskchampion/taskchampion/src/server/cloud/service.rs b/taskchampion/taskchampion/src/server/cloud/service.rs deleted file mode 100644 index 84b1c24ef..000000000 --- a/taskchampion/taskchampion/src/server/cloud/service.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::errors::Result; - -/// Information about an object as returned from `Service::list` -pub(in crate::server) struct ObjectInfo { - /// Name of the object. - pub(in crate::server) name: Vec, - /// Creation time of the object, in seconds since the UNIX epoch. - pub(in crate::server) creation: u64, -} - -/// An abstraction of a cloud-storage service. -/// -/// The underlying cloud storage is assumed to be a map from object names to object values, -/// similar to a HashMap, with the addition of a compare-and-swap operation. Object names -/// are always simple strings from the character set `[a-zA-Z0-9-]`, no more than 100 characters -/// in length. -pub(in crate::server) trait Service { - /// Put an object into cloud storage. If the object exists, it is overwritten. - fn put(&mut self, name: &[u8], value: &[u8]) -> Result<()>; - - /// Get an object from cloud storage, or None if the object does not exist. - fn get(&mut self, name: &[u8]) -> Result>>; - - /// Delete an object. Does nothing if the object does not exist. - fn del(&mut self, name: &[u8]) -> Result<()>; - - /// Enumerate objects with the given prefix. - fn list<'a>(&'a mut self, prefix: &[u8]) -> Box> + 'a>; - - /// Compare the existing object's value with `existing_value`, and replace with `new_value` - /// only if the values match. Returns true if the replacement occurred. - fn compare_and_swap( - &mut self, - name: &[u8], - existing_value: Option>, - new_value: Vec, - ) -> Result; -} diff --git a/taskchampion/taskchampion/src/server/config.rs b/taskchampion/taskchampion/src/server/config.rs deleted file mode 100644 index 20e0e6598..000000000 --- a/taskchampion/taskchampion/src/server/config.rs +++ /dev/null @@ -1,74 +0,0 @@ -use super::types::Server; -use crate::errors::Result; -#[cfg(feature = "server-gcp")] -use crate::server::cloud::gcp::GcpService; -#[cfg(feature = "cloud")] -use crate::server::cloud::CloudServer; -use crate::server::local::LocalServer; -#[cfg(feature = "server-sync")] -use crate::server::sync::SyncServer; -use std::path::PathBuf; -#[cfg(feature = "server-sync")] -use uuid::Uuid; - -/// The configuration for a replica's access to a sync server. -pub enum ServerConfig { - /// A local task database, for situations with a single replica. - Local { - /// Path containing the server's DB - server_dir: PathBuf, - }, - /// A remote taskchampion-sync-server instance - #[cfg(feature = "server-sync")] - Remote { - /// Sync server "origin"; a URL with schema and hostname but no path or trailing `/` - origin: String, - - /// Client ID to identify and authenticate this replica to the server - client_id: Uuid, - - /// Private encryption secret used to encrypt all data sent to the server. This can - /// be any suitably un-guessable string of bytes. - encryption_secret: Vec, - }, - /// A remote taskchampion-sync-server instance - #[cfg(feature = "server-gcp")] - Gcp { - /// Bucket in which to store the task data. This bucket must not be used for any other - /// purpose. - bucket: String, - /// Path to a GCP credential file, in JSON format. This is required for GCP access incase - /// some other application already makes use of Application Default Credentials. - /// See https://cloud.google.com/docs/authentication#service-accounts for more details. - /// See https://cloud.google.com/iam/docs/keys-create-delete for instructions on how to - /// create a service account key. - credential_path: Option, - /// Private encryption secret used to encrypt all data sent to the server. This can - /// be any suitably un-guessable string of bytes. - encryption_secret: Vec, - }, -} - -impl ServerConfig { - /// Get a server based on this configuration - pub fn into_server(self) -> Result> { - Ok(match self { - ServerConfig::Local { server_dir } => Box::new(LocalServer::new(server_dir)?), - #[cfg(feature = "server-sync")] - ServerConfig::Remote { - origin, - client_id, - encryption_secret, - } => Box::new(SyncServer::new(origin, client_id, encryption_secret)?), - #[cfg(feature = "server-gcp")] - ServerConfig::Gcp { - bucket, - credential_path, - encryption_secret, - } => Box::new(CloudServer::new( - GcpService::new(bucket, credential_path)?, - encryption_secret, - )?), - }) - } -} diff --git a/taskchampion/taskchampion/src/server/encryption.rs b/taskchampion/taskchampion/src/server/encryption.rs deleted file mode 100644 index 23fa17a9c..000000000 --- a/taskchampion/taskchampion/src/server/encryption.rs +++ /dev/null @@ -1,414 +0,0 @@ -/// This module implements the encryption specified in the sync-protocol -/// document. -use crate::errors::{Error, Result}; -use ring::{aead, pbkdf2, rand, rand::SecureRandom}; -use uuid::Uuid; - -const PBKDF2_ITERATIONS: u32 = 600000; -const ENVELOPE_VERSION: u8 = 1; -const AAD_LEN: usize = 17; -const TASK_APP_ID: u8 = 1; - -/// An Cryptor stores a secret and allows sealing and unsealing. It derives a key from the secret, -/// which takes a nontrivial amount of time, so it should be created once and re-used for the given -/// context. -#[derive(Clone)] -pub(super) struct Cryptor { - key: aead::LessSafeKey, - rng: rand::SystemRandom, -} - -impl Cryptor { - pub(super) fn new(salt: impl AsRef<[u8]>, secret: &Secret) -> Result { - Ok(Cryptor { - key: Self::derive_key(salt, secret)?, - rng: rand::SystemRandom::new(), - }) - } - - /// Generate a suitable random salt. - pub(super) fn gen_salt() -> Result> { - let rng = rand::SystemRandom::new(); - let mut salt = [0u8; 16]; - rng.fill(&mut salt) - .map_err(|_| anyhow::anyhow!("error generating random salt"))?; - Ok(salt.to_vec()) - } - - /// Derive a key as specified for version 1. Note that this may take 10s of ms. - fn derive_key(salt: impl AsRef<[u8]>, secret: &Secret) -> Result { - let mut key_bytes = vec![0u8; aead::CHACHA20_POLY1305.key_len()]; - pbkdf2::derive( - pbkdf2::PBKDF2_HMAC_SHA256, - std::num::NonZeroU32::new(PBKDF2_ITERATIONS).unwrap(), - salt.as_ref(), - secret.as_ref(), - &mut key_bytes, - ); - - let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key_bytes) - .map_err(|_| anyhow::anyhow!("error while creating AEAD key"))?; - Ok(aead::LessSafeKey::new(unbound_key)) - } - - /// Encrypt the given payload. - pub(super) fn seal(&self, payload: Unsealed) -> Result { - let Unsealed { - version_id, - mut payload, - } = payload; - - let mut nonce_buf = [0u8; aead::NONCE_LEN]; - self.rng - .fill(&mut nonce_buf) - .map_err(|_| anyhow::anyhow!("error generating random nonce"))?; - let nonce = aead::Nonce::assume_unique_for_key(nonce_buf); - - let aad = self.make_aad(version_id); - - let tag = self - .key - .seal_in_place_separate_tag(nonce, aad, &mut payload) - .map_err(|_| anyhow::anyhow!("error while sealing"))?; - payload.extend_from_slice(tag.as_ref()); - - let env = Envelope { - nonce: &nonce_buf, - payload: payload.as_ref(), - }; - - Ok(Sealed { - version_id, - payload: env.to_bytes(), - }) - } - - /// Decrypt the given payload, verifying it was created for the given version_id - pub(super) fn unseal(&self, payload: Sealed) -> Result { - let Sealed { - version_id, - payload, - } = payload; - - let env = Envelope::from_bytes(&payload)?; - - let mut nonce = [0u8; aead::NONCE_LEN]; - nonce.copy_from_slice(env.nonce); - let nonce = aead::Nonce::assume_unique_for_key(nonce); - let aad = self.make_aad(version_id); - - let mut payload = env.payload.to_vec(); - let plaintext = self - .key - .open_in_place(nonce, aad, payload.as_mut()) - .map_err(|_| anyhow::anyhow!("error while unsealing encrypted value"))?; - - Ok(Unsealed { - version_id, - payload: plaintext.to_vec(), - }) - } - - fn make_aad(&self, version_id: Uuid) -> aead::Aad<[u8; AAD_LEN]> { - let mut aad = [0u8; AAD_LEN]; - aad[0] = TASK_APP_ID; - aad[1..].copy_from_slice(version_id.as_bytes()); - aead::Aad::from(aad) - } -} - -/// Secret represents a secret key as used for encryption and decryption. -pub(super) struct Secret(pub(super) Vec); - -impl From> for Secret { - fn from(bytes: Vec) -> Self { - Self(bytes) - } -} - -impl AsRef<[u8]> for Secret { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -/// Envelope for the data stored on the server, containing the information -/// required to decrypt. -#[derive(Debug, PartialEq, Eq)] -struct Envelope<'a> { - nonce: &'a [u8], - payload: &'a [u8], -} - -impl<'a> Envelope<'a> { - fn from_bytes(buf: &'a [u8]) -> Result> { - if buf.len() <= 1 + aead::NONCE_LEN { - return Err(Error::Server(String::from("envelope is too small"))); - } - - let version = buf[0]; - if version != ENVELOPE_VERSION { - return Err(Error::Server(format!( - "unrecognized encryption envelope version {}", - version - ))); - } - - Ok(Envelope { - nonce: &buf[1..1 + aead::NONCE_LEN], - payload: &buf[1 + aead::NONCE_LEN..], - }) - } - - fn to_bytes(&self) -> Vec { - let mut buf = Vec::with_capacity(1 + self.nonce.len() + self.payload.len()); - - buf.push(ENVELOPE_VERSION); - buf.extend_from_slice(self.nonce); - buf.extend_from_slice(self.payload); - buf - } -} - -/// A unsealed payload with an attached version_id. The version_id is used to -/// validate the context of the payload on unsealing. -pub(super) struct Unsealed { - pub(super) version_id: Uuid, - pub(super) payload: Vec, -} - -impl From for Vec { - fn from(val: Unsealed) -> Self { - val.payload - } -} - -/// An encrypted payload -pub(super) struct Sealed { - pub(super) version_id: Uuid, - pub(super) payload: Vec, -} - -impl AsRef<[u8]> for Sealed { - fn as_ref(&self) -> &[u8] { - self.payload.as_ref() - } -} - -impl From for Vec { - fn from(val: Sealed) -> Self { - val.payload - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - fn make_salt() -> Vec { - Cryptor::gen_salt().unwrap() - } - - #[test] - fn envelope_round_trip() { - let env = Envelope { - nonce: &[2; 12], - payload: b"HELLO", - }; - - let bytes = env.to_bytes(); - let env2 = Envelope::from_bytes(&bytes).unwrap(); - assert_eq!(env, env2); - } - - #[test] - fn envelope_bad_version() { - let env = Envelope { - nonce: &[2; 12], - payload: b"HELLO", - }; - - let mut bytes = env.to_bytes(); - bytes[0] = 99; - assert!(Envelope::from_bytes(&bytes).is_err()); - } - - #[test] - fn envelope_too_short() { - let env = Envelope { - nonce: &[2; 12], - payload: b"HELLO", - }; - - let bytes = env.to_bytes(); - let bytes = &bytes[..10]; - assert!(Envelope::from_bytes(bytes).is_err()); - } - - #[test] - fn round_trip() { - let version_id = Uuid::new_v4(); - let payload = b"HISTORY REPEATS ITSELF".to_vec(); - - let secret = Secret(b"SEKRIT".to_vec()); - let cryptor = Cryptor::new(make_salt(), &secret).unwrap(); - - let unsealed = Unsealed { - version_id, - payload: payload.clone(), - }; - let sealed = cryptor.seal(unsealed).unwrap(); - let unsealed = cryptor.unseal(sealed).unwrap(); - - assert_eq!(unsealed.payload, payload); - assert_eq!(unsealed.version_id, version_id); - } - - #[test] - fn round_trip_bad_key() { - let version_id = Uuid::new_v4(); - let payload = b"HISTORY REPEATS ITSELF".to_vec(); - let salt = make_salt(); - - let secret = Secret(b"SEKRIT".to_vec()); - let cryptor = Cryptor::new(&salt, &secret).unwrap(); - - let unsealed = Unsealed { - version_id, - payload, - }; - let sealed = cryptor.seal(unsealed).unwrap(); - - let secret = Secret(b"DIFFERENT_SECRET".to_vec()); - let cryptor = Cryptor::new(&salt, &secret).unwrap(); - assert!(cryptor.unseal(sealed).is_err()); - } - - #[test] - fn round_trip_bad_version() { - let version_id = Uuid::new_v4(); - let payload = b"HISTORY REPEATS ITSELF".to_vec(); - - let secret = Secret(b"SEKRIT".to_vec()); - let cryptor = Cryptor::new(make_salt(), &secret).unwrap(); - - let unsealed = Unsealed { - version_id, - payload, - }; - let mut sealed = cryptor.seal(unsealed).unwrap(); - sealed.version_id = Uuid::new_v4(); // change the version_id - assert!(cryptor.unseal(sealed).is_err()); - } - - #[test] - fn round_trip_bad_salt() { - let version_id = Uuid::new_v4(); - let payload = b"HISTORY REPEATS ITSELF".to_vec(); - - let secret = Secret(b"SEKRIT".to_vec()); - let cryptor = Cryptor::new(make_salt(), &secret).unwrap(); - - let unsealed = Unsealed { - version_id, - payload, - }; - let sealed = cryptor.seal(unsealed).unwrap(); - - let cryptor = Cryptor::new(make_salt(), &secret).unwrap(); - assert!(cryptor.unseal(sealed).is_err()); - } - - mod externally_valid { - // validate data generated by generate-test-data.py. The intent is to - // validate that this format matches the specification by implementing - // the specification in a second language - use super::*; - use pretty_assertions::assert_eq; - - /// The values in generate-test-data.py - fn defaults() -> (Uuid, Vec, Vec) { - let version_id = Uuid::parse_str("b0517957-f912-4d49-8330-f612e73030c4").unwrap(); - let encryption_secret = b"b4a4e6b7b811eda1dc1a2693ded".to_vec(); - let client_id = Uuid::parse_str("0666d464-418a-4a08-ad53-6f15c78270cd").unwrap(); - let salt = client_id.as_bytes().to_vec(); - (version_id, salt, encryption_secret) - } - - #[test] - fn good() { - let (version_id, salt, encryption_secret) = defaults(); - let sealed = Sealed { - version_id, - payload: include_bytes!("test-good.data").to_vec(), - }; - - let cryptor = Cryptor::new(salt, &Secret(encryption_secret)).unwrap(); - let unsealed = cryptor.unseal(sealed).unwrap(); - - assert_eq!(unsealed.payload, b"SUCCESS"); - assert_eq!(unsealed.version_id, version_id); - } - - #[test] - fn bad_version_id() { - let (version_id, salt, encryption_secret) = defaults(); - let sealed = Sealed { - version_id, - payload: include_bytes!("test-bad-version-id.data").to_vec(), - }; - - let cryptor = Cryptor::new(salt, &Secret(encryption_secret)).unwrap(); - assert!(cryptor.unseal(sealed).is_err()); - } - - #[test] - fn bad_salt() { - let (version_id, salt, encryption_secret) = defaults(); - let sealed = Sealed { - version_id, - payload: include_bytes!("test-bad-client-id.data").to_vec(), - }; - - let cryptor = Cryptor::new(salt, &Secret(encryption_secret)).unwrap(); - assert!(cryptor.unseal(sealed).is_err()); - } - - #[test] - fn bad_secret() { - let (version_id, salt, encryption_secret) = defaults(); - let sealed = Sealed { - version_id, - payload: include_bytes!("test-bad-secret.data").to_vec(), - }; - - let cryptor = Cryptor::new(salt, &Secret(encryption_secret)).unwrap(); - assert!(cryptor.unseal(sealed).is_err()); - } - - #[test] - fn bad_version() { - let (version_id, salt, encryption_secret) = defaults(); - let sealed = Sealed { - version_id, - payload: include_bytes!("test-bad-version.data").to_vec(), - }; - - let cryptor = Cryptor::new(salt, &Secret(encryption_secret)).unwrap(); - assert!(cryptor.unseal(sealed).is_err()); - } - - #[test] - fn bad_app_id() { - let (version_id, salt, encryption_secret) = defaults(); - let sealed = Sealed { - version_id, - payload: include_bytes!("test-bad-app-id.data").to_vec(), - }; - - let cryptor = Cryptor::new(salt, &Secret(encryption_secret)).unwrap(); - assert!(cryptor.unseal(sealed).is_err()); - } - } -} diff --git a/taskchampion/taskchampion/src/server/generate-test-data.py b/taskchampion/taskchampion/src/server/generate-test-data.py deleted file mode 100644 index d4f818190..000000000 --- a/taskchampion/taskchampion/src/server/generate-test-data.py +++ /dev/null @@ -1,77 +0,0 @@ -# This file generates test-encrypted.data. To run it: -# - pip install cryptography pbkdf2 -# - python taskchampion/taskchampion/src/server/generate-test-data.py taskchampion/taskchampion/src/server/ - -import os -import hashlib -import pbkdf2 -import secrets -import sys -import uuid - -from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 - -# these values match values used in the rust tests -client_id = "0666d464-418a-4a08-ad53-6f15c78270cd" -encryption_secret = b"b4a4e6b7b811eda1dc1a2693ded" -version_id = "b0517957-f912-4d49-8330-f612e73030c4" - -def gen( - version_id=version_id, client_id=client_id, encryption_secret=encryption_secret, - app_id=1, version=1): - # first, generate the encryption key - salt = uuid.UUID(client_id).bytes - key = pbkdf2.PBKDF2( - encryption_secret, - salt, - digestmodule=hashlib.sha256, - iterations=600000, - ).read(32) - - # create a nonce - nonce = secrets.token_bytes(12) - - assert len(b"\x01") == 1 - # create the AAD - aad = b''.join([ - bytes([app_id]), - uuid.UUID(version_id).bytes, - ]) - - # encrypt using AEAD - chacha = ChaCha20Poly1305(key) - ciphertext = chacha.encrypt(nonce, b"SUCCESS", aad) - - # create the envelope - envelope = b''.join([ - bytes([version]), - nonce, - ciphertext, - ]) - - return envelope - - -def main(): - dir = sys.argv[1] - - with open(os.path.join(dir, 'test-good.data'), "wb") as f: - f.write(gen()) - - with open(os.path.join(dir, 'test-bad-version-id.data'), "wb") as f: - f.write(gen(version_id=uuid.uuid4().hex)) - - with open(os.path.join(dir, 'test-bad-client-id.data'), "wb") as f: - f.write(gen(client_id=uuid.uuid4().hex)) - - with open(os.path.join(dir, 'test-bad-secret.data'), "wb") as f: - f.write(gen(encryption_secret=b"xxxxxxxxxxxxxxxxxxxxx")) - - with open(os.path.join(dir, 'test-bad-version.data'), "wb") as f: - f.write(gen(version=99)) - - with open(os.path.join(dir, 'test-bad-app-id.data'), "wb") as f: - f.write(gen(app_id=99)) - - -main() diff --git a/taskchampion/taskchampion/src/server/local/mod.rs b/taskchampion/taskchampion/src/server/local/mod.rs deleted file mode 100644 index a5a4d2f98..000000000 --- a/taskchampion/taskchampion/src/server/local/mod.rs +++ /dev/null @@ -1,258 +0,0 @@ -use crate::errors::Result; -use crate::server::{ - AddVersionResult, GetVersionResult, HistorySegment, Server, Snapshot, SnapshotUrgency, - VersionId, NIL_VERSION_ID, -}; -use crate::storage::sqlite::StoredUuid; -use anyhow::Context; -use rusqlite::params; -use rusqlite::OptionalExtension; -use serde::{Deserialize, Serialize}; -use std::path::Path; -use uuid::Uuid; - -#[derive(Serialize, Deserialize, Debug)] -struct Version { - version_id: VersionId, - parent_version_id: VersionId, - history_segment: HistorySegment, -} - -pub struct LocalServer { - con: rusqlite::Connection, -} - -impl LocalServer { - fn txn(&mut self) -> Result { - let txn = self.con.transaction()?; - Ok(txn) - } - - /// A server which has no notion of clients, signatures, encryption, etc. - pub fn new>(directory: P) -> Result { - let db_file = directory - .as_ref() - .join("taskchampion-local-sync-server.sqlite3"); - let con = rusqlite::Connection::open(db_file)?; - - let queries = vec![ - "CREATE TABLE IF NOT EXISTS data (key STRING PRIMARY KEY, value STRING);", - "CREATE TABLE IF NOT EXISTS versions (version_id STRING PRIMARY KEY, parent_version_id STRING, data STRING);", - ]; - for q in queries { - con.execute(q, []).context("Creating table")?; - } - - Ok(LocalServer { con }) - } - - fn get_latest_version_id(&mut self) -> Result { - let t = self.txn()?; - let result: Option = t - .query_row( - "SELECT value FROM data WHERE key = 'latest_version_id' LIMIT 1", - rusqlite::params![], - |r| r.get(0), - ) - .optional()?; - Ok(result.map(|x| x.0).unwrap_or(NIL_VERSION_ID)) - } - - fn set_latest_version_id(&mut self, version_id: VersionId) -> Result<()> { - let t = self.txn()?; - t.execute( - "INSERT OR REPLACE INTO data (key, value) VALUES ('latest_version_id', ?)", - params![&StoredUuid(version_id)], - ) - .context("Update task query")?; - t.commit()?; - Ok(()) - } - - fn get_version_by_parent_version_id( - &mut self, - parent_version_id: VersionId, - ) -> Result> { - let t = self.txn()?; - let r = t.query_row( - "SELECT version_id, parent_version_id, data FROM versions WHERE parent_version_id = ?", - params![&StoredUuid(parent_version_id)], - |r| { - let version_id: StoredUuid = r.get("version_id")?; - let parent_version_id: StoredUuid = r.get("parent_version_id")?; - - Ok(Version{ - version_id: version_id.0, - parent_version_id: parent_version_id.0, - history_segment: r.get("data")?, - })} - ) - .optional() - .context("Get version query") - ?; - Ok(r) - } - - fn add_version_by_parent_version_id(&mut self, version: Version) -> Result<()> { - let t = self.txn()?; - t.execute( - "INSERT INTO versions (version_id, parent_version_id, data) VALUES (?, ?, ?)", - params![ - StoredUuid(version.version_id), - StoredUuid(version.parent_version_id), - version.history_segment - ], - )?; - t.commit()?; - Ok(()) - } -} - -impl Server for LocalServer { - // TODO: better transaction isolation for add_version (gets and sets should be in the same - // transaction) - - fn add_version( - &mut self, - parent_version_id: VersionId, - history_segment: HistorySegment, - ) -> Result<(AddVersionResult, SnapshotUrgency)> { - // no client lookup - // no signature validation - - // check the parent_version_id for linearity - let latest_version_id = self.get_latest_version_id()?; - if latest_version_id != NIL_VERSION_ID && parent_version_id != latest_version_id { - return Ok(( - AddVersionResult::ExpectedParentVersion(latest_version_id), - SnapshotUrgency::None, - )); - } - - // invent a new ID for this version - let version_id = Uuid::new_v4(); - - self.add_version_by_parent_version_id(Version { - version_id, - parent_version_id, - history_segment, - })?; - self.set_latest_version_id(version_id)?; - - Ok((AddVersionResult::Ok(version_id), SnapshotUrgency::None)) - } - - fn get_child_version(&mut self, parent_version_id: VersionId) -> Result { - if let Some(version) = self.get_version_by_parent_version_id(parent_version_id)? { - Ok(GetVersionResult::Version { - version_id: version.version_id, - parent_version_id: version.parent_version_id, - history_segment: version.history_segment, - }) - } else { - Ok(GetVersionResult::NoSuchVersion) - } - } - - fn add_snapshot(&mut self, _version_id: VersionId, _snapshot: Snapshot) -> Result<()> { - // the local server never requests a snapshot, so it should never get one - unreachable!() - } - - fn get_snapshot(&mut self) -> Result> { - Ok(None) - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - use tempfile::TempDir; - - #[test] - fn test_empty() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut server = LocalServer::new(tmp_dir.path())?; - let child_version = server.get_child_version(NIL_VERSION_ID)?; - assert_eq!(child_version, GetVersionResult::NoSuchVersion); - Ok(()) - } - - #[test] - fn test_add_zero_base() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut server = LocalServer::new(tmp_dir.path())?; - let history = b"1234".to_vec(); - match server.add_version(NIL_VERSION_ID, history.clone())?.0 { - AddVersionResult::ExpectedParentVersion(_) => { - panic!("should have accepted the version") - } - AddVersionResult::Ok(version_id) => { - let new_version = server.get_child_version(NIL_VERSION_ID)?; - assert_eq!( - new_version, - GetVersionResult::Version { - version_id, - parent_version_id: NIL_VERSION_ID, - history_segment: history, - } - ); - } - } - - Ok(()) - } - - #[test] - fn test_add_nonzero_base() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut server = LocalServer::new(tmp_dir.path())?; - let history = b"1234".to_vec(); - let parent_version_id = Uuid::new_v4() as VersionId; - - // This is OK because the server has no latest_version_id yet - match server.add_version(parent_version_id, history.clone())?.0 { - AddVersionResult::ExpectedParentVersion(_) => { - panic!("should have accepted the version") - } - AddVersionResult::Ok(version_id) => { - let new_version = server.get_child_version(parent_version_id)?; - assert_eq!( - new_version, - GetVersionResult::Version { - version_id, - parent_version_id, - history_segment: history, - } - ); - } - } - - Ok(()) - } - - #[test] - fn test_add_nonzero_base_forbidden() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut server = LocalServer::new(tmp_dir.path())?; - let history = b"1234".to_vec(); - let parent_version_id = Uuid::new_v4() as VersionId; - - // add a version - if let (AddVersionResult::ExpectedParentVersion(_), SnapshotUrgency::None) = - server.add_version(parent_version_id, history.clone())? - { - panic!("should have accepted the version") - } - - // then add another, not based on that one - if let (AddVersionResult::Ok(_), SnapshotUrgency::None) = - server.add_version(parent_version_id, history)? - { - panic!("should not have accepted the version") - } - - Ok(()) - } -} diff --git a/taskchampion/taskchampion/src/server/mod.rs b/taskchampion/taskchampion/src/server/mod.rs deleted file mode 100644 index caccdee02..000000000 --- a/taskchampion/taskchampion/src/server/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -/** - -This module defines the client interface to TaskChampion sync servers. -It defines a [trait](crate::server::Server) for servers, and implements both local and remote servers. - -Typical uses of this crate do not interact directly with this module; [`ServerConfig`](crate::ServerConfig) is sufficient. -However, users who wish to implement their own server interfaces can implement the traits defined here and pass the result to [`Replica`](crate::Replica). - -*/ - -#[cfg(test)] -pub(crate) mod test; - -mod config; -mod local; -mod op; -mod types; - -#[cfg(feature = "encryption")] -mod encryption; - -#[cfg(feature = "server-sync")] -mod sync; - -#[cfg(feature = "cloud")] -mod cloud; - -pub use config::ServerConfig; -pub use types::*; - -pub(crate) use op::SyncOp; diff --git a/taskchampion/taskchampion/src/server/op.rs b/taskchampion/taskchampion/src/server/op.rs deleted file mode 100644 index abd592d17..000000000 --- a/taskchampion/taskchampion/src/server/op.rs +++ /dev/null @@ -1,421 +0,0 @@ -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -/// A SyncOp defines a single change to the task database, that can be synchronized -/// via a server. -#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] -pub enum SyncOp { - /// Create a new task. - /// - /// On application, if the task already exists, the operation does nothing. - Create { uuid: Uuid }, - - /// Delete an existing task. - /// - /// On application, if the task does not exist, the operation does nothing. - Delete { uuid: Uuid }, - - /// Update an existing task, setting the given property to the given value. If the value is - /// None, then the corresponding property is deleted. - /// - /// If the given task does not exist, the operation does nothing. - Update { - uuid: Uuid, - property: String, - value: Option, - timestamp: DateTime, - }, -} - -use SyncOp::*; - -impl SyncOp { - // Transform takes two operations A and B that happened concurrently and produces two - // operations A' and B' such that `apply(apply(S, A), B') = apply(apply(S, B), A')`. This - // function is used to serialize operations in a process similar to a Git "rebase". - // - // * - // / \ - // op1 / \ op2 - // / \ - // * * - // - // this function "completes the diamond: - // - // * * - // \ / - // op2' \ / op1' - // \ / - // * - // - // such that applying op2' after op1 has the same effect as applying op1' after op2. This - // allows two different systems which have already applied op1 and op2, respectively, and thus - // reached different states, to return to the same state by applying op2' and op1', - // respectively. - pub fn transform(operation1: SyncOp, operation2: SyncOp) -> (Option, Option) { - match (&operation1, &operation2) { - // Two creations or deletions of the same uuid reach the same state, so there's no need - // for any further operations to bring the state together. - (&Create { uuid: uuid1 }, &Create { uuid: uuid2 }) if uuid1 == uuid2 => (None, None), - (&Delete { uuid: uuid1 }, &Delete { uuid: uuid2 }) if uuid1 == uuid2 => (None, None), - - // Given a create and a delete of the same task, one of the operations is invalid: the - // create implies the task does not exist, but the delete implies it exists. Somewhat - // arbitrarily, we prefer the Create - (&Create { uuid: uuid1 }, &Delete { uuid: uuid2 }) if uuid1 == uuid2 => { - (Some(operation1), None) - } - (&Delete { uuid: uuid1 }, &Create { uuid: uuid2 }) if uuid1 == uuid2 => { - (None, Some(operation2)) - } - - // And again from an Update and a Create, prefer the Update - (&Update { uuid: uuid1, .. }, &Create { uuid: uuid2 }) if uuid1 == uuid2 => { - (Some(operation1), None) - } - (&Create { uuid: uuid1 }, &Update { uuid: uuid2, .. }) if uuid1 == uuid2 => { - (None, Some(operation2)) - } - - // Given a delete and an update, prefer the delete - (&Update { uuid: uuid1, .. }, &Delete { uuid: uuid2 }) if uuid1 == uuid2 => { - (None, Some(operation2)) - } - (&Delete { uuid: uuid1 }, &Update { uuid: uuid2, .. }) if uuid1 == uuid2 => { - (Some(operation1), None) - } - - // Two updates to the same property of the same task might conflict. - ( - Update { - uuid: uuid1, - property: property1, - value: value1, - timestamp: timestamp1, - }, - Update { - uuid: uuid2, - property: property2, - value: value2, - timestamp: timestamp2, - }, - ) if uuid1 == uuid2 && property1 == property2 => { - // if the value is the same, there's no conflict - if value1 == value2 { - (None, None) - } else if timestamp1 < timestamp2 { - // prefer the later modification - (None, Some(operation2)) - } else { - // prefer the later modification or, if the modifications are the same, - // just choose one of them - (Some(operation1), None) - } - } - - // anything else is not a conflict of any sort, so return the operations unchanged - (_, _) => (Some(operation1), Some(operation2)), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::errors::Result; - use crate::storage::InMemoryStorage; - use crate::taskdb::TaskDb; - use chrono::{Duration, Utc}; - use pretty_assertions::assert_eq; - use proptest::prelude::*; - - #[test] - fn test_json_create() -> Result<()> { - let uuid = Uuid::new_v4(); - let op = Create { uuid }; - let json = serde_json::to_string(&op)?; - assert_eq!(json, format!(r#"{{"Create":{{"uuid":"{}"}}}}"#, uuid)); - let deser: SyncOp = serde_json::from_str(&json)?; - assert_eq!(deser, op); - Ok(()) - } - - #[test] - fn test_json_delete() -> Result<()> { - let uuid = Uuid::new_v4(); - let op = Delete { uuid }; - let json = serde_json::to_string(&op)?; - assert_eq!(json, format!(r#"{{"Delete":{{"uuid":"{}"}}}}"#, uuid)); - let deser: SyncOp = serde_json::from_str(&json)?; - assert_eq!(deser, op); - Ok(()) - } - - #[test] - fn test_json_update() -> Result<()> { - let uuid = Uuid::new_v4(); - let timestamp = Utc::now(); - - let op = Update { - uuid, - property: "abc".into(), - value: Some("false".into()), - timestamp, - }; - - let json = serde_json::to_string(&op)?; - assert_eq!( - json, - format!( - r#"{{"Update":{{"uuid":"{}","property":"abc","value":"false","timestamp":"{:?}"}}}}"#, - uuid, timestamp, - ) - ); - let deser: SyncOp = serde_json::from_str(&json)?; - assert_eq!(deser, op); - Ok(()) - } - - #[test] - fn test_json_update_none() -> Result<()> { - let uuid = Uuid::new_v4(); - let timestamp = Utc::now(); - - let op = Update { - uuid, - property: "abc".into(), - value: None, - timestamp, - }; - - let json = serde_json::to_string(&op)?; - assert_eq!( - json, - format!( - r#"{{"Update":{{"uuid":"{}","property":"abc","value":null,"timestamp":"{:?}"}}}}"#, - uuid, timestamp, - ) - ); - let deser: SyncOp = serde_json::from_str(&json)?; - assert_eq!(deser, op); - Ok(()) - } - - fn test_transform( - setup: Option, - o1: SyncOp, - o2: SyncOp, - exp1p: Option, - exp2p: Option, - ) { - let (o1p, o2p) = SyncOp::transform(o1.clone(), o2.clone()); - assert_eq!((&o1p, &o2p), (&exp1p, &exp2p)); - - // check that the two operation sequences have the same effect, enforcing the invariant of - // the transform function. - let mut db1 = TaskDb::new_inmemory(); - if let Some(ref o) = setup { - db1.apply(o.clone()).unwrap(); - } - db1.apply(o1).unwrap(); - if let Some(o) = o2p { - db1.apply(o).unwrap(); - } - - let mut db2 = TaskDb::new_inmemory(); - if let Some(ref o) = setup { - db2.apply(o.clone()).unwrap(); - } - db2.apply(o2).unwrap(); - if let Some(o) = o1p { - db2.apply(o).unwrap(); - } - - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); - } - - #[test] - fn test_unrelated_create() { - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - - test_transform( - None, - Create { uuid: uuid1 }, - Create { uuid: uuid2 }, - Some(Create { uuid: uuid1 }), - Some(Create { uuid: uuid2 }), - ); - } - - #[test] - fn test_related_updates_different_props() { - let uuid = Uuid::new_v4(); - let timestamp = Utc::now(); - - test_transform( - Some(Create { uuid }), - Update { - uuid, - property: "abc".into(), - value: Some("true".into()), - timestamp, - }, - Update { - uuid, - property: "def".into(), - value: Some("false".into()), - timestamp, - }, - Some(Update { - uuid, - property: "abc".into(), - value: Some("true".into()), - timestamp, - }), - Some(Update { - uuid, - property: "def".into(), - value: Some("false".into()), - timestamp, - }), - ); - } - - #[test] - fn test_related_updates_same_prop() { - let uuid = Uuid::new_v4(); - let timestamp1 = Utc::now(); - let timestamp2 = timestamp1 + Duration::seconds(10); - - test_transform( - Some(Create { uuid }), - Update { - uuid, - property: "abc".into(), - value: Some("true".into()), - timestamp: timestamp1, - }, - Update { - uuid, - property: "abc".into(), - value: Some("false".into()), - timestamp: timestamp2, - }, - None, - Some(Update { - uuid, - property: "abc".into(), - value: Some("false".into()), - timestamp: timestamp2, - }), - ); - } - - #[test] - fn test_related_updates_same_prop_same_time() { - let uuid = Uuid::new_v4(); - let timestamp = Utc::now(); - - test_transform( - Some(Create { uuid }), - Update { - uuid, - property: "abc".into(), - value: Some("true".into()), - timestamp, - }, - Update { - uuid, - property: "abc".into(), - value: Some("false".into()), - timestamp, - }, - Some(Update { - uuid, - property: "abc".into(), - value: Some("true".into()), - timestamp, - }), - None, - ); - } - - fn uuid_strategy() -> impl Strategy { - prop_oneof![ - Just(Uuid::parse_str("83a2f9ef-f455-4195-b92e-a54c161eebfc").unwrap()), - Just(Uuid::parse_str("56e0be07-c61f-494c-a54c-bdcfdd52d2a7").unwrap()), - Just(Uuid::parse_str("4b7ed904-f7b0-4293-8a10-ad452422c7b3").unwrap()), - Just(Uuid::parse_str("9bdd0546-07c8-4e1f-a9bc-9d6299f4773b").unwrap()), - ] - } - - fn operation_strategy() -> impl Strategy { - prop_oneof![ - uuid_strategy().prop_map(|uuid| Create { uuid }), - uuid_strategy().prop_map(|uuid| Delete { uuid }), - (uuid_strategy(), "(title|project|status)").prop_map(|(uuid, property)| { - Update { - uuid, - property, - value: Some("true".into()), - timestamp: Utc::now(), - } - }), - ] - } - - proptest! { - #![proptest_config(ProptestConfig { - cases: 1024, .. ProptestConfig::default() - })] - #[test] - // check that the two operation sequences have the same effect, enforcing the invariant of - // the transform function. - fn transform_invariant_holds(o1 in operation_strategy(), o2 in operation_strategy()) { - let (o1p, o2p) = SyncOp::transform(o1.clone(), o2.clone()); - - let mut db1 = TaskDb::new(Box::new(InMemoryStorage::new())); - let mut db2 = TaskDb::new(Box::new(InMemoryStorage::new())); - - // Ensure that any expected tasks already exist - if let Update{ uuid, .. } = o1 { - let _ = db1.apply(Create{uuid}); - let _ = db2.apply(Create{uuid}); - } - - if let Update{ uuid, .. } = o2 { - let _ = db1.apply(Create{uuid}); - let _ = db2.apply(Create{uuid}); - } - - if let Delete{ uuid } = o1 { - let _ = db1.apply(Create{uuid}); - let _ = db2.apply(Create{uuid}); - } - - if let Delete{ uuid } = o2 { - let _ = db1.apply(Create{uuid}); - let _ = db2.apply(Create{uuid}); - } - - // if applying the initial operations fail, that indicates the operation was invalid - // in the base state, so consider the case successful. - if db1.apply(o1).is_err() { - return Ok(()); - } - if db2.apply(o2).is_err() { - return Ok(()); - } - - if let Some(o) = o2p { - db1.apply(o).map_err(|e| TestCaseError::Fail(format!("Applying to db1: {}", e).into()))?; - } - if let Some(o) = o1p { - db2.apply(o).map_err(|e| TestCaseError::Fail(format!("Applying to db2: {}", e).into()))?; - } - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); - } - } -} diff --git a/taskchampion/taskchampion/src/server/sync/mod.rs b/taskchampion/taskchampion/src/server/sync/mod.rs deleted file mode 100644 index 3ae86aa23..000000000 --- a/taskchampion/taskchampion/src/server/sync/mod.rs +++ /dev/null @@ -1,192 +0,0 @@ -use crate::errors::{Error, Result}; -use crate::server::{ - AddVersionResult, GetVersionResult, HistorySegment, Server, Snapshot, SnapshotUrgency, - VersionId, -}; -use std::time::Duration; -use url::Url; -use uuid::Uuid; - -use super::encryption::{Cryptor, Sealed, Secret, Unsealed}; - -pub struct SyncServer { - origin: String, - client_id: Uuid, - cryptor: Cryptor, - agent: ureq::Agent, -} - -/// The content-type for history segments (opaque blobs of bytes) -const HISTORY_SEGMENT_CONTENT_TYPE: &str = "application/vnd.taskchampion.history-segment"; - -/// The content-type for snapshots (opaque blobs of bytes) -const SNAPSHOT_CONTENT_TYPE: &str = "application/vnd.taskchampion.snapshot"; - -/// A SyncServer communicates with a sync server over HTTP. -impl SyncServer { - /// Construct a new SyncServer. The `origin` is the sync server's protocol and hostname - /// without a trailing slash, such as `https://tcsync.example.com`. Pass a client_id to - /// identify this client to the server. Multiple replicas synchronizing the same task history - /// should use the same client_id. - pub fn new(origin: String, client_id: Uuid, encryption_secret: Vec) -> Result { - let origin = Url::parse(&origin) - .map_err(|_| Error::Server(format!("Could not parse {} as a URL", origin)))?; - if origin.path() != "/" { - return Err(Error::Server(format!( - "Server origin must have an empty path; got {}", - origin - ))); - } - Ok(SyncServer { - origin: origin.to_string(), - client_id, - cryptor: Cryptor::new(client_id, &Secret(encryption_secret.to_vec()))?, - agent: ureq::AgentBuilder::new() - .timeout_connect(Duration::from_secs(10)) - .timeout_read(Duration::from_secs(60)) - .build(), - }) - } -} - -/// Read a UUID-bearing header or fail trying -fn get_uuid_header(resp: &ureq::Response, name: &str) -> Result { - let value = resp - .header(name) - .ok_or_else(|| anyhow::anyhow!("Response does not have {} header", name))?; - let value = Uuid::parse_str(value) - .map_err(|e| anyhow::anyhow!("{} header is not a valid UUID: {}", name, e))?; - Ok(value) -} - -/// Read the X-Snapshot-Request header and return a SnapshotUrgency -fn get_snapshot_urgency(resp: &ureq::Response) -> SnapshotUrgency { - match resp.header("X-Snapshot-Request") { - None => SnapshotUrgency::None, - Some(hdr) => match hdr { - "urgency=low" => SnapshotUrgency::Low, - "urgency=high" => SnapshotUrgency::High, - _ => SnapshotUrgency::None, - }, - } -} - -fn sealed_from_resp(resp: ureq::Response, version_id: Uuid, content_type: &str) -> Result { - use std::io::Read; - if resp.header("Content-Type") == Some(content_type) { - let mut reader = resp.into_reader(); - let mut payload = vec![]; - reader.read_to_end(&mut payload)?; - Ok(Sealed { - version_id, - payload, - }) - } else { - Err(Error::Server(String::from( - "Response did not have expected content-type", - ))) - } -} - -impl Server for SyncServer { - fn add_version( - &mut self, - parent_version_id: VersionId, - history_segment: HistorySegment, - ) -> Result<(AddVersionResult, SnapshotUrgency)> { - let url = format!("{}v1/client/add-version/{}", self.origin, parent_version_id); - let unsealed = Unsealed { - version_id: parent_version_id, - payload: history_segment, - }; - let sealed = self.cryptor.seal(unsealed)?; - match self - .agent - .post(&url) - .set("Content-Type", HISTORY_SEGMENT_CONTENT_TYPE) - .set("X-Client-Id", &self.client_id.to_string()) - .send_bytes(sealed.as_ref()) - { - Ok(resp) => { - let version_id = get_uuid_header(&resp, "X-Version-Id")?; - Ok(( - AddVersionResult::Ok(version_id), - get_snapshot_urgency(&resp), - )) - } - Err(ureq::Error::Status(status, resp)) if status == 409 => { - let parent_version_id = get_uuid_header(&resp, "X-Parent-Version-Id")?; - Ok(( - AddVersionResult::ExpectedParentVersion(parent_version_id), - SnapshotUrgency::None, - )) - } - Err(err) => Err(err.into()), - } - } - - fn get_child_version(&mut self, parent_version_id: VersionId) -> Result { - let url = format!( - "{}v1/client/get-child-version/{}", - self.origin, parent_version_id - ); - match self - .agent - .get(&url) - .set("X-Client-Id", &self.client_id.to_string()) - .call() - { - Ok(resp) => { - let parent_version_id = get_uuid_header(&resp, "X-Parent-Version-Id")?; - let version_id = get_uuid_header(&resp, "X-Version-Id")?; - let sealed = - sealed_from_resp(resp, parent_version_id, HISTORY_SEGMENT_CONTENT_TYPE)?; - let history_segment = self.cryptor.unseal(sealed)?.payload; - Ok(GetVersionResult::Version { - version_id, - parent_version_id, - history_segment, - }) - } - Err(ureq::Error::Status(status, _)) if status == 404 => { - Ok(GetVersionResult::NoSuchVersion) - } - Err(err) => Err(err.into()), - } - } - - fn add_snapshot(&mut self, version_id: VersionId, snapshot: Snapshot) -> Result<()> { - let url = format!("{}v1/client/add-snapshot/{}", self.origin, version_id); - let unsealed = Unsealed { - version_id, - payload: snapshot, - }; - let sealed = self.cryptor.seal(unsealed)?; - Ok(self - .agent - .post(&url) - .set("Content-Type", SNAPSHOT_CONTENT_TYPE) - .set("X-Client-Id", &self.client_id.to_string()) - .send_bytes(sealed.as_ref()) - .map(|_| ())?) - } - - fn get_snapshot(&mut self) -> Result> { - let url = format!("{}v1/client/snapshot", self.origin); - match self - .agent - .get(&url) - .set("X-Client-Id", &self.client_id.to_string()) - .call() - { - Ok(resp) => { - let version_id = get_uuid_header(&resp, "X-Version-Id")?; - let sealed = sealed_from_resp(resp, version_id, SNAPSHOT_CONTENT_TYPE)?; - let snapshot = self.cryptor.unseal(sealed)?.payload; - Ok(Some((version_id, snapshot))) - } - Err(ureq::Error::Status(status, _)) if status == 404 => Ok(None), - Err(err) => Err(err.into()), - } - } -} diff --git a/taskchampion/taskchampion/src/server/test-bad-app-id.data b/taskchampion/taskchampion/src/server/test-bad-app-id.data deleted file mode 100644 index e0dbb5f69..000000000 --- a/taskchampion/taskchampion/src/server/test-bad-app-id.data +++ /dev/null @@ -1,2 +0,0 @@ -INu -;Ɛ^9I걏ĉib \ No newline at end of file diff --git a/taskchampion/taskchampion/src/server/test-bad-client-id.data b/taskchampion/taskchampion/src/server/test-bad-client-id.data deleted file mode 100644 index 10885b533..000000000 --- a/taskchampion/taskchampion/src/server/test-bad-client-id.data +++ /dev/null @@ -1 +0,0 @@ -hX.EI / B  \ No newline at end of file diff --git a/taskchampion/taskchampion/src/server/test-bad-secret.data b/taskchampion/taskchampion/src/server/test-bad-secret.data deleted file mode 100644 index 435585345..000000000 --- a/taskchampion/taskchampion/src/server/test-bad-secret.data +++ /dev/null @@ -1 +0,0 @@ -Y|z JuѾKzF]bCx \ No newline at end of file diff --git a/taskchampion/taskchampion/src/server/test-bad-version-id.data b/taskchampion/taskchampion/src/server/test-bad-version-id.data deleted file mode 100644 index 3b14c54db..000000000 --- a/taskchampion/taskchampion/src/server/test-bad-version-id.data +++ /dev/null @@ -1 +0,0 @@ -SsL|:=";ў5 JK= \ No newline at end of file diff --git a/taskchampion/taskchampion/src/server/test-bad-version.data b/taskchampion/taskchampion/src/server/test-bad-version.data deleted file mode 100644 index 7a1380c57..000000000 --- a/taskchampion/taskchampion/src/server/test-bad-version.data +++ /dev/null @@ -1 +0,0 @@ -czy326LzA};B6@ \ No newline at end of file diff --git a/taskchampion/taskchampion/src/server/test-bad-version_id.data b/taskchampion/taskchampion/src/server/test-bad-version_id.data deleted file mode 100644 index 4a228ec4a..000000000 --- a/taskchampion/taskchampion/src/server/test-bad-version_id.data +++ /dev/null @@ -1,2 +0,0 @@ -B --3%j,*ߺ7꩖QKOFPZ \ No newline at end of file diff --git a/taskchampion/taskchampion/src/server/test-good.data b/taskchampion/taskchampion/src/server/test-good.data deleted file mode 100644 index afe7678ba..000000000 --- a/taskchampion/taskchampion/src/server/test-good.data +++ /dev/null @@ -1 +0,0 @@ - t`&_)Ӊgr}-Zs \ No newline at end of file diff --git a/taskchampion/taskchampion/src/server/test.rs b/taskchampion/taskchampion/src/server/test.rs deleted file mode 100644 index 165fa557b..000000000 --- a/taskchampion/taskchampion/src/server/test.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::errors::Result; -use crate::server::{ - AddVersionResult, GetVersionResult, HistorySegment, Server, Snapshot, SnapshotUrgency, - VersionId, NIL_VERSION_ID, -}; -use std::collections::HashMap; -use std::sync::{Arc, Mutex}; -use uuid::Uuid; - -struct Version { - version_id: VersionId, - parent_version_id: VersionId, - history_segment: HistorySegment, -} - -/// TestServer implements the Server trait with a test implementation. -#[derive(Clone)] -pub(crate) struct TestServer(Arc>); - -pub(crate) struct Inner { - latest_version_id: VersionId, - // NOTE: indexed by parent_version_id! - versions: HashMap, - snapshot_urgency: SnapshotUrgency, - snapshot: Option<(VersionId, Snapshot)>, -} - -impl TestServer { - /// A test server has no notion of clients, signatures, encryption, etc. - pub(crate) fn new() -> TestServer { - TestServer(Arc::new(Mutex::new(Inner { - latest_version_id: NIL_VERSION_ID, - versions: HashMap::new(), - snapshot_urgency: SnapshotUrgency::None, - snapshot: None, - }))) - } - // feel free to add any test utility functions here - - /// Get a boxed Server implementation referring to this TestServer - pub(crate) fn server(&self) -> Box { - Box::new(self.clone()) - } - - pub(crate) fn set_snapshot_urgency(&self, urgency: SnapshotUrgency) { - let mut inner = self.0.lock().unwrap(); - inner.snapshot_urgency = urgency; - } - - /// Get the latest snapshot added to this server - pub(crate) fn snapshot(&self) -> Option<(VersionId, Snapshot)> { - let inner = self.0.lock().unwrap(); - inner.snapshot.as_ref().cloned() - } - - /// Delete a version from storage - pub(crate) fn delete_version(&mut self, parent_version_id: VersionId) { - let mut inner = self.0.lock().unwrap(); - inner.versions.remove(&parent_version_id); - } -} - -impl Server for TestServer { - /// Add a new version. If the given version number is incorrect, this responds with the - /// appropriate version and expects the caller to try again. - fn add_version( - &mut self, - parent_version_id: VersionId, - history_segment: HistorySegment, - ) -> Result<(AddVersionResult, SnapshotUrgency)> { - let mut inner = self.0.lock().unwrap(); - - // no client lookup - // no signature validation - - // check the parent_version_id for linearity - if inner.latest_version_id != NIL_VERSION_ID && parent_version_id != inner.latest_version_id - { - return Ok(( - AddVersionResult::ExpectedParentVersion(inner.latest_version_id), - SnapshotUrgency::None, - )); - } - - // invent a new ID for this version - let version_id = Uuid::new_v4(); - - inner.versions.insert( - parent_version_id, - Version { - version_id, - parent_version_id, - history_segment, - }, - ); - inner.latest_version_id = version_id; - - // reply with the configured urgency and reset it to None - let urgency = inner.snapshot_urgency; - inner.snapshot_urgency = SnapshotUrgency::None; - Ok((AddVersionResult::Ok(version_id), urgency)) - } - - /// Get a vector of all versions after `since_version` - fn get_child_version(&mut self, parent_version_id: VersionId) -> Result { - let inner = self.0.lock().unwrap(); - - if let Some(version) = inner.versions.get(&parent_version_id) { - Ok(GetVersionResult::Version { - version_id: version.version_id, - parent_version_id: version.parent_version_id, - history_segment: version.history_segment.clone(), - }) - } else { - Ok(GetVersionResult::NoSuchVersion) - } - } - - fn add_snapshot(&mut self, version_id: VersionId, snapshot: Snapshot) -> Result<()> { - let mut inner = self.0.lock().unwrap(); - - // test implementation -- does not perform any validation - inner.snapshot = Some((version_id, snapshot)); - Ok(()) - } - - fn get_snapshot(&mut self) -> Result> { - let inner = self.0.lock().unwrap(); - Ok(inner.snapshot.clone()) - } -} diff --git a/taskchampion/taskchampion/src/server/types.rs b/taskchampion/taskchampion/src/server/types.rs deleted file mode 100644 index 8929ae23b..000000000 --- a/taskchampion/taskchampion/src/server/types.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::errors::Result; -use uuid::Uuid; - -/// Versions are referred to with UUIDs. -pub type VersionId = Uuid; - -/// The distinguished value for "no version" -pub const NIL_VERSION_ID: VersionId = Uuid::nil(); - -/// A segment in the history of this task database, in the form of a sequence of operations. This -/// data is pre-encoded, and from the protocol level appears as a sequence of bytes. -pub type HistorySegment = Vec; - -/// A snapshot of the state of the task database. This is encoded by the taskdb implementation -/// and treated as a sequence of bytes by the server implementation. -pub type Snapshot = Vec; - -/// AddVersionResult is the response type from [`crate::server::Server::add_version`]. -#[derive(Debug, PartialEq, Eq)] -pub enum AddVersionResult { - /// OK, version added with the given ID - Ok(VersionId), - /// Rejected; expected a version with the given parent version - ExpectedParentVersion(VersionId), -} - -/// SnapshotUrgency indicates how much the server would like this replica to send a snapshot. -#[derive(PartialEq, Debug, Clone, Copy, Eq, PartialOrd, Ord)] -pub enum SnapshotUrgency { - /// Don't need a snapshot right now. - None, - /// A snapshot would be good, but can wait for other replicas to provide it. - Low, - /// A snapshot is needed right now. - High, -} - -/// A version as downloaded from the server -#[derive(Debug, PartialEq, Eq)] -pub enum GetVersionResult { - /// No such version exists - NoSuchVersion, - - /// The requested version - Version { - version_id: VersionId, - parent_version_id: VersionId, - history_segment: HistorySegment, - }, -} - -/// A value implementing this trait can act as a server against which a replica can sync. -pub trait Server { - /// Add a new version. - /// - /// This must ensure that the new version is the only version with the given - /// `parent_version_id`, and that all versions form a single parent-child chain. Inductively, - /// this means that if there are any versions on the server, then `parent_version_id` must be - /// the only version that does not already have a child. - fn add_version( - &mut self, - parent_version_id: VersionId, - history_segment: HistorySegment, - ) -> Result<(AddVersionResult, SnapshotUrgency)>; - - /// Get the version with the given parent VersionId - fn get_child_version(&mut self, parent_version_id: VersionId) -> Result; - - /// Add a snapshot on the server - fn add_snapshot(&mut self, version_id: VersionId, snapshot: Snapshot) -> Result<()>; - - fn get_snapshot(&mut self) -> Result>; -} diff --git a/taskchampion/taskchampion/src/storage/config.rs b/taskchampion/taskchampion/src/storage/config.rs deleted file mode 100644 index cabc6de54..000000000 --- a/taskchampion/taskchampion/src/storage/config.rs +++ /dev/null @@ -1,29 +0,0 @@ -use super::{InMemoryStorage, SqliteStorage, Storage}; -use crate::errors::Result; -use std::path::PathBuf; - -/// The configuration required for a replica's storage. -pub enum StorageConfig { - /// Store the data on disk. This is the common choice. - OnDisk { - /// Path containing the task DB. - taskdb_dir: PathBuf, - - /// Create the DB if it does not already exist - create_if_missing: bool, - }, - /// Store the data in memory. This is only useful for testing. - InMemory, -} - -impl StorageConfig { - pub fn into_storage(self) -> Result> { - Ok(match self { - StorageConfig::OnDisk { - taskdb_dir, - create_if_missing, - } => Box::new(SqliteStorage::new(taskdb_dir, create_if_missing)?), - StorageConfig::InMemory => Box::new(InMemoryStorage::new()), - }) - } -} diff --git a/taskchampion/taskchampion/src/storage/inmemory.rs b/taskchampion/taskchampion/src/storage/inmemory.rs deleted file mode 100644 index 6bf771a93..000000000 --- a/taskchampion/taskchampion/src/storage/inmemory.rs +++ /dev/null @@ -1,246 +0,0 @@ -#![allow(clippy::new_without_default)] - -use crate::errors::{Error, Result}; -use crate::storage::{ReplicaOp, Storage, StorageTxn, TaskMap, VersionId, DEFAULT_BASE_VERSION}; -use std::collections::hash_map::Entry; -use std::collections::HashMap; -use uuid::Uuid; - -#[derive(PartialEq, Debug, Clone)] -struct Data { - tasks: HashMap, - base_version: VersionId, - operations: Vec, - working_set: Vec>, -} - -struct Txn<'t> { - storage: &'t mut InMemoryStorage, - new_data: Option, -} - -impl<'t> Txn<'t> { - fn mut_data_ref(&mut self) -> &mut Data { - if self.new_data.is_none() { - self.new_data = Some(self.storage.data.clone()); - } - if let Some(ref mut data) = self.new_data { - data - } else { - unreachable!(); - } - } - - fn data_ref(&mut self) -> &Data { - if let Some(ref data) = self.new_data { - data - } else { - &self.storage.data - } - } -} - -impl<'t> StorageTxn for Txn<'t> { - fn get_task(&mut self, uuid: Uuid) -> Result> { - match self.data_ref().tasks.get(&uuid) { - None => Ok(None), - Some(t) => Ok(Some(t.clone())), - } - } - - fn create_task(&mut self, uuid: Uuid) -> Result { - if let ent @ Entry::Vacant(_) = self.mut_data_ref().tasks.entry(uuid) { - ent.or_insert_with(TaskMap::new); - Ok(true) - } else { - Ok(false) - } - } - - fn set_task(&mut self, uuid: Uuid, task: TaskMap) -> Result<()> { - self.mut_data_ref().tasks.insert(uuid, task); - Ok(()) - } - - fn delete_task(&mut self, uuid: Uuid) -> Result { - Ok(self.mut_data_ref().tasks.remove(&uuid).is_some()) - } - - fn all_tasks<'a>(&mut self) -> Result> { - Ok(self - .data_ref() - .tasks - .iter() - .map(|(u, t)| (*u, t.clone())) - .collect()) - } - - fn all_task_uuids<'a>(&mut self) -> Result> { - Ok(self.data_ref().tasks.keys().copied().collect()) - } - - fn base_version(&mut self) -> Result { - Ok(self.data_ref().base_version) - } - - fn set_base_version(&mut self, version: VersionId) -> Result<()> { - self.mut_data_ref().base_version = version; - Ok(()) - } - - fn operations(&mut self) -> Result> { - Ok(self.data_ref().operations.clone()) - } - - fn num_operations(&mut self) -> Result { - Ok(self.data_ref().operations.len()) - } - - fn add_operation(&mut self, op: ReplicaOp) -> Result<()> { - self.mut_data_ref().operations.push(op); - Ok(()) - } - - fn set_operations(&mut self, ops: Vec) -> Result<()> { - self.mut_data_ref().operations = ops; - Ok(()) - } - - fn get_working_set(&mut self) -> Result>> { - Ok(self.data_ref().working_set.clone()) - } - - fn add_to_working_set(&mut self, uuid: Uuid) -> Result { - let working_set = &mut self.mut_data_ref().working_set; - working_set.push(Some(uuid)); - Ok(working_set.len()) - } - - fn set_working_set_item(&mut self, index: usize, uuid: Option) -> Result<()> { - let working_set = &mut self.mut_data_ref().working_set; - if index >= working_set.len() { - return Err(Error::Database(format!( - "Index {} is not in the working set", - index - ))); - } - working_set[index] = uuid; - Ok(()) - } - - fn clear_working_set(&mut self) -> Result<()> { - self.mut_data_ref().working_set = vec![None]; - Ok(()) - } - - fn commit(&mut self) -> Result<()> { - // copy the new_data back into storage to commit the transaction - if let Some(data) = self.new_data.take() { - self.storage.data = data; - } - Ok(()) - } -} - -/// InMemoryStorage is a simple in-memory task storage implementation. It is not useful for -/// production data, but is useful for testing purposes. -#[derive(PartialEq, Debug, Clone)] -pub struct InMemoryStorage { - data: Data, -} - -impl InMemoryStorage { - pub fn new() -> InMemoryStorage { - InMemoryStorage { - data: Data { - tasks: HashMap::new(), - base_version: DEFAULT_BASE_VERSION, - operations: vec![], - working_set: vec![None], - }, - } - } -} - -impl Storage for InMemoryStorage { - fn txn<'a>(&'a mut self) -> Result> { - Ok(Box::new(Txn { - storage: self, - new_data: None, - })) - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - // (note: this module is heavily used in tests so most of its functionality is well-tested - // elsewhere and not tested here) - - #[test] - fn get_working_set_empty() -> Result<()> { - let mut storage = InMemoryStorage::new(); - - { - let mut txn = storage.txn()?; - let ws = txn.get_working_set()?; - assert_eq!(ws, vec![None]); - } - - Ok(()) - } - - #[test] - fn add_to_working_set() -> Result<()> { - let mut storage = InMemoryStorage::new(); - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - - { - let mut txn = storage.txn()?; - txn.add_to_working_set(uuid1)?; - txn.add_to_working_set(uuid2)?; - txn.commit()?; - } - - { - let mut txn = storage.txn()?; - let ws = txn.get_working_set()?; - assert_eq!(ws, vec![None, Some(uuid1), Some(uuid2)]); - } - - Ok(()) - } - - #[test] - fn clear_working_set() -> Result<()> { - let mut storage = InMemoryStorage::new(); - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - - { - let mut txn = storage.txn()?; - txn.add_to_working_set(uuid1)?; - txn.add_to_working_set(uuid2)?; - txn.commit()?; - } - - { - let mut txn = storage.txn()?; - txn.clear_working_set()?; - txn.add_to_working_set(uuid2)?; - txn.add_to_working_set(uuid1)?; - txn.commit()?; - } - - { - let mut txn = storage.txn()?; - let ws = txn.get_working_set()?; - assert_eq!(ws, vec![None, Some(uuid2), Some(uuid1)]); - } - - Ok(()) - } -} diff --git a/taskchampion/taskchampion/src/storage/mod.rs b/taskchampion/taskchampion/src/storage/mod.rs deleted file mode 100644 index 5674e1baf..000000000 --- a/taskchampion/taskchampion/src/storage/mod.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::errors::Result; -/** -This module defines the backend storage used by [`Replica`](crate::Replica). -It defines a [trait](crate::storage::Storage) for storage implementations, and provides a default on-disk implementation as well as an in-memory implementation for testing. - -Typical uses of this crate do not interact directly with this module; [`StorageConfig`](crate::StorageConfig) is sufficient. -However, users who wish to implement their own storage backends can implement the traits defined here and pass the result to [`Replica`](crate::Replica). -*/ -use std::collections::HashMap; -use uuid::Uuid; - -mod config; -mod inmemory; -mod op; -pub(crate) mod sqlite; - -pub use config::StorageConfig; -pub use inmemory::InMemoryStorage; -pub use sqlite::SqliteStorage; - -pub use op::ReplicaOp; - -/// An in-memory representation of a task as a simple hashmap -pub type TaskMap = HashMap; - -#[cfg(test)] -fn taskmap_with(mut properties: Vec<(String, String)>) -> TaskMap { - let mut rv = TaskMap::new(); - for (p, v) in properties.drain(..) { - rv.insert(p, v); - } - rv -} - -/// The type of VersionIds -pub use crate::server::VersionId; - -/// The default for base_version. -pub(crate) const DEFAULT_BASE_VERSION: Uuid = crate::server::NIL_VERSION_ID; - -/// A Storage transaction, in which storage operations are performed. -/// -/// # Concurrency -/// -/// Serializable consistency must be maintained. Concurrent access is unusual -/// and some implementations may simply apply a mutex to limit access to -/// one transaction at a time. -/// -/// # Commiting and Aborting -/// -/// A transaction is not visible to other readers until it is committed with -/// [`crate::storage::StorageTxn::commit`]. Transactions are aborted if they are dropped. -/// It is safe and performant to drop transactions that did not modify any data without committing. -pub trait StorageTxn { - /// Get an (immutable) task, if it is in the storage - fn get_task(&mut self, uuid: Uuid) -> Result>; - - /// Create an (empty) task, only if it does not already exist. Returns true if - /// the task was created (did not already exist). - fn create_task(&mut self, uuid: Uuid) -> Result; - - /// Set a task, overwriting any existing task. If the task does not exist, this implicitly - /// creates it (use `get_task` to check first, if necessary). - fn set_task(&mut self, uuid: Uuid, task: TaskMap) -> Result<()>; - - /// Delete a task, if it exists. Returns true if the task was deleted (already existed) - fn delete_task(&mut self, uuid: Uuid) -> Result; - - /// Get the uuids and bodies of all tasks in the storage, in undefined order. - fn all_tasks(&mut self) -> Result>; - - /// Get the uuids of all tasks in the storage, in undefined order. - fn all_task_uuids(&mut self) -> Result>; - - /// Get the current base_version for this storage -- the last version synced from the server. - fn base_version(&mut self) -> Result; - - /// Set the current base_version for this storage. - fn set_base_version(&mut self, version: VersionId) -> Result<()>; - - /// Get the current set of outstanding operations (operations that have not been sync'd to the - /// server yet) - fn operations(&mut self) -> Result>; - - /// Get the current set of outstanding operations (operations that have not been sync'd to the - /// server yet) - fn num_operations(&mut self) -> Result; - - /// Add an operation to the end of the list of operations in the storage. Note that this - /// merely *stores* the operation; it is up to the TaskDb to apply it. - fn add_operation(&mut self, op: ReplicaOp) -> Result<()>; - - /// Replace the current list of operations with a new list. - fn set_operations(&mut self, ops: Vec) -> Result<()>; - - /// Get the entire working set, with each task UUID at its appropriate (1-based) index. - /// Element 0 is always None. - fn get_working_set(&mut self) -> Result>>; - - /// Add a task to the working set and return its (one-based) index. This index will be one greater - /// than the highest used index. - fn add_to_working_set(&mut self, uuid: Uuid) -> Result; - - /// Update the working set task at the given index. This cannot add a new item to the - /// working set. - fn set_working_set_item(&mut self, index: usize, uuid: Option) -> Result<()>; - - /// Clear all tasks from the working set in preparation for a garbage-collection operation. - /// Note that this is the only way items are removed from the set. - fn clear_working_set(&mut self) -> Result<()>; - - /// Check whether this storage is entirely empty - #[allow(clippy::wrong_self_convention)] // mut is required here for storage access - fn is_empty(&mut self) -> Result { - let mut empty = true; - empty = empty && self.all_tasks()?.is_empty(); - empty = empty && self.get_working_set()? == vec![None]; - empty = empty && self.base_version()? == Uuid::nil(); - empty = empty && self.operations()?.is_empty(); - Ok(empty) - } - - /// Commit any changes made in the transaction. It is an error to call this more than - /// once. - fn commit(&mut self) -> Result<()>; -} - -/// A trait for objects able to act as task storage. Most of the interesting behavior is in the -/// [`crate::storage::StorageTxn`] trait. -pub trait Storage { - /// Begin a transaction - fn txn<'a>(&'a mut self) -> Result>; -} diff --git a/taskchampion/taskchampion/src/storage/op.rs b/taskchampion/taskchampion/src/storage/op.rs deleted file mode 100644 index c63e9efa6..000000000 --- a/taskchampion/taskchampion/src/storage/op.rs +++ /dev/null @@ -1,289 +0,0 @@ -use crate::server::SyncOp; -use crate::storage::TaskMap; -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -/// A ReplicaOp defines a single change to the task database, as stored locally in the replica. -/// This contains additional information not included in SyncOp. -#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] -pub enum ReplicaOp { - /// Create a new task. - /// - /// On undo, the task is deleted. - Create { uuid: Uuid }, - - /// Delete an existing task. - /// - /// On undo, the task's data is restored from old_task. - Delete { uuid: Uuid, old_task: TaskMap }, - - /// Update an existing task, setting the given property to the given value. If the value is - /// None, then the corresponding property is deleted. - /// - /// On undo, the property is set back to its previous value. - Update { - uuid: Uuid, - property: String, - old_value: Option, - value: Option, - timestamp: DateTime, - }, - - /// Mark a point in the operations history to which the user might like to undo. Users - /// typically want to undo more than one operation at a time (for example, most changes update - /// both the `modified` property and some other task property -- the user would like to "undo" - /// both updates at the same time). Applying an UndoPoint does nothing. - UndoPoint, -} - -impl ReplicaOp { - /// Convert this operation into a [`SyncOp`]. - pub fn into_sync(self) -> Option { - match self { - Self::Create { uuid } => Some(SyncOp::Create { uuid }), - Self::Delete { uuid, .. } => Some(SyncOp::Delete { uuid }), - Self::Update { - uuid, - property, - value, - timestamp, - .. - } => Some(SyncOp::Update { - uuid, - property, - value, - timestamp, - }), - Self::UndoPoint => None, - } - } - - /// Determine whether this is an undo point. - pub fn is_undo_point(&self) -> bool { - self == &Self::UndoPoint - } - - /// Generate a sequence of SyncOp's to reverse the effects of this ReplicaOp. - pub fn reverse_ops(self) -> Vec { - match self { - Self::Create { uuid } => vec![SyncOp::Delete { uuid }], - Self::Delete { uuid, mut old_task } => { - let mut ops = vec![SyncOp::Create { uuid }]; - // We don't have the original update timestamp, but it doesn't - // matter because this SyncOp will just be applied and discarded. - let timestamp = Utc::now(); - for (property, value) in old_task.drain() { - ops.push(SyncOp::Update { - uuid, - property, - value: Some(value), - timestamp, - }); - } - ops - } - Self::Update { - uuid, - property, - old_value, - timestamp, - .. - } => vec![SyncOp::Update { - uuid, - property, - value: old_value, - timestamp, - }], - Self::UndoPoint => vec![], - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::errors::Result; - use crate::storage::taskmap_with; - use chrono::Utc; - use pretty_assertions::assert_eq; - - use ReplicaOp::*; - - #[test] - fn test_json_create() -> Result<()> { - let uuid = Uuid::new_v4(); - let op = Create { uuid }; - let json = serde_json::to_string(&op)?; - assert_eq!(json, format!(r#"{{"Create":{{"uuid":"{}"}}}}"#, uuid)); - let deser: ReplicaOp = serde_json::from_str(&json)?; - assert_eq!(deser, op); - Ok(()) - } - - #[test] - fn test_json_delete() -> Result<()> { - let uuid = Uuid::new_v4(); - let old_task = vec![("foo".into(), "bar".into())].drain(..).collect(); - let op = Delete { uuid, old_task }; - let json = serde_json::to_string(&op)?; - assert_eq!( - json, - format!( - r#"{{"Delete":{{"uuid":"{}","old_task":{{"foo":"bar"}}}}}}"#, - uuid - ) - ); - let deser: ReplicaOp = serde_json::from_str(&json)?; - assert_eq!(deser, op); - Ok(()) - } - - #[test] - fn test_json_update() -> Result<()> { - let uuid = Uuid::new_v4(); - let timestamp = Utc::now(); - - let op = Update { - uuid, - property: "abc".into(), - old_value: Some("true".into()), - value: Some("false".into()), - timestamp, - }; - - let json = serde_json::to_string(&op)?; - assert_eq!( - json, - format!( - r#"{{"Update":{{"uuid":"{}","property":"abc","old_value":"true","value":"false","timestamp":"{:?}"}}}}"#, - uuid, timestamp, - ) - ); - let deser: ReplicaOp = serde_json::from_str(&json)?; - assert_eq!(deser, op); - Ok(()) - } - - #[test] - fn test_json_update_none() -> Result<()> { - let uuid = Uuid::new_v4(); - let timestamp = Utc::now(); - - let op = Update { - uuid, - property: "abc".into(), - old_value: None, - value: None, - timestamp, - }; - - let json = serde_json::to_string(&op)?; - assert_eq!( - json, - format!( - r#"{{"Update":{{"uuid":"{}","property":"abc","old_value":null,"value":null,"timestamp":"{:?}"}}}}"#, - uuid, timestamp, - ) - ); - let deser: ReplicaOp = serde_json::from_str(&json)?; - assert_eq!(deser, op); - Ok(()) - } - - #[test] - fn test_into_sync_create() { - let uuid = Uuid::new_v4(); - assert_eq!(Create { uuid }.into_sync(), Some(SyncOp::Create { uuid })); - } - - #[test] - fn test_into_sync_delete() { - let uuid = Uuid::new_v4(); - assert_eq!( - Delete { - uuid, - old_task: TaskMap::new() - } - .into_sync(), - Some(SyncOp::Delete { uuid }) - ); - } - - #[test] - fn test_into_sync_update() { - let uuid = Uuid::new_v4(); - let timestamp = Utc::now(); - assert_eq!( - Update { - uuid, - property: "prop".into(), - old_value: Some("foo".into()), - value: Some("v".into()), - timestamp, - } - .into_sync(), - Some(SyncOp::Update { - uuid, - property: "prop".into(), - value: Some("v".into()), - timestamp, - }) - ); - } - - #[test] - fn test_into_sync_undo_point() { - assert_eq!(UndoPoint.into_sync(), None); - } - - #[test] - fn test_reverse_create() { - let uuid = Uuid::new_v4(); - assert_eq!(Create { uuid }.reverse_ops(), vec![SyncOp::Delete { uuid }]); - } - - #[test] - fn test_reverse_delete() { - let uuid = Uuid::new_v4(); - let reversed = Delete { - uuid, - old_task: taskmap_with(vec![("prop1".into(), "v1".into())]), - } - .reverse_ops(); - assert_eq!(reversed.len(), 2); - assert_eq!(reversed[0], SyncOp::Create { uuid }); - assert!(matches!( - &reversed[1], - SyncOp::Update { uuid: u, property: p, value: Some(v), ..} - if u == &uuid && p == "prop1" && v == "v1" - )); - } - - #[test] - fn test_reverse_update() { - let uuid = Uuid::new_v4(); - let timestamp = Utc::now(); - assert_eq!( - Update { - uuid, - property: "prop".into(), - old_value: Some("foo".into()), - value: Some("v".into()), - timestamp, - } - .reverse_ops(), - vec![SyncOp::Update { - uuid, - property: "prop".into(), - value: Some("foo".into()), - timestamp, - }] - ); - } - - #[test] - fn test_reverse_undo_point() { - assert_eq!(UndoPoint.reverse_ops(), vec![]); - } -} diff --git a/taskchampion/taskchampion/src/storage/sqlite.rs b/taskchampion/taskchampion/src/storage/sqlite.rs deleted file mode 100644 index 8abdde476..000000000 --- a/taskchampion/taskchampion/src/storage/sqlite.rs +++ /dev/null @@ -1,819 +0,0 @@ -use crate::errors::Result; -use crate::storage::{ReplicaOp, Storage, StorageTxn, TaskMap, VersionId, DEFAULT_BASE_VERSION}; -use anyhow::Context; -use rusqlite::types::{FromSql, ToSql}; -use rusqlite::{params, Connection, OpenFlags, OptionalExtension}; -use std::path::Path; -use uuid::Uuid; - -#[derive(Debug, thiserror::Error)] -pub enum SqliteError { - #[error("SQLite transaction already committted")] - TransactionAlreadyCommitted, -} - -/// Newtype to allow implementing `FromSql` for foreign `uuid::Uuid` -pub(crate) struct StoredUuid(pub(crate) Uuid); - -/// Conversion from Uuid stored as a string (rusqlite's uuid feature stores as binary blob) -impl FromSql for StoredUuid { - fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult { - let u = Uuid::parse_str(value.as_str()?) - .map_err(|_| rusqlite::types::FromSqlError::InvalidType)?; - Ok(StoredUuid(u)) - } -} - -/// Store Uuid as string in database -impl ToSql for StoredUuid { - fn to_sql(&self) -> rusqlite::Result> { - let s = self.0.to_string(); - Ok(s.into()) - } -} - -/// Wraps [`TaskMap`] (type alias for HashMap) so we can implement rusqlite conversion traits for it -struct StoredTaskMap(TaskMap); - -/// Parses TaskMap stored as JSON in string column -impl FromSql for StoredTaskMap { - fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult { - let o: TaskMap = serde_json::from_str(value.as_str()?) - .map_err(|_| rusqlite::types::FromSqlError::InvalidType)?; - Ok(StoredTaskMap(o)) - } -} - -/// Stores TaskMap in string column -impl ToSql for StoredTaskMap { - fn to_sql(&self) -> rusqlite::Result> { - let s = serde_json::to_string(&self.0) - .map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?; - Ok(s.into()) - } -} - -/// Stores [`ReplicaOp`] in SQLite -impl FromSql for ReplicaOp { - fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult { - let o: ReplicaOp = serde_json::from_str(value.as_str()?) - .map_err(|_| rusqlite::types::FromSqlError::InvalidType)?; - Ok(o) - } -} - -/// Parses ReplicaOp stored as JSON in string column -impl ToSql for ReplicaOp { - fn to_sql(&self) -> rusqlite::Result> { - let s = serde_json::to_string(&self) - .map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?; - Ok(s.into()) - } -} - -/// SqliteStorage is an on-disk storage backed by SQLite3. -pub struct SqliteStorage { - con: Connection, -} - -impl SqliteStorage { - pub fn new>(directory: P, create_if_missing: bool) -> Result { - if create_if_missing { - // Ensure parent folder exists - std::fs::create_dir_all(&directory)?; - } - - // Open (or create) database - let db_file = directory.as_ref().join("taskchampion.sqlite3"); - let mut flags = OpenFlags::default(); - // default contains SQLITE_OPEN_CREATE, so remove it if we are not to - // create a DB when missing. - if !create_if_missing { - flags.remove(OpenFlags::SQLITE_OPEN_CREATE); - } - let con = Connection::open_with_flags(db_file, flags)?; - - // Initialize database - let queries = vec![ - "CREATE TABLE IF NOT EXISTS operations (id INTEGER PRIMARY KEY AUTOINCREMENT, data STRING);", - "CREATE TABLE IF NOT EXISTS sync_meta (key STRING PRIMARY KEY, value STRING);", - "CREATE TABLE IF NOT EXISTS tasks (uuid STRING PRIMARY KEY, data STRING);", - "CREATE TABLE IF NOT EXISTS working_set (id INTEGER PRIMARY KEY, uuid STRING);", - ]; - for q in queries { - con.execute(q, []).context("Creating table")?; - } - - Ok(SqliteStorage { con }) - } -} - -struct Txn<'t> { - txn: Option>, -} - -impl<'t> Txn<'t> { - fn get_txn(&self) -> std::result::Result<&rusqlite::Transaction<'t>, SqliteError> { - self.txn - .as_ref() - .ok_or(SqliteError::TransactionAlreadyCommitted) - } - - fn get_next_working_set_number(&self) -> Result { - let t = self.get_txn()?; - let next_id: Option = t - .query_row( - "SELECT COALESCE(MAX(id), 0) + 1 FROM working_set", - [], - |r| r.get(0), - ) - .optional() - .context("Getting highest working set ID")?; - - Ok(next_id.unwrap_or(0)) - } -} - -impl Storage for SqliteStorage { - fn txn<'a>(&'a mut self) -> Result> { - let txn = self.con.transaction()?; - Ok(Box::new(Txn { txn: Some(txn) })) - } -} - -impl<'t> StorageTxn for Txn<'t> { - fn get_task(&mut self, uuid: Uuid) -> Result> { - let t = self.get_txn()?; - let result: Option = t - .query_row( - "SELECT data FROM tasks WHERE uuid = ? LIMIT 1", - [&StoredUuid(uuid)], - |r| r.get("data"), - ) - .optional()?; - - // Get task from "stored" wrapper - Ok(result.map(|t| t.0)) - } - - fn create_task(&mut self, uuid: Uuid) -> Result { - let t = self.get_txn()?; - let count: usize = t.query_row( - "SELECT count(uuid) FROM tasks WHERE uuid = ?", - [&StoredUuid(uuid)], - |x| x.get(0), - )?; - if count > 0 { - return Ok(false); - } - - let data = TaskMap::default(); - t.execute( - "INSERT INTO tasks (uuid, data) VALUES (?, ?)", - params![&StoredUuid(uuid), &StoredTaskMap(data)], - ) - .context("Create task query")?; - Ok(true) - } - - fn set_task(&mut self, uuid: Uuid, task: TaskMap) -> Result<()> { - let t = self.get_txn()?; - t.execute( - "INSERT OR REPLACE INTO tasks (uuid, data) VALUES (?, ?)", - params![&StoredUuid(uuid), &StoredTaskMap(task)], - ) - .context("Update task query")?; - Ok(()) - } - - fn delete_task(&mut self, uuid: Uuid) -> Result { - let t = self.get_txn()?; - let changed = t - .execute("DELETE FROM tasks WHERE uuid = ?", [&StoredUuid(uuid)]) - .context("Delete task query")?; - Ok(changed > 0) - } - - fn all_tasks(&mut self) -> Result> { - let t = self.get_txn()?; - - let mut q = t.prepare("SELECT uuid, data FROM tasks")?; - let rows = q.query_map([], |r| { - let uuid: StoredUuid = r.get("uuid")?; - let data: StoredTaskMap = r.get("data")?; - Ok((uuid.0, data.0)) - })?; - - let mut ret = vec![]; - for r in rows { - ret.push(r?); - } - Ok(ret) - } - - fn all_task_uuids(&mut self) -> Result> { - let t = self.get_txn()?; - - let mut q = t.prepare("SELECT uuid FROM tasks")?; - let rows = q.query_map([], |r| { - let uuid: StoredUuid = r.get("uuid")?; - Ok(uuid.0) - })?; - - let mut ret = vec![]; - for r in rows { - ret.push(r?); - } - Ok(ret) - } - - fn base_version(&mut self) -> Result { - let t = self.get_txn()?; - - let version: Option = t - .query_row( - "SELECT value FROM sync_meta WHERE key = 'base_version'", - [], - |r| r.get("value"), - ) - .optional()?; - Ok(version.map(|u| u.0).unwrap_or(DEFAULT_BASE_VERSION)) - } - - fn set_base_version(&mut self, version: VersionId) -> Result<()> { - let t = self.get_txn()?; - t.execute( - "INSERT OR REPLACE INTO sync_meta (key, value) VALUES (?, ?)", - params!["base_version", &StoredUuid(version)], - ) - .context("Set base version")?; - Ok(()) - } - - fn operations(&mut self) -> Result> { - let t = self.get_txn()?; - - let mut q = t.prepare("SELECT data FROM operations ORDER BY id ASC")?; - let rows = q.query_map([], |r| { - let data: ReplicaOp = r.get("data")?; - Ok(data) - })?; - - let mut ret = vec![]; - for r in rows { - ret.push(r?); - } - Ok(ret) - } - - fn num_operations(&mut self) -> Result { - let t = self.get_txn()?; - let count: usize = t.query_row("SELECT count(*) FROM operations", [], |x| x.get(0))?; - Ok(count) - } - - fn add_operation(&mut self, op: ReplicaOp) -> Result<()> { - let t = self.get_txn()?; - - t.execute("INSERT INTO operations (data) VALUES (?)", params![&op]) - .context("Add operation query")?; - Ok(()) - } - - fn set_operations(&mut self, ops: Vec) -> Result<()> { - let t = self.get_txn()?; - t.execute("DELETE FROM operations", []) - .context("Clear all existing operations")?; - t.execute("DELETE FROM sqlite_sequence WHERE name = 'operations'", []) - .context("Clear all existing operations")?; - - for o in ops { - self.add_operation(o)?; - } - Ok(()) - } - - fn get_working_set(&mut self) -> Result>> { - let t = self.get_txn()?; - - let mut q = t.prepare("SELECT id, uuid FROM working_set ORDER BY id ASC")?; - let rows = q - .query_map([], |r| { - let id: usize = r.get("id")?; - let uuid: StoredUuid = r.get("uuid")?; - Ok((id, uuid.0)) - }) - .context("Get working set query")?; - - let rows: Vec> = rows.collect(); - let mut res = Vec::with_capacity(rows.len()); - for _ in 0..self - .get_next_working_set_number() - .context("Getting working set number")? - { - res.push(None); - } - for r in rows { - let (id, uuid) = r?; - res[id] = Some(uuid); - } - - Ok(res) - } - - fn add_to_working_set(&mut self, uuid: Uuid) -> Result { - let t = self.get_txn()?; - - let next_working_id = self.get_next_working_set_number()?; - - t.execute( - "INSERT INTO working_set (id, uuid) VALUES (?, ?)", - params![next_working_id, &StoredUuid(uuid)], - ) - .context("Create task query")?; - - Ok(next_working_id) - } - - fn set_working_set_item(&mut self, index: usize, uuid: Option) -> Result<()> { - let t = self.get_txn()?; - match uuid { - // Add or override item - Some(uuid) => t.execute( - "INSERT OR REPLACE INTO working_set (id, uuid) VALUES (?, ?)", - params![index, &StoredUuid(uuid)], - ), - // Setting to None removes the row from database - None => t.execute("DELETE FROM working_set WHERE id = ?", [index]), - } - .context("Set working set item query")?; - Ok(()) - } - - fn clear_working_set(&mut self) -> Result<()> { - let t = self.get_txn()?; - t.execute("DELETE FROM working_set", []) - .context("Clear working set query")?; - Ok(()) - } - - fn commit(&mut self) -> Result<()> { - let t = self - .txn - .take() - .ok_or(SqliteError::TransactionAlreadyCommitted)?; - t.commit().context("Committing transaction")?; - Ok(()) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::storage::taskmap_with; - use pretty_assertions::assert_eq; - use tempfile::TempDir; - - #[test] - fn test_empty_dir() -> Result<()> { - let tmp_dir = TempDir::new()?; - let non_existant = tmp_dir.path().join("subdir"); - let mut storage = SqliteStorage::new(non_existant, true)?; - let uuid = Uuid::new_v4(); - { - let mut txn = storage.txn()?; - assert!(txn.create_task(uuid)?); - txn.commit()?; - } - { - let mut txn = storage.txn()?; - let task = txn.get_task(uuid)?; - assert_eq!(task, Some(taskmap_with(vec![]))); - } - Ok(()) - } - - #[test] - fn drop_transaction() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - - { - let mut txn = storage.txn()?; - assert!(txn.create_task(uuid1)?); - txn.commit()?; - } - - { - let mut txn = storage.txn()?; - assert!(txn.create_task(uuid2)?); - std::mem::drop(txn); // Unnecessary explicit drop of transaction - } - - { - let mut txn = storage.txn()?; - let uuids = txn.all_task_uuids()?; - - assert_eq!(uuids, [uuid1]); - } - - Ok(()) - } - - #[test] - fn test_create() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid = Uuid::new_v4(); - { - let mut txn = storage.txn()?; - assert!(txn.create_task(uuid)?); - txn.commit()?; - } - { - let mut txn = storage.txn()?; - let task = txn.get_task(uuid)?; - assert_eq!(task, Some(taskmap_with(vec![]))); - } - Ok(()) - } - - #[test] - fn test_create_exists() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid = Uuid::new_v4(); - { - let mut txn = storage.txn()?; - assert!(txn.create_task(uuid)?); - txn.commit()?; - } - { - let mut txn = storage.txn()?; - assert!(!txn.create_task(uuid)?); - txn.commit()?; - } - Ok(()) - } - - #[test] - fn test_get_missing() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid = Uuid::new_v4(); - { - let mut txn = storage.txn()?; - let task = txn.get_task(uuid)?; - assert_eq!(task, None); - } - Ok(()) - } - - #[test] - fn test_set_task() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid = Uuid::new_v4(); - { - let mut txn = storage.txn()?; - txn.set_task(uuid, taskmap_with(vec![("k".to_string(), "v".to_string())]))?; - txn.commit()?; - } - { - let mut txn = storage.txn()?; - let task = txn.get_task(uuid)?; - assert_eq!( - task, - Some(taskmap_with(vec![("k".to_string(), "v".to_string())])) - ); - } - Ok(()) - } - - #[test] - fn test_delete_task_missing() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid = Uuid::new_v4(); - { - let mut txn = storage.txn()?; - assert!(!txn.delete_task(uuid)?); - } - Ok(()) - } - - #[test] - fn test_delete_task_exists() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid = Uuid::new_v4(); - { - let mut txn = storage.txn()?; - assert!(txn.create_task(uuid)?); - txn.commit()?; - } - { - let mut txn = storage.txn()?; - assert!(txn.delete_task(uuid)?); - } - Ok(()) - } - - #[test] - fn test_all_tasks_empty() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - { - let mut txn = storage.txn()?; - let tasks = txn.all_tasks()?; - assert_eq!(tasks, vec![]); - } - Ok(()) - } - - #[test] - fn test_all_tasks_and_uuids() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - { - let mut txn = storage.txn()?; - assert!(txn.create_task(uuid1)?); - txn.set_task( - uuid1, - taskmap_with(vec![("num".to_string(), "1".to_string())]), - )?; - assert!(txn.create_task(uuid2)?); - txn.set_task( - uuid2, - taskmap_with(vec![("num".to_string(), "2".to_string())]), - )?; - txn.commit()?; - } - { - let mut txn = storage.txn()?; - let mut tasks = txn.all_tasks()?; - - // order is nondeterministic, so sort by uuid - tasks.sort_by(|a, b| a.0.cmp(&b.0)); - - let mut exp = vec![ - ( - uuid1, - taskmap_with(vec![("num".to_string(), "1".to_string())]), - ), - ( - uuid2, - taskmap_with(vec![("num".to_string(), "2".to_string())]), - ), - ]; - exp.sort_by(|a, b| a.0.cmp(&b.0)); - - assert_eq!(tasks, exp); - } - { - let mut txn = storage.txn()?; - let mut uuids = txn.all_task_uuids()?; - uuids.sort(); - - let mut exp = vec![uuid1, uuid2]; - exp.sort(); - - assert_eq!(uuids, exp); - } - Ok(()) - } - - #[test] - fn test_base_version_default() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - { - let mut txn = storage.txn()?; - assert_eq!(txn.base_version()?, DEFAULT_BASE_VERSION); - } - Ok(()) - } - - #[test] - fn test_base_version_setting() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let u = Uuid::new_v4(); - { - let mut txn = storage.txn()?; - txn.set_base_version(u)?; - txn.commit()?; - } - { - let mut txn = storage.txn()?; - assert_eq!(txn.base_version()?, u); - } - Ok(()) - } - - #[test] - fn test_operations() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - let uuid3 = Uuid::new_v4(); - - // create some operations - { - let mut txn = storage.txn()?; - txn.add_operation(ReplicaOp::Create { uuid: uuid1 })?; - txn.add_operation(ReplicaOp::Create { uuid: uuid2 })?; - txn.commit()?; - } - - // read them back - { - let mut txn = storage.txn()?; - let ops = txn.operations()?; - assert_eq!( - ops, - vec![ - ReplicaOp::Create { uuid: uuid1 }, - ReplicaOp::Create { uuid: uuid2 }, - ] - ); - - assert_eq!(txn.num_operations()?, 2); - } - - // set them to a different bunch - { - let mut txn = storage.txn()?; - txn.set_operations(vec![ - ReplicaOp::Delete { - uuid: uuid2, - old_task: TaskMap::new(), - }, - ReplicaOp::Delete { - uuid: uuid1, - old_task: TaskMap::new(), - }, - ])?; - txn.commit()?; - } - - // create some more operations (to test adding operations after clearing) - { - let mut txn = storage.txn()?; - txn.add_operation(ReplicaOp::Create { uuid: uuid3 })?; - txn.add_operation(ReplicaOp::Delete { - uuid: uuid3, - old_task: TaskMap::new(), - })?; - txn.commit()?; - } - - // read them back - { - let mut txn = storage.txn()?; - let ops = txn.operations()?; - assert_eq!( - ops, - vec![ - ReplicaOp::Delete { - uuid: uuid2, - old_task: TaskMap::new() - }, - ReplicaOp::Delete { - uuid: uuid1, - old_task: TaskMap::new() - }, - ReplicaOp::Create { uuid: uuid3 }, - ReplicaOp::Delete { - uuid: uuid3, - old_task: TaskMap::new() - }, - ] - ); - assert_eq!(txn.num_operations()?, 4); - } - Ok(()) - } - - #[test] - fn get_working_set_empty() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - - { - let mut txn = storage.txn()?; - let ws = txn.get_working_set()?; - assert_eq!(ws, vec![None]); - } - - Ok(()) - } - - #[test] - fn add_to_working_set() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - - { - let mut txn = storage.txn()?; - txn.add_to_working_set(uuid1)?; - txn.add_to_working_set(uuid2)?; - txn.commit()?; - } - - { - let mut txn = storage.txn()?; - let ws = txn.get_working_set()?; - assert_eq!(ws, vec![None, Some(uuid1), Some(uuid2)]); - } - - Ok(()) - } - - #[test] - fn clear_working_set() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - - { - let mut txn = storage.txn()?; - txn.add_to_working_set(uuid1)?; - txn.add_to_working_set(uuid2)?; - txn.commit()?; - } - - { - let mut txn = storage.txn()?; - txn.clear_working_set()?; - txn.add_to_working_set(uuid2)?; - txn.add_to_working_set(uuid1)?; - txn.commit()?; - } - - { - let mut txn = storage.txn()?; - let ws = txn.get_working_set()?; - assert_eq!(ws, vec![None, Some(uuid2), Some(uuid1)]); - } - - Ok(()) - } - - #[test] - fn set_working_set_item() -> Result<()> { - let tmp_dir = TempDir::new()?; - let mut storage = SqliteStorage::new(tmp_dir.path(), true)?; - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - - { - let mut txn = storage.txn()?; - txn.add_to_working_set(uuid1)?; - txn.add_to_working_set(uuid2)?; - txn.commit()?; - } - - { - let mut txn = storage.txn()?; - let ws = txn.get_working_set()?; - assert_eq!(ws, vec![None, Some(uuid1), Some(uuid2)]); - } - - // Clear one item - { - let mut txn = storage.txn()?; - txn.set_working_set_item(1, None)?; - txn.commit()?; - } - - { - let mut txn = storage.txn()?; - let ws = txn.get_working_set()?; - assert_eq!(ws, vec![None, None, Some(uuid2)]); - } - - // Override item - { - let mut txn = storage.txn()?; - txn.set_working_set_item(2, Some(uuid1))?; - txn.commit()?; - } - - { - let mut txn = storage.txn()?; - let ws = txn.get_working_set()?; - assert_eq!(ws, vec![None, None, Some(uuid1)]); - } - - Ok(()) - } -} diff --git a/taskchampion/taskchampion/src/task/annotation.rs b/taskchampion/taskchampion/src/task/annotation.rs deleted file mode 100644 index 951dc3f11..000000000 --- a/taskchampion/taskchampion/src/task/annotation.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::Timestamp; - -/// An annotation for a task -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Annotation { - /// Time the annotation was made - pub entry: Timestamp, - /// Content of the annotation - pub description: String, -} diff --git a/taskchampion/taskchampion/src/task/mod.rs b/taskchampion/taskchampion/src/task/mod.rs deleted file mode 100644 index 259ed6ab0..000000000 --- a/taskchampion/taskchampion/src/task/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![allow(clippy::module_inception)] -mod annotation; -mod status; -mod tag; -mod task; -mod time; - -pub use annotation::Annotation; -pub use status::Status; -pub use tag::Tag; -pub use task::{Task, TaskMut}; -pub use time::{utc_timestamp, Timestamp}; diff --git a/taskchampion/taskchampion/src/task/status.rs b/taskchampion/taskchampion/src/task/status.rs deleted file mode 100644 index 38d017df7..000000000 --- a/taskchampion/taskchampion/src/task/status.rs +++ /dev/null @@ -1,74 +0,0 @@ -/// The status of a task, as defined by the task data model. -#[derive(Debug, PartialEq, Eq, Clone, strum_macros::Display)] -#[repr(C)] -pub enum Status { - Pending, - Completed, - Deleted, - Recurring, - /// Unknown signifies a status in the task DB that was not - /// recognized. This supports forward-compatibility if a - /// new status is added. Tasks with unknown status should - /// be ignored (but not deleted). - Unknown(String), -} - -impl Status { - /// Get a Status from the string value in a TaskMap - pub(crate) fn from_taskmap(s: &str) -> Status { - match s { - "pending" => Status::Pending, - "completed" => Status::Completed, - "deleted" => Status::Deleted, - "recurring" => Status::Recurring, - v => Status::Unknown(v.to_string()), - } - } - - /// Get the 1-character value for this status to use in the TaskMap. - pub(crate) fn to_taskmap(&self) -> &str { - match self { - Status::Pending => "pending", - Status::Completed => "completed", - Status::Deleted => "deleted", - Status::Recurring => "recurring", - Status::Unknown(v) => v.as_ref(), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn to_taskmap() { - assert_eq!(Status::Pending.to_taskmap(), "pending"); - assert_eq!(Status::Completed.to_taskmap(), "completed"); - assert_eq!(Status::Deleted.to_taskmap(), "deleted"); - assert_eq!(Status::Recurring.to_taskmap(), "recurring"); - assert_eq!(Status::Unknown("wishful".into()).to_taskmap(), "wishful"); - } - - #[test] - fn from_taskmap() { - assert_eq!(Status::from_taskmap("pending"), Status::Pending); - assert_eq!(Status::from_taskmap("completed"), Status::Completed); - assert_eq!(Status::from_taskmap("deleted"), Status::Deleted); - assert_eq!(Status::from_taskmap("recurring"), Status::Recurring); - assert_eq!( - Status::from_taskmap("something-else"), - Status::Unknown("something-else".into()) - ); - } - - #[test] - fn display() { - assert_eq!(format!("{}", Status::Pending), "Pending"); - assert_eq!(format!("{}", Status::Completed), "Completed"); - assert_eq!(format!("{}", Status::Deleted), "Deleted"); - assert_eq!(format!("{}", Status::Recurring), "Recurring"); - assert_eq!(format!("{}", Status::Unknown("wishful".into())), "Unknown"); - } -} diff --git a/taskchampion/taskchampion/src/task/tag.rs b/taskchampion/taskchampion/src/task/tag.rs deleted file mode 100644 index a4f1e677c..000000000 --- a/taskchampion/taskchampion/src/task/tag.rs +++ /dev/null @@ -1,174 +0,0 @@ -use std::convert::TryFrom; -use std::fmt; -use std::str::FromStr; - -/// A Tag is a descriptor for a task, that is either present or absent, and can be used for -/// filtering. Tags composed of all uppercase letters are reserved for synthetic tags. -/// -/// Valid tags must not contain whitespace or any of the characters in `+-*/(<>^! %=~`. -/// The first characters additionally cannot be a digit, and subsequent characters cannot be `:`. -/// This definition is based on [that of -/// TaskWarrior](https://github.com/GothenburgBitFactory/taskwarrior/blob/663c6575ceca5bd0135ae884879339dac89d3142/src/Lexer.cpp#L146-L164). -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub struct Tag(TagInner); - -/// Inner type to hide the implementation -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub(super) enum TagInner { - User(String), - Synthetic(SyntheticTag), -} - -// see doc comment for Tag, above -pub const INVALID_TAG_CHARACTERS: &str = "+-*/(<>^! %=~"; - -impl Tag { - /// True if this tag is a synthetic tag - pub fn is_synthetic(&self) -> bool { - matches!(self.0, TagInner::Synthetic(_)) - } - - /// True if this tag is a user-provided tag (not synthetic) - pub fn is_user(&self) -> bool { - matches!(self.0, TagInner::User(_)) - } - - pub(super) fn inner(&self) -> &TagInner { - &self.0 - } - - pub(super) fn from_inner(inner: TagInner) -> Self { - Self(inner) - } -} - -impl FromStr for Tag { - type Err = anyhow::Error; - - fn from_str(value: &str) -> Result { - fn err(value: &str) -> Result { - anyhow::bail!("invalid tag {:?}", value) - } - - // first, look for synthetic tags - if value.chars().all(|c| c.is_ascii_uppercase()) { - if let Ok(st) = SyntheticTag::from_str(value) { - return Ok(Self(TagInner::Synthetic(st))); - } - // all uppercase, but not a valid synthetic tag - return err(value); - } - - if let Some(c) = value.chars().next() { - if c.is_whitespace() || c.is_ascii_digit() || INVALID_TAG_CHARACTERS.contains(c) { - return err(value); - } - } else { - return err(value); - } - if !value - .chars() - .skip(1) - .all(|c| !(c.is_whitespace() || c == ':' || INVALID_TAG_CHARACTERS.contains(c))) - { - return err(value); - } - Ok(Self(TagInner::User(String::from(value)))) - } -} - -impl TryFrom<&str> for Tag { - type Error = anyhow::Error; - - fn try_from(value: &str) -> Result { - Self::from_str(value) - } -} - -impl TryFrom<&String> for Tag { - type Error = anyhow::Error; - - fn try_from(value: &String) -> Result { - Self::from_str(&value[..]) - } -} - -impl fmt::Display for Tag { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.0 { - TagInner::User(s) => s.fmt(f), - TagInner::Synthetic(st) => st.as_ref().fmt(f), - } - } -} - -impl AsRef for Tag { - fn as_ref(&self) -> &str { - match &self.0 { - TagInner::User(s) => s.as_ref(), - TagInner::Synthetic(st) => st.as_ref(), - } - } -} - -/// A synthetic tag, represented as an `enum`. This type is used directly by -/// [`taskchampion::task::task`] for efficiency. -#[derive( - Debug, - Clone, - Eq, - PartialEq, - Ord, - PartialOrd, - Hash, - strum_macros::EnumString, - strum_macros::AsRefStr, - strum_macros::EnumIter, -)] -#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] -pub(super) enum SyntheticTag { - // When adding items here, also implement and test them in `task.rs` and document them in - // `docs/src/tags.md`. - Waiting, - Active, - Pending, - Completed, - Deleted, - Blocked, - Unblocked, - Blocking, -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - use rstest::rstest; - use std::convert::TryInto; - - #[rstest] - #[case::simple("abc")] - #[case::colon_prefix(":abc")] - #[case::letters_and_numbers("a123_456")] - #[case::synthetic("WAITING")] - fn test_tag_try_into_success(#[case] s: &'static str) { - let tag: Tag = s.try_into().unwrap(); - // check Display (via to_string) and AsRef while we're here - assert_eq!(tag.to_string(), s.to_owned()); - assert_eq!(tag.as_ref(), s); - } - - #[rstest] - #[case::empty("")] - #[case::colon_infix("a:b")] - #[case::digits("999")] - #[case::bangs("abc!!!")] - #[case::no_such_synthetic("NOSUCH")] - fn test_tag_try_into_err(#[case] s: &'static str) { - let tag: Result = s.try_into(); - assert_eq!( - tag.unwrap_err().to_string(), - format!("invalid tag \"{}\"", s) - ); - } -} diff --git a/taskchampion/taskchampion/src/task/task.rs b/taskchampion/taskchampion/src/task/task.rs deleted file mode 100644 index 3dda37d61..000000000 --- a/taskchampion/taskchampion/src/task/task.rs +++ /dev/null @@ -1,1270 +0,0 @@ -use super::tag::{SyntheticTag, TagInner}; -use super::{utc_timestamp, Annotation, Status, Tag, Timestamp}; -use crate::depmap::DependencyMap; -use crate::errors::{Error, Result}; -use crate::replica::Replica; -use crate::storage::TaskMap; -use chrono::prelude::*; -use log::trace; -use std::convert::AsRef; -use std::convert::TryInto; -use std::rc::Rc; -use std::str::FromStr; -use uuid::Uuid; - -/* The Task and TaskMut classes wrap the underlying [`TaskMap`], which is a simple key/value map. - * They provide semantic meaning to that TaskMap according to the TaskChampion data model. For - * example, [`get_status`](Task::get_status) and [`set_status`](TaskMut::set_status) translate from - * strings in the TaskMap to [`Status`]. - * - * The same approach applies for more complex data such as dependencies or annotations. Users of - * this API should only need the [`get_taskmap`](Task::get_taskmap) method for debugging purposes, - * and should never need to make changes to the TaskMap directly. - */ - -/// A task, as publicly exposed by this crate. -/// -/// Note that Task objects represent a snapshot of the task at a moment in time, and are not -/// protected by the atomicity of the backend storage. Concurrent modifications are safe, -/// but a Task that is cached for more than a few seconds may cause the user to see stale -/// data. Fetch, use, and drop Tasks quickly. -/// -/// This struct contains only getters for various values on the task. The -/// [`into_mut`](Task::into_mut) method -/// returns a TaskMut which can be used to modify the task. -#[derive(Debug, Clone)] -pub struct Task { - uuid: Uuid, - taskmap: TaskMap, - depmap: Rc, -} - -impl PartialEq for Task { - fn eq(&self, other: &Task) -> bool { - // compare only the taskmap and uuid; depmap is just present for reference - self.uuid == other.uuid && self.taskmap == other.taskmap - } -} - -/// A mutable task, with setter methods. -/// -/// Most methods are simple setters and not further described. Calling a setter will update the -/// referenced Replica, as well as the included Task, immediately. -/// -/// The [`Task`] methods are available on [`TaskMut`] via [`Deref`](std::ops::Deref). -pub struct TaskMut<'r> { - task: Task, - replica: &'r mut Replica, - updated_modified: bool, -} - -/// An enum containing all of the key names defined in the data model, with the exception -/// of the properties containing data (`tag_..`, etc.) -#[derive(strum_macros::AsRefStr, strum_macros::EnumString)] -#[strum(serialize_all = "kebab-case")] -enum Prop { - Description, - Due, - Modified, - Start, - Status, - Priority, - Wait, - End, - Entry, -} - -#[allow(clippy::ptr_arg)] -fn uda_string_to_tuple(key: &str) -> (&str, &str) { - let mut iter = key.splitn(2, '.'); - let first = iter.next().unwrap(); - let second = iter.next(); - if let Some(second) = second { - (first, second) - } else { - ("", first) - } -} - -fn uda_tuple_to_string(namespace: impl AsRef, key: impl AsRef) -> String { - let namespace = namespace.as_ref(); - let key = key.as_ref(); - if namespace.is_empty() { - key.into() - } else { - format!("{}.{}", namespace, key) - } -} - -impl Task { - pub(crate) fn new(uuid: Uuid, taskmap: TaskMap, depmap: Rc) -> Task { - Task { - uuid, - taskmap, - depmap, - } - } - - pub fn get_uuid(&self) -> Uuid { - self.uuid - } - - pub fn get_taskmap(&self) -> &TaskMap { - &self.taskmap - } - - /// Prepare to mutate this task, requiring a mutable Replica - /// in order to update the data it contains. - pub fn into_mut(self, replica: &mut Replica) -> TaskMut { - TaskMut { - task: self, - replica, - updated_modified: false, - } - } - - pub fn get_status(&self) -> Status { - self.taskmap - .get(Prop::Status.as_ref()) - .map(|s| Status::from_taskmap(s)) - .unwrap_or(Status::Pending) - } - - pub fn get_description(&self) -> &str { - self.taskmap - .get(Prop::Description.as_ref()) - .map(|s| s.as_ref()) - .unwrap_or("") - } - - pub fn get_entry(&self) -> Option { - self.get_timestamp(Prop::Entry.as_ref()) - } - - pub fn get_priority(&self) -> &str { - self.taskmap - .get(Prop::Priority.as_ref()) - .map(|s| s.as_ref()) - .unwrap_or("") - } - - /// Get the wait time. If this value is set, it will be returned, even - /// if it is in the past. - pub fn get_wait(&self) -> Option { - self.get_timestamp(Prop::Wait.as_ref()) - } - - /// Determine whether this task is waiting now. - pub fn is_waiting(&self) -> bool { - if let Some(ts) = self.get_wait() { - return ts > Utc::now(); - } - false - } - - /// Determine whether this task is active -- that is, that it has been started - /// and not stopped. - pub fn is_active(&self) -> bool { - self.taskmap.contains_key(Prop::Start.as_ref()) - } - - /// Determine whether this task is blocked -- that is, has at least one unresolved dependency. - pub fn is_blocked(&self) -> bool { - self.depmap.dependencies(self.uuid).next().is_some() - } - - /// Determine whether this task is blocking -- that is, has at least one unresolved dependent. - pub fn is_blocking(&self) -> bool { - self.depmap.dependents(self.uuid).next().is_some() - } - - /// Determine whether a given synthetic tag is present on this task. All other - /// synthetic tag calculations are based on this one. - fn has_synthetic_tag(&self, synth: &SyntheticTag) -> bool { - match synth { - SyntheticTag::Waiting => self.is_waiting(), - SyntheticTag::Active => self.is_active(), - SyntheticTag::Pending => self.get_status() == Status::Pending, - SyntheticTag::Completed => self.get_status() == Status::Completed, - SyntheticTag::Deleted => self.get_status() == Status::Deleted, - SyntheticTag::Blocked => self.is_blocked(), - SyntheticTag::Unblocked => !self.is_blocked(), - SyntheticTag::Blocking => self.is_blocking(), - } - } - - /// Check if this task has the given tag - pub fn has_tag(&self, tag: &Tag) -> bool { - match tag.inner() { - TagInner::User(s) => self.taskmap.contains_key(&format!("tag_{}", s)), - TagInner::Synthetic(st) => self.has_synthetic_tag(st), - } - } - - /// Iterate over the task's tags - pub fn get_tags(&self) -> impl Iterator + '_ { - use strum::IntoEnumIterator; - - self.taskmap - .iter() - .filter_map(|(k, _)| { - if let Some(tag) = k.strip_prefix("tag_") { - if let Ok(tag) = tag.try_into() { - return Some(tag); - } - // note that invalid "tag_*" are ignored - } - None - }) - .chain( - SyntheticTag::iter() - .filter(move |st| self.has_synthetic_tag(st)) - .map(|st| Tag::from_inner(TagInner::Synthetic(st))), - ) - } - - /// Iterate over the task's annotations, in arbitrary order. - pub fn get_annotations(&self) -> impl Iterator + '_ { - self.taskmap.iter().filter_map(|(k, v)| { - if let Some(ts) = k.strip_prefix("annotation_") { - if let Ok(ts) = ts.parse::() { - return Some(Annotation { - entry: utc_timestamp(ts), - description: v.to_owned(), - }); - } - // note that invalid "annotation_*" are ignored - } - None - }) - } - - /// Get the named user defined attributes (UDA). This will return None - /// for any key defined in the Task data model, regardless of whether - /// it is set or not. - pub fn get_uda(&self, namespace: &str, key: &str) -> Option<&str> { - self.get_legacy_uda(uda_tuple_to_string(namespace, key).as_ref()) - } - - /// Get the user defined attributes (UDAs) of this task, in arbitrary order. Each key is split - /// on the first `.` character. Legacy keys that do not contain `.` are represented as `("", - /// key)`. - pub fn get_udas(&self) -> impl Iterator + '_ { - self.taskmap - .iter() - .filter(|(k, _)| !Task::is_known_key(k)) - .map(|(k, v)| (uda_string_to_tuple(k), v.as_ref())) - } - - /// Get the named user defined attribute (UDA) in a legacy format. This will return None for - /// any key defined in the Task data model, regardless of whether it is set or not. - pub fn get_legacy_uda(&self, key: &str) -> Option<&str> { - if Task::is_known_key(key) { - return None; - } - self.taskmap.get(key).map(|s| s.as_ref()) - } - - /// Like `get_udas`, but returning each UDA key as a single string. - pub fn get_legacy_udas(&self) -> impl Iterator + '_ { - self.taskmap - .iter() - .filter(|(p, _)| !Task::is_known_key(p)) - .map(|(p, v)| (p.as_ref(), v.as_ref())) - } - - /// Get the modification time for this task. - pub fn get_modified(&self) -> Option { - self.get_timestamp(Prop::Modified.as_ref()) - } - - /// Get the due time for this task. - pub fn get_due(&self) -> Option { - self.get_timestamp(Prop::Due.as_ref()) - } - - /// Get the UUIDs of tasks on which this task depends. - /// - /// This includes all dependencies, regardless of their status. In fact, it may include - /// dependencies that do not exist. - pub fn get_dependencies(&self) -> impl Iterator + '_ { - self.taskmap.iter().filter_map(|(p, _)| { - if let Some(dep_str) = p.strip_prefix("dep_") { - if let Ok(u) = Uuid::parse_str(dep_str) { - return Some(u); - } - // (un-parseable dep_.. properties are ignored) - } - None - }) - } - - /// Get task's property value by name. - pub fn get_value>(&self, property: S) -> Option<&str> { - let property = property.into(); - self.taskmap.get(&property).map(|s| s.as_ref()) - } - - // -- utility functions - - fn is_known_key(key: &str) -> bool { - Prop::from_str(key).is_ok() - || key.starts_with("tag_") - || key.starts_with("annotation_") - || key.starts_with("dep_") - } - - fn get_timestamp(&self, property: &str) -> Option { - if let Some(ts) = self.taskmap.get(property) { - if let Ok(ts) = ts.parse() { - return Some(utc_timestamp(ts)); - } - // if the value does not parse as an integer, default to None - } - None - } -} - -impl<'r> TaskMut<'r> { - /// Get the immutable version of this object, ending the exclusive reference to the Replica. - pub fn into_immut(self) -> Task { - self.task - } - - /// Set the task's status. This also adds the task to the working set if the - /// new status puts it in that set. - pub fn set_status(&mut self, status: Status) -> Result<()> { - match status { - Status::Pending | Status::Recurring => { - // clear "end" when a task becomes "pending" or "recurring" - if self.taskmap.contains_key(Prop::End.as_ref()) { - self.set_timestamp(Prop::End.as_ref(), None)?; - } - // ..and add to working set - self.replica.add_to_working_set(self.uuid)?; - } - Status::Completed | Status::Deleted => { - // set "end" when a task is deleted or completed - if !self.taskmap.contains_key(Prop::End.as_ref()) { - self.set_timestamp(Prop::End.as_ref(), Some(Utc::now()))?; - } - } - _ => {} - } - self.set_string( - Prop::Status.as_ref(), - Some(String::from(status.to_taskmap())), - ) - } - - pub fn set_description(&mut self, description: String) -> Result<()> { - self.set_string(Prop::Description.as_ref(), Some(description)) - } - - pub fn set_priority(&mut self, priority: String) -> Result<()> { - self.set_string(Prop::Priority.as_ref(), Some(priority)) - } - - pub fn set_entry(&mut self, entry: Option) -> Result<()> { - self.set_timestamp(Prop::Entry.as_ref(), entry) - } - - pub fn set_wait(&mut self, wait: Option) -> Result<()> { - self.set_timestamp(Prop::Wait.as_ref(), wait) - } - - pub fn set_modified(&mut self, modified: Timestamp) -> Result<()> { - self.set_timestamp(Prop::Modified.as_ref(), Some(modified)) - } - - /// Set a tasks's property by name. - /// - /// This will not automatically update the `modified` timestamp or perform any other - /// "automatic" operations -- it simply sets the property. Howerver, if property is - /// "modified", then subsequent calls to other `set_..` methods will not update the - /// `modified` timestamp. - pub fn set_value>(&mut self, property: S, value: Option) -> Result<()> { - let property = property.into(); - - if &property == "modified" { - self.updated_modified = true; - } - - if let Some(ref v) = value { - trace!("task {}: set property {}={:?}", self.task.uuid, property, v); - } else { - trace!("task {}: remove property {}", self.task.uuid, property); - } - - self.task.taskmap = self - .replica - .update_task(self.task.uuid, &property, value.as_ref())?; - - Ok(()) - } - - /// Start the task by creating "start": "", if the task is not already - /// active. - pub fn start(&mut self) -> Result<()> { - if self.is_active() { - return Ok(()); - } - self.set_timestamp(Prop::Start.as_ref(), Some(Utc::now())) - } - - /// Stop the task by removing the `start` key - pub fn stop(&mut self) -> Result<()> { - self.set_timestamp(Prop::Start.as_ref(), None) - } - - /// Mark this task as complete - pub fn done(&mut self) -> Result<()> { - self.set_status(Status::Completed) - } - - /// Mark this task as deleted. - /// - /// Note that this does not delete the task. It merely marks the task as - /// deleted. - pub fn delete(&mut self) -> Result<()> { - self.set_status(Status::Deleted) - } - - /// Add a tag to this task. Does nothing if the tag is already present. - pub fn add_tag(&mut self, tag: &Tag) -> Result<()> { - if tag.is_synthetic() { - return Err(Error::Usage(String::from( - "Synthetic tags cannot be modified", - ))); - } - self.set_string(format!("tag_{}", tag), Some("".to_owned())) - } - - /// Remove a tag from this task. Does nothing if the tag is not present. - pub fn remove_tag(&mut self, tag: &Tag) -> Result<()> { - if tag.is_synthetic() { - return Err(Error::Usage(String::from( - "Synthetic tags cannot be modified", - ))); - } - self.set_string(format!("tag_{}", tag), None) - } - - /// Add a new annotation. Note that annotations with the same entry time - /// will overwrite one another. - pub fn add_annotation(&mut self, ann: Annotation) -> Result<()> { - self.set_string( - format!("annotation_{}", ann.entry.timestamp()), - Some(ann.description), - ) - } - - /// Remove an annotation, based on its entry time. - pub fn remove_annotation(&mut self, entry: Timestamp) -> Result<()> { - self.set_string(format!("annotation_{}", entry.timestamp()), None) - } - - pub fn set_due(&mut self, due: Option) -> Result<()> { - self.set_timestamp(Prop::Due.as_ref(), due) - } - - /// Set a user-defined attribute (UDA). This will fail if the key is defined by the data - /// model. - pub fn set_uda( - &mut self, - namespace: impl AsRef, - key: impl AsRef, - value: impl Into, - ) -> Result<()> { - let key = uda_tuple_to_string(namespace, key); - self.set_legacy_uda(key, value) - } - - /// Remove a user-defined attribute (UDA). This will fail if the key is defined by the data - /// model. - pub fn remove_uda(&mut self, namespace: impl AsRef, key: impl AsRef) -> Result<()> { - let key = uda_tuple_to_string(namespace, key); - self.remove_legacy_uda(key) - } - - /// Set a user-defined attribute (UDA), where the key is a legacy key. - pub fn set_legacy_uda( - &mut self, - key: impl Into, - value: impl Into, - ) -> Result<()> { - let key = key.into(); - if Task::is_known_key(&key) { - return Err(Error::Usage(format!( - "Property name {} as special meaning in a task and cannot be used as a UDA", - key - ))); - } - self.set_string(key, Some(value.into())) - } - - /// Remove a user-defined attribute (UDA), where the key is a legacy key. - pub fn remove_legacy_uda(&mut self, key: impl Into) -> Result<()> { - let key = key.into(); - if Task::is_known_key(&key) { - return Err(Error::Usage(format!( - "Property name {} as special meaning in a task and cannot be used as a UDA", - key - ))); - } - self.set_string(key, None) - } - - /// Add a dependency. - pub fn add_dependency(&mut self, dep: Uuid) -> Result<()> { - let key = format!("dep_{}", dep); - self.set_string(key, Some("".to_string())) - } - - /// Remove a dependency. - pub fn remove_dependency(&mut self, dep: Uuid) -> Result<()> { - let key = format!("dep_{}", dep); - self.set_string(key, None) - } - - // -- utility functions - - fn update_modified(&mut self) -> Result<()> { - if !self.updated_modified { - let now = format!("{}", Utc::now().timestamp()); - trace!("task {}: set property modified={:?}", self.task.uuid, now); - self.task.taskmap = - self.replica - .update_task(self.task.uuid, Prop::Modified.as_ref(), Some(now))?; - self.updated_modified = true; - } - Ok(()) - } - - fn set_string>(&mut self, property: S, value: Option) -> Result<()> { - let property = property.into(); - // update the modified timestamp unless we are setting it explicitly - if &property != "modified" { - self.update_modified()?; - } - - self.set_value(property, value) - } - - fn set_timestamp(&mut self, property: &str, value: Option) -> Result<()> { - self.set_string(property, value.map(|v| v.timestamp().to_string())) - } - - /// Used by tests to ensure that updates are properly written - #[cfg(test)] - fn reload(&mut self) -> Result<()> { - let uuid = self.uuid; - let task = self.replica.get_task(uuid)?.unwrap(); - self.task.taskmap = task.taskmap; - Ok(()) - } -} - -impl<'r> std::ops::Deref for TaskMut<'r> { - type Target = Task; - - fn deref(&self) -> &Self::Target { - &self.task - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - use std::collections::HashSet; - - fn dm() -> Rc { - Rc::new(DependencyMap::new()) - } - - fn with_mut_task(f: F) { - let mut replica = Replica::new_inmemory(); - let task = replica.new_task(Status::Pending, "test".into()).unwrap(); - let task = task.into_mut(&mut replica); - f(task) - } - - /// Create a user tag, without checking its validity - fn utag(name: &'static str) -> Tag { - Tag::from_inner(TagInner::User(name.into())) - } - - /// Create a synthetic tag - fn stag(synth: SyntheticTag) -> Tag { - Tag::from_inner(TagInner::Synthetic(synth)) - } - - #[test] - fn test_is_active_never_started() { - let task = Task::new(Uuid::new_v4(), TaskMap::new(), dm()); - assert!(!task.is_active()); - } - - #[test] - fn test_is_active_active() { - let task = Task::new( - Uuid::new_v4(), - vec![(String::from("start"), String::from("1234"))] - .drain(..) - .collect(), - dm(), - ); - - assert!(task.is_active()); - } - - #[test] - fn test_is_active_inactive() { - let task = Task::new(Uuid::new_v4(), Default::default(), dm()); - assert!(!task.is_active()); - } - - #[test] - fn test_entry_not_set() { - let task = Task::new(Uuid::new_v4(), TaskMap::new(), dm()); - assert_eq!(task.get_entry(), None); - } - - #[test] - fn test_entry_set() { - let ts = Utc.ymd(1980, 1, 1).and_hms(0, 0, 0); - let task = Task::new( - Uuid::new_v4(), - vec![(String::from("entry"), format!("{}", ts.timestamp()))] - .drain(..) - .collect(), - dm(), - ); - assert_eq!(task.get_entry(), Some(ts)); - } - - #[test] - fn test_wait_not_set() { - let task = Task::new(Uuid::new_v4(), TaskMap::new(), dm()); - - assert!(!task.is_waiting()); - assert_eq!(task.get_wait(), None); - } - - #[test] - fn test_wait_in_past() { - let ts = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); - let task = Task::new( - Uuid::new_v4(), - vec![(String::from("wait"), format!("{}", ts.timestamp()))] - .drain(..) - .collect(), - dm(), - ); - - assert!(!task.is_waiting()); - assert_eq!(task.get_wait(), Some(ts)); - } - - #[test] - fn test_wait_in_future() { - let ts = Utc.ymd(3000, 1, 1).and_hms(0, 0, 0); - let task = Task::new( - Uuid::new_v4(), - vec![(String::from("wait"), format!("{}", ts.timestamp()))] - .drain(..) - .collect(), - dm(), - ); - - assert!(task.is_waiting()); - assert_eq!(task.get_wait(), Some(ts)); - } - - #[test] - fn test_has_tag() { - let task = Task::new( - Uuid::new_v4(), - vec![ - (String::from("tag_abc"), String::from("")), - (String::from("start"), String::from("1234")), - ] - .drain(..) - .collect(), - dm(), - ); - - assert!(task.has_tag(&utag("abc"))); - assert!(!task.has_tag(&utag("def"))); - assert!(task.has_tag(&stag(SyntheticTag::Active))); - assert!(task.has_tag(&stag(SyntheticTag::Pending))); - assert!(!task.has_tag(&stag(SyntheticTag::Waiting))); - } - - #[test] - fn test_get_tags() { - let task = Task::new( - Uuid::new_v4(), - vec![ - (String::from("tag_abc"), String::from("")), - (String::from("tag_def"), String::from("")), - // set `wait` so the synthetic tag WAITING is present - (String::from("wait"), String::from("33158909732")), - ] - .drain(..) - .collect(), - dm(), - ); - - let tags: HashSet<_> = task.get_tags().collect(); - let exp = set![ - utag("abc"), - utag("def"), - stag(SyntheticTag::Pending), - stag(SyntheticTag::Waiting), - stag(SyntheticTag::Unblocked), - ]; - assert_eq!(tags, exp); - } - - #[test] - fn test_get_tags_invalid_tags() { - let task = Task::new( - Uuid::new_v4(), - vec![ - (String::from("tag_ok"), String::from("")), - (String::from("tag_"), String::from("")), - (String::from("tag_123"), String::from("")), - (String::from("tag_a!!"), String::from("")), - ] - .drain(..) - .collect(), - dm(), - ); - - // only "ok" is OK - let tags: HashSet<_> = task.get_tags().collect(); - assert_eq!( - tags, - set![ - utag("ok"), - stag(SyntheticTag::Pending), - stag(SyntheticTag::Unblocked) - ] - ); - } - - #[test] - fn test_get_due() { - let test_time = Utc.ymd(2033, 1, 1).and_hms(0, 0, 0); - let task = Task::new( - Uuid::new_v4(), - vec![(String::from("due"), format!("{}", test_time.timestamp()))] - .drain(..) - .collect(), - dm(), - ); - assert_eq!(task.get_due(), Some(test_time)) - } - - #[test] - fn test_get_invalid_due() { - let task = Task::new( - Uuid::new_v4(), - vec![(String::from("due"), String::from("invalid"))] - .drain(..) - .collect(), - dm(), - ); - assert_eq!(task.get_due(), None); - } - - #[test] - fn test_add_due() { - let test_time = Utc.ymd(2033, 1, 1).and_hms(0, 0, 0); - with_mut_task(|mut task| { - assert_eq!(task.get_due(), None); - task.set_due(Some(test_time)).unwrap(); - assert_eq!(task.get_due(), Some(test_time)) - }); - } - - #[test] - fn test_remove_due() { - let test_time = Utc.ymd(2033, 1, 1).and_hms(0, 0, 0); - with_mut_task(|mut task| { - task.set_due(Some(test_time)).unwrap(); - task.reload().unwrap(); - assert!(task.taskmap.contains_key("due")); - task.set_due(None).unwrap(); - assert!(!task.taskmap.contains_key("due")); - }); - } - - #[test] - fn test_get_priority_default() { - let task = Task::new(Uuid::new_v4(), TaskMap::new(), dm()); - assert_eq!(task.get_priority(), ""); - } - - #[test] - fn test_get_annotations() { - let task = Task::new( - Uuid::new_v4(), - vec![ - ( - String::from("annotation_1635301873"), - String::from("left message"), - ), - ( - String::from("annotation_1635301883"), - String::from("left another message"), - ), - (String::from("annotation_"), String::from("invalid")), - (String::from("annotation_abcde"), String::from("invalid")), - ] - .drain(..) - .collect(), - dm(), - ); - - let mut anns: Vec<_> = task.get_annotations().collect(); - anns.sort(); - assert_eq!( - anns, - vec![ - Annotation { - entry: Utc.timestamp(1635301873, 0), - description: "left message".into() - }, - Annotation { - entry: Utc.timestamp(1635301883, 0), - description: "left another message".into() - } - ] - ); - } - - #[test] - fn test_add_annotation() { - with_mut_task(|mut task| { - task.add_annotation(Annotation { - entry: Utc.timestamp(1635301900, 0), - description: "right message".into(), - }) - .unwrap(); - let k = "annotation_1635301900"; - assert_eq!(task.taskmap[k], "right message".to_owned()); - task.reload().unwrap(); - assert_eq!(task.taskmap[k], "right message".to_owned()); - // adding with same time overwrites.. - task.add_annotation(Annotation { - entry: Utc.timestamp(1635301900, 0), - description: "right message 2".into(), - }) - .unwrap(); - assert_eq!(task.taskmap[k], "right message 2".to_owned()); - }); - } - - #[test] - fn test_remove_annotation() { - with_mut_task(|mut task| { - task.set_string("annotation_1635301873", Some("left message".into())) - .unwrap(); - task.set_string("annotation_1635301883", Some("left another message".into())) - .unwrap(); - - task.remove_annotation(Utc.timestamp(1635301873, 0)) - .unwrap(); - - task.reload().unwrap(); - - let mut anns: Vec<_> = task.get_annotations().collect(); - anns.sort(); - assert_eq!( - anns, - vec![Annotation { - entry: Utc.timestamp(1635301883, 0), - description: "left another message".into() - }] - ); - }); - } - - #[test] - fn test_set_get_priority() { - with_mut_task(|mut task| { - assert_eq!(task.get_priority(), ""); - task.set_priority("H".into()).unwrap(); - assert_eq!(task.get_priority(), "H"); - }); - } - - #[test] - fn test_set_status_pending() { - with_mut_task(|mut task| { - task.done().unwrap(); - - task.set_status(Status::Pending).unwrap(); - assert_eq!(task.get_status(), Status::Pending); - assert!(!task.taskmap.contains_key("end")); - assert!(task.has_tag(&stag(SyntheticTag::Pending))); - assert!(!task.has_tag(&stag(SyntheticTag::Completed))); - }); - } - - #[test] - fn test_set_status_recurring() { - with_mut_task(|mut task| { - task.done().unwrap(); - - task.set_status(Status::Recurring).unwrap(); - assert_eq!(task.get_status(), Status::Recurring); - assert!(!task.taskmap.contains_key("end")); - assert!(!task.has_tag(&stag(SyntheticTag::Pending))); // recurring is not +PENDING - assert!(!task.has_tag(&stag(SyntheticTag::Completed))); - }); - } - - #[test] - fn test_set_status_completed() { - with_mut_task(|mut task| { - task.set_status(Status::Completed).unwrap(); - assert_eq!(task.get_status(), Status::Completed); - assert!(task.taskmap.contains_key("end")); - assert!(!task.has_tag(&stag(SyntheticTag::Pending))); - assert!(task.has_tag(&stag(SyntheticTag::Completed))); - }); - } - - #[test] - fn test_set_status_deleted() { - with_mut_task(|mut task| { - task.set_status(Status::Deleted).unwrap(); - assert_eq!(task.get_status(), Status::Deleted); - assert!(task.taskmap.contains_key("end")); - assert!(!task.has_tag(&stag(SyntheticTag::Pending))); - assert!(!task.has_tag(&stag(SyntheticTag::Completed))); - }); - } - - #[test] - fn test_set_get_value() { - with_mut_task(|mut task| { - let property = "property-name"; - task.set_value(property, Some("value".into())).unwrap(); - assert_eq!(task.get_value(property), Some("value")); - task.set_value(property, None).unwrap(); - assert_eq!(task.get_value(property), None); - }); - } - - #[test] - fn test_start() { - with_mut_task(|mut task| { - task.start().unwrap(); - assert!(task.taskmap.contains_key("start")); - - task.reload().unwrap(); - assert!(task.taskmap.contains_key("start")); - - // second start doesn't change anything.. - task.start().unwrap(); - assert!(task.taskmap.contains_key("start")); - - task.reload().unwrap(); - assert!(task.taskmap.contains_key("start")); - }); - } - - #[test] - fn test_stop() { - with_mut_task(|mut task| { - task.start().unwrap(); - task.stop().unwrap(); - assert!(!task.taskmap.contains_key("start")); - - task.reload().unwrap(); - assert!(!task.taskmap.contains_key("start")); - - // redundant call does nothing.. - task.stop().unwrap(); - assert!(!task.taskmap.contains_key("start")); - - task.reload().unwrap(); - assert!(!task.taskmap.contains_key("start")); - }); - } - - #[test] - fn test_done() { - with_mut_task(|mut task| { - task.done().unwrap(); - assert_eq!(task.get_status(), Status::Completed); - assert!(task.taskmap.contains_key("end")); - assert!(task.has_tag(&stag(SyntheticTag::Completed))); - - // redundant call does nothing.. - task.done().unwrap(); - assert_eq!(task.get_status(), Status::Completed); - assert!(task.has_tag(&stag(SyntheticTag::Completed))); - }); - } - - #[test] - fn test_delete() { - with_mut_task(|mut task| { - task.delete().unwrap(); - assert_eq!(task.get_status(), Status::Deleted); - assert!(task.taskmap.contains_key("end")); - assert!(!task.has_tag(&stag(SyntheticTag::Completed))); - - // redundant call does nothing.. - task.delete().unwrap(); - assert_eq!(task.get_status(), Status::Deleted); - assert!(!task.has_tag(&stag(SyntheticTag::Completed))); - }); - } - - #[test] - fn test_add_tags() { - with_mut_task(|mut task| { - task.add_tag(&utag("abc")).unwrap(); - assert!(task.taskmap.contains_key("tag_abc")); - task.reload().unwrap(); - assert!(task.taskmap.contains_key("tag_abc")); - // redundant add has no effect.. - task.add_tag(&utag("abc")).unwrap(); - assert!(task.taskmap.contains_key("tag_abc")); - }); - } - - #[test] - fn test_remove_tags() { - with_mut_task(|mut task| { - task.add_tag(&utag("abc")).unwrap(); - task.reload().unwrap(); - assert!(task.taskmap.contains_key("tag_abc")); - - task.remove_tag(&utag("abc")).unwrap(); - assert!(!task.taskmap.contains_key("tag_abc")); - // redundant remove has no effect.. - task.remove_tag(&utag("abc")).unwrap(); - assert!(!task.taskmap.contains_key("tag_abc")); - }); - } - - #[test] - fn test_get_udas() { - let task = Task::new( - Uuid::new_v4(), - vec![ - ("description".into(), "not a uda".into()), - ("modified".into(), "not a uda".into()), - ("start".into(), "not a uda".into()), - ("status".into(), "not a uda".into()), - ("wait".into(), "not a uda".into()), - ("start".into(), "not a uda".into()), - ("tag_abc".into(), "not a uda".into()), - ("dep_1234".into(), "not a uda".into()), - ("annotation_1234".into(), "not a uda".into()), - ("githubid".into(), "123".into()), - ("jira.url".into(), "h://x".into()), - ] - .drain(..) - .collect(), - dm(), - ); - - let mut udas: Vec<_> = task.get_udas().collect(); - udas.sort_unstable(); - assert_eq!( - udas, - vec![(("", "githubid"), "123"), (("jira", "url"), "h://x")] - ); - } - - #[test] - fn test_get_uda() { - let task = Task::new( - Uuid::new_v4(), - vec![ - ("description".into(), "not a uda".into()), - ("githubid".into(), "123".into()), - ("jira.url".into(), "h://x".into()), - ] - .drain(..) - .collect(), - dm(), - ); - - assert_eq!(task.get_uda("", "description"), None); // invalid UDA - assert_eq!(task.get_uda("", "githubid"), Some("123")); - assert_eq!(task.get_uda("jira", "url"), Some("h://x")); - assert_eq!(task.get_uda("bugzilla", "url"), None); - } - - #[test] - fn test_get_legacy_uda() { - let task = Task::new( - Uuid::new_v4(), - vec![ - ("description".into(), "not a uda".into()), - ("dep_1234".into(), "not a uda".into()), - ("githubid".into(), "123".into()), - ("jira.url".into(), "h://x".into()), - ] - .drain(..) - .collect(), - dm(), - ); - - assert_eq!(task.get_legacy_uda("description"), None); // invalid UDA - assert_eq!(task.get_legacy_uda("dep_1234"), None); // invalid UDA - assert_eq!(task.get_legacy_uda("githubid"), Some("123")); - assert_eq!(task.get_legacy_uda("jira.url"), Some("h://x")); - assert_eq!(task.get_legacy_uda("bugzilla.url"), None); - } - - #[test] - fn test_set_uda() { - with_mut_task(|mut task| { - task.set_uda("jira", "url", "h://y").unwrap(); - let udas: Vec<_> = task.get_udas().collect(); - assert_eq!(udas, vec![(("jira", "url"), "h://y")]); - - task.set_uda("", "jiraid", "TW-1234").unwrap(); - - let mut udas: Vec<_> = task.get_udas().collect(); - udas.sort_unstable(); - assert_eq!( - udas, - vec![(("", "jiraid"), "TW-1234"), (("jira", "url"), "h://y")] - ); - }) - } - - #[test] - fn test_set_legacy_uda() { - with_mut_task(|mut task| { - task.set_legacy_uda("jira.url", "h://y").unwrap(); - let udas: Vec<_> = task.get_udas().collect(); - assert_eq!(udas, vec![(("jira", "url"), "h://y")]); - - task.set_legacy_uda("jiraid", "TW-1234").unwrap(); - - let mut udas: Vec<_> = task.get_udas().collect(); - udas.sort_unstable(); - assert_eq!( - udas, - vec![(("", "jiraid"), "TW-1234"), (("jira", "url"), "h://y")] - ); - }) - } - - #[test] - fn test_set_uda_invalid() { - with_mut_task(|mut task| { - assert!(task.set_uda("", "modified", "123").is_err()); - assert!(task.set_uda("", "tag_abc", "123").is_err()); - assert!(task.set_legacy_uda("modified", "123").is_err()); - assert!(task.set_legacy_uda("tag_abc", "123").is_err()); - }) - } - - #[test] - fn test_remove_uda() { - with_mut_task(|mut task| { - task.set_string("github.id", Some("123".into())).unwrap(); - task.remove_uda("github", "id").unwrap(); - - let udas: Vec<_> = task.get_udas().collect(); - assert_eq!(udas, vec![]); - }) - } - - #[test] - fn test_remove_legacy_uda() { - with_mut_task(|mut task| { - task.set_string("githubid", Some("123".into())).unwrap(); - task.remove_legacy_uda("githubid").unwrap(); - - let udas: Vec<_> = task.get_udas().collect(); - assert_eq!(udas, vec![]); - }) - } - - #[test] - fn test_remove_uda_invalid() { - with_mut_task(|mut task| { - assert!(task.remove_uda("", "modified").is_err()); - assert!(task.remove_uda("", "tag_abc").is_err()); - assert!(task.remove_legacy_uda("modified").is_err()); - assert!(task.remove_legacy_uda("tag_abc").is_err()); - }) - } - - #[test] - fn test_dependencies() { - with_mut_task(|mut task| { - assert_eq!(task.get_dependencies().collect::>(), vec![]); - let dep1 = Uuid::new_v4(); - let dep2 = Uuid::new_v4(); - - task.add_dependency(dep1).unwrap(); - assert_eq!(task.get_dependencies().collect::>(), vec![dep1]); - - task.add_dependency(dep1).unwrap(); // add twice is ok - task.add_dependency(dep2).unwrap(); - let deps = task.get_dependencies().collect::>(); - assert!(deps.contains(&dep1)); - assert!(deps.contains(&dep2)); - - task.remove_dependency(dep1).unwrap(); - assert_eq!(task.get_dependencies().collect::>(), vec![dep2]); - }) - } - - #[test] - fn dependencies_tags() { - let mut rep = Replica::new_inmemory(); - let uuid1; - let uuid2; - { - let t1 = rep.new_task(Status::Pending, "1".into()).unwrap(); - uuid1 = t1.get_uuid(); - let t2 = rep.new_task(Status::Pending, "2".into()).unwrap(); - uuid2 = t2.get_uuid(); - - let mut t1 = t1.into_mut(&mut rep); - t1.add_dependency(t2.get_uuid()).unwrap(); - } - - // force-refresh depmap - rep.dependency_map(true).unwrap(); - - let t1 = rep.get_task(uuid1).unwrap().unwrap(); - let t2 = rep.get_task(uuid2).unwrap().unwrap(); - assert!(t1.has_tag(&stag(SyntheticTag::Blocked))); - assert!(!t1.has_tag(&stag(SyntheticTag::Unblocked))); - assert!(!t1.has_tag(&stag(SyntheticTag::Blocking))); - assert!(!t2.has_tag(&stag(SyntheticTag::Blocked))); - assert!(t2.has_tag(&stag(SyntheticTag::Unblocked))); - assert!(t2.has_tag(&stag(SyntheticTag::Blocking))); - } - - #[test] - fn set_value_modified() { - with_mut_task(|mut task| { - // set the modified property to something in the past.. - task.set_value("modified", Some("1671820000".into())) - .unwrap(); - // update another property - task.set_description("fun times".into()).unwrap(); - // verify the modified property was not updated - assert_eq!(task.get_value("modified").unwrap(), "1671820000") - }) - } -} diff --git a/taskchampion/taskchampion/src/task/time.rs b/taskchampion/taskchampion/src/task/time.rs deleted file mode 100644 index 928da2a6a..000000000 --- a/taskchampion/taskchampion/src/task/time.rs +++ /dev/null @@ -1,11 +0,0 @@ -use chrono::{offset::LocalResult, DateTime, TimeZone, Utc}; - -pub type Timestamp = DateTime; - -pub fn utc_timestamp(secs: i64) -> Timestamp { - match Utc.timestamp_opt(secs, 0) { - LocalResult::Single(tz) => tz, - // The other two variants are None and Ambiguous, which both are caused by DST. - _ => unreachable!("We're requesting UTC so daylight saving time isn't a factor."), - } -} diff --git a/taskchampion/taskchampion/src/taskdb/apply.rs b/taskchampion/taskchampion/src/taskdb/apply.rs deleted file mode 100644 index 0cea9f28c..000000000 --- a/taskchampion/taskchampion/src/taskdb/apply.rs +++ /dev/null @@ -1,406 +0,0 @@ -use crate::errors::{Error, Result}; -use crate::server::SyncOp; -use crate::storage::{ReplicaOp, StorageTxn, TaskMap}; - -/// Apply the given SyncOp to the replica, updating both the task data and adding a -/// ReplicaOp to the list of operations. Returns the TaskMap of the task after the -/// operation has been applied (or an empty TaskMap for Delete). It is not an error -/// to create an existing task, nor to delete a nonexistent task. -pub(super) fn apply_and_record(txn: &mut dyn StorageTxn, op: SyncOp) -> Result { - match op { - SyncOp::Create { uuid } => { - let created = txn.create_task(uuid)?; - if created { - txn.add_operation(ReplicaOp::Create { uuid })?; - txn.commit()?; - Ok(TaskMap::new()) - } else { - Ok(txn - .get_task(uuid)? - .expect("create_task failed but task does not exist")) - } - } - SyncOp::Delete { uuid } => { - let task = txn.get_task(uuid)?; - if let Some(task) = task { - txn.delete_task(uuid)?; - txn.add_operation(ReplicaOp::Delete { - uuid, - old_task: task, - })?; - txn.commit()?; - Ok(TaskMap::new()) - } else { - Ok(TaskMap::new()) - } - } - SyncOp::Update { - uuid, - property, - value, - timestamp, - } => { - let task = txn.get_task(uuid)?; - if let Some(mut task) = task { - let old_value = task.get(&property).cloned(); - if let Some(ref v) = value { - task.insert(property.clone(), v.clone()); - } else { - task.remove(&property); - } - txn.set_task(uuid, task.clone())?; - txn.add_operation(ReplicaOp::Update { - uuid, - property, - old_value, - value, - timestamp, - })?; - txn.commit()?; - Ok(task) - } else { - Err(Error::Database(format!("Task {} does not exist", uuid))) - } - } - } -} - -/// Apply an op to the TaskDb's set of tasks (without recording it in the list of operations) -pub(super) fn apply_op(txn: &mut dyn StorageTxn, op: &SyncOp) -> Result<()> { - // TODO: test - // TODO: it'd be nice if this was integrated into apply() somehow, but that clones TaskMaps - // unnecessariliy - match op { - SyncOp::Create { uuid } => { - // insert if the task does not already exist - if !txn.create_task(*uuid)? { - return Err(Error::Database(format!("Task {} already exists", uuid))); - } - } - SyncOp::Delete { ref uuid } => { - if !txn.delete_task(*uuid)? { - return Err(Error::Database(format!("Task {} does not exist", uuid))); - } - } - SyncOp::Update { - ref uuid, - ref property, - ref value, - timestamp: _, - } => { - // update if this task exists, otherwise ignore - if let Some(mut task) = txn.get_task(*uuid)? { - match value { - Some(ref val) => task.insert(property.to_string(), val.clone()), - None => task.remove(property), - }; - txn.set_task(*uuid, task)?; - } else { - return Err(Error::Database(format!("Task {} does not exist", uuid))); - } - } - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::storage::TaskMap; - use crate::taskdb::TaskDb; - use chrono::Utc; - use pretty_assertions::assert_eq; - use std::collections::HashMap; - use uuid::Uuid; - - #[test] - fn test_apply_create() -> Result<()> { - let mut db = TaskDb::new_inmemory(); - let uuid = Uuid::new_v4(); - let op = SyncOp::Create { uuid }; - - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op)?; - assert_eq!(taskmap.len(), 0); - txn.commit()?; - } - - assert_eq!(db.sorted_tasks(), vec![(uuid, vec![]),]); - assert_eq!(db.operations(), vec![ReplicaOp::Create { uuid }]); - Ok(()) - } - - #[test] - fn test_apply_create_exists() -> Result<()> { - let mut db = TaskDb::new_inmemory(); - let uuid = Uuid::new_v4(); - { - let mut txn = db.storage.txn()?; - txn.create_task(uuid)?; - let mut taskmap = TaskMap::new(); - taskmap.insert("foo".into(), "bar".into()); - txn.set_task(uuid, taskmap)?; - txn.commit()?; - } - - let op = SyncOp::Create { uuid }; - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op)?; - - assert_eq!(taskmap.len(), 1); - assert_eq!(taskmap.get("foo").unwrap(), "bar"); - - txn.commit()?; - } - - // create did not delete the old task.. - assert_eq!( - db.sorted_tasks(), - vec![(uuid, vec![("foo".into(), "bar".into())])] - ); - // create was done "manually" above, and no new op was added - assert_eq!(db.operations(), vec![]); - Ok(()) - } - - #[test] - fn test_apply_create_update() -> Result<()> { - let mut db = TaskDb::new_inmemory(); - let uuid = Uuid::new_v4(); - let now = Utc::now(); - let op1 = SyncOp::Create { uuid }; - - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op1)?; - assert_eq!(taskmap.len(), 0); - txn.commit()?; - } - - let op2 = SyncOp::Update { - uuid, - property: String::from("title"), - value: Some("my task".into()), - timestamp: now, - }; - { - let mut txn = db.storage.txn()?; - let mut taskmap = apply_and_record(txn.as_mut(), op2)?; - assert_eq!( - taskmap.drain().collect::>(), - vec![("title".into(), "my task".into())] - ); - txn.commit()?; - } - - assert_eq!( - db.sorted_tasks(), - vec![(uuid, vec![("title".into(), "my task".into())])] - ); - assert_eq!( - db.operations(), - vec![ - ReplicaOp::Create { uuid }, - ReplicaOp::Update { - uuid, - property: "title".into(), - old_value: None, - value: Some("my task".into()), - timestamp: now - } - ] - ); - - Ok(()) - } - - #[test] - fn test_apply_create_update_delete_prop() -> Result<()> { - let mut db = TaskDb::new_inmemory(); - let uuid = Uuid::new_v4(); - let now = Utc::now(); - let op1 = SyncOp::Create { uuid }; - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op1)?; - assert_eq!(taskmap.len(), 0); - txn.commit()?; - } - - let op2 = SyncOp::Update { - uuid, - property: String::from("title"), - value: Some("my task".into()), - timestamp: now, - }; - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op2)?; - assert_eq!(taskmap.get("title"), Some(&"my task".to_owned())); - txn.commit()?; - } - - let op3 = SyncOp::Update { - uuid, - property: String::from("priority"), - value: Some("H".into()), - timestamp: now, - }; - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op3)?; - assert_eq!(taskmap.get("priority"), Some(&"H".to_owned())); - txn.commit()?; - } - - let op4 = SyncOp::Update { - uuid, - property: String::from("title"), - value: None, - timestamp: now, - }; - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op4)?; - assert_eq!(taskmap.get("title"), None); - assert_eq!(taskmap.get("priority"), Some(&"H".to_owned())); - txn.commit()?; - } - - let mut exp = HashMap::new(); - let mut task = HashMap::new(); - task.insert(String::from("priority"), String::from("H")); - exp.insert(uuid, task); - assert_eq!( - db.sorted_tasks(), - vec![(uuid, vec![("priority".into(), "H".into())])] - ); - assert_eq!( - db.operations(), - vec![ - ReplicaOp::Create { uuid }, - ReplicaOp::Update { - uuid, - property: "title".into(), - old_value: None, - value: Some("my task".into()), - timestamp: now, - }, - ReplicaOp::Update { - uuid, - property: "priority".into(), - old_value: None, - value: Some("H".into()), - timestamp: now, - }, - ReplicaOp::Update { - uuid, - property: "title".into(), - old_value: Some("my task".into()), - value: None, - timestamp: now, - } - ] - ); - - Ok(()) - } - - #[test] - fn test_apply_update_does_not_exist() -> Result<()> { - let mut db = TaskDb::new_inmemory(); - let uuid = Uuid::new_v4(); - let op = SyncOp::Update { - uuid, - property: String::from("title"), - value: Some("my task".into()), - timestamp: Utc::now(), - }; - { - let mut txn = db.storage.txn()?; - assert_eq!( - apply_and_record(txn.as_mut(), op) - .err() - .unwrap() - .to_string(), - format!("Task Database Error: Task {} does not exist", uuid) - ); - txn.commit()?; - } - - Ok(()) - } - - #[test] - fn test_apply_create_delete() -> Result<()> { - let mut db = TaskDb::new_inmemory(); - let uuid = Uuid::new_v4(); - let now = Utc::now(); - - let op1 = SyncOp::Create { uuid }; - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op1)?; - assert_eq!(taskmap.len(), 0); - } - - let op2 = SyncOp::Update { - uuid, - property: String::from("priority"), - value: Some("H".into()), - timestamp: now, - }; - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op2)?; - assert_eq!(taskmap.get("priority"), Some(&"H".to_owned())); - txn.commit()?; - } - - let op3 = SyncOp::Delete { uuid }; - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op3)?; - assert_eq!(taskmap.len(), 0); - txn.commit()?; - } - - assert_eq!(db.sorted_tasks(), vec![]); - let mut old_task = TaskMap::new(); - old_task.insert("priority".into(), "H".into()); - assert_eq!( - db.operations(), - vec![ - ReplicaOp::Create { uuid }, - ReplicaOp::Update { - uuid, - property: "priority".into(), - old_value: None, - value: Some("H".into()), - timestamp: now, - }, - ReplicaOp::Delete { uuid, old_task }, - ] - ); - - Ok(()) - } - - #[test] - fn test_apply_delete_not_present() -> Result<()> { - let mut db = TaskDb::new_inmemory(); - let uuid = Uuid::new_v4(); - let op = SyncOp::Delete { uuid }; - { - let mut txn = db.storage.txn()?; - let taskmap = apply_and_record(txn.as_mut(), op)?; - assert_eq!(taskmap.len(), 0); - txn.commit()?; - } - - Ok(()) - } -} diff --git a/taskchampion/taskchampion/src/taskdb/mod.rs b/taskchampion/taskchampion/src/taskdb/mod.rs deleted file mode 100644 index 6418f9279..000000000 --- a/taskchampion/taskchampion/src/taskdb/mod.rs +++ /dev/null @@ -1,297 +0,0 @@ -use crate::errors::Result; -use crate::server::{Server, SyncOp}; -use crate::storage::{ReplicaOp, Storage, TaskMap}; -use uuid::Uuid; - -mod apply; -mod snapshot; -mod sync; -pub mod undo; -mod working_set; - -/// A TaskDb is the backend for a replica. It manages the storage, operations, synchronization, -/// and so on, and all the invariants that come with it. It leaves the meaning of particular task -/// properties to the replica and task implementations. -pub struct TaskDb { - storage: Box, -} - -impl TaskDb { - /// Create a new TaskDb with the given backend storage - pub fn new(storage: Box) -> TaskDb { - TaskDb { storage } - } - - #[cfg(test)] - pub fn new_inmemory() -> TaskDb { - #[cfg(test)] - use crate::storage::InMemoryStorage; - - TaskDb::new(Box::new(InMemoryStorage::new())) - } - - /// Apply an operation to the TaskDb. This will update the set of tasks and add a ReplicaOp to - /// the set of operations in the TaskDb, and return the TaskMap containing the resulting task's - /// properties (or an empty TaskMap for deletion). - /// - /// Aside from synchronization operations, this is the only way to modify the TaskDb. In cases - /// where an operation does not make sense, this function will do nothing and return an error - /// (but leave the TaskDb in a consistent state). - pub fn apply(&mut self, op: SyncOp) -> Result { - let mut txn = self.storage.txn()?; - apply::apply_and_record(txn.as_mut(), op) - } - - /// Add an UndoPoint operation to the list of replica operations. - pub fn add_undo_point(&mut self) -> Result<()> { - let mut txn = self.storage.txn()?; - txn.add_operation(ReplicaOp::UndoPoint)?; - txn.commit() - } - - /// Get all tasks. - pub fn all_tasks(&mut self) -> Result> { - let mut txn = self.storage.txn()?; - txn.all_tasks() - } - - /// Get the UUIDs of all tasks - pub fn all_task_uuids(&mut self) -> Result> { - let mut txn = self.storage.txn()?; - txn.all_task_uuids() - } - - /// Get the working set - pub fn working_set(&mut self) -> Result>> { - let mut txn = self.storage.txn()?; - txn.get_working_set() - } - - /// Get a single task, by uuid. - pub fn get_task(&mut self, uuid: Uuid) -> Result> { - let mut txn = self.storage.txn()?; - txn.get_task(uuid) - } - - /// Rebuild the working set using a function to identify tasks that should be in the set. This - /// renumbers the existing working-set tasks to eliminate gaps, and also adds any tasks that - /// are not already in the working set but should be. The rebuild occurs in a single - /// trasnsaction against the storage backend. - pub fn rebuild_working_set(&mut self, in_working_set: F, renumber: bool) -> Result<()> - where - F: Fn(&TaskMap) -> bool, - { - working_set::rebuild(self.storage.txn()?.as_mut(), in_working_set, renumber) - } - - /// Add the given uuid to the working set and return its index; if it is already in the working - /// set, its index is returned. This does *not* renumber any existing tasks. - pub fn add_to_working_set(&mut self, uuid: Uuid) -> Result { - let mut txn = self.storage.txn()?; - // search for an existing entry for this task.. - for (i, elt) in txn.get_working_set()?.iter().enumerate() { - if *elt == Some(uuid) { - // (note that this drops the transaction with no changes made) - return Ok(i); - } - } - // and if not found, add one - let i = txn.add_to_working_set(uuid)?; - txn.commit()?; - Ok(i) - } - - /// Sync to the given server, pulling remote changes and pushing local changes. - /// - /// If `avoid_snapshots` is true, the sync operations produces a snapshot only when the server - /// indicate it is urgent (snapshot urgency "high"). This allows time for other replicas to - /// create a snapshot before this one does. - /// - /// Set this to true on systems more constrained in CPU, memory, or bandwidth than a typical desktop - /// system - pub fn sync(&mut self, server: &mut Box, avoid_snapshots: bool) -> Result<()> { - let mut txn = self.storage.txn()?; - sync::sync(server, txn.as_mut(), avoid_snapshots) - } - - /// Return undo local operations until the most recent UndoPoint, returning an empty Vec if there are no - /// local operations to undo. - pub fn get_undo_ops(&mut self) -> Result> { - let mut txn = self.storage.txn()?; - undo::get_undo_ops(txn.as_mut()) - } - - /// Undo local operations in storage, returning a boolean indicating success. - pub fn commit_undo_ops(&mut self, undo_ops: Vec) -> Result { - let mut txn = self.storage.txn()?; - undo::commit_undo_ops(txn.as_mut(), undo_ops) - } - - /// Get the number of un-synchronized operations in storage, excluding undo - /// operations. - pub fn num_operations(&mut self) -> Result { - let mut txn = self.storage.txn().unwrap(); - Ok(txn - .operations()? - .iter() - .filter(|o| !o.is_undo_point()) - .count()) - } - - /// Get the number of (un-synchronized) undo points in storage. - pub fn num_undo_points(&mut self) -> Result { - let mut txn = self.storage.txn().unwrap(); - Ok(txn - .operations()? - .iter() - .filter(|o| o.is_undo_point()) - .count()) - } - - // functions for supporting tests - - #[cfg(test)] - pub(crate) fn sorted_tasks(&mut self) -> Vec<(Uuid, Vec<(String, String)>)> { - let mut res: Vec<(Uuid, Vec<(String, String)>)> = self - .all_tasks() - .unwrap() - .iter() - .map(|(u, t)| { - let mut t = t - .iter() - .map(|(p, v)| (p.clone(), v.clone())) - .collect::>(); - t.sort(); - (*u, t) - }) - .collect(); - res.sort(); - res - } - - #[cfg(test)] - pub(crate) fn operations(&mut self) -> Vec { - let mut txn = self.storage.txn().unwrap(); - txn.operations().unwrap().to_vec() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::server::test::TestServer; - use crate::storage::{InMemoryStorage, ReplicaOp}; - use chrono::Utc; - use pretty_assertions::assert_eq; - use proptest::prelude::*; - use uuid::Uuid; - - #[test] - fn test_apply() { - // this verifies that the operation is both applied and included in the list of - // operations; more detailed tests are in the `apply` module. - let mut db = TaskDb::new_inmemory(); - let uuid = Uuid::new_v4(); - let op = SyncOp::Create { uuid }; - db.apply(op).unwrap(); - - assert_eq!(db.sorted_tasks(), vec![(uuid, vec![]),]); - assert_eq!(db.operations(), vec![ReplicaOp::Create { uuid }]); - } - - #[test] - fn test_add_undo_point() { - let mut db = TaskDb::new_inmemory(); - db.add_undo_point().unwrap(); - assert_eq!(db.operations(), vec![ReplicaOp::UndoPoint]); - } - - #[test] - fn test_num_operations() { - let mut db = TaskDb::new_inmemory(); - db.apply(SyncOp::Create { - uuid: Uuid::new_v4(), - }) - .unwrap(); - db.add_undo_point().unwrap(); - db.apply(SyncOp::Create { - uuid: Uuid::new_v4(), - }) - .unwrap(); - assert_eq!(db.num_operations().unwrap(), 2); - } - - #[test] - fn test_num_undo_points() { - let mut db = TaskDb::new_inmemory(); - db.add_undo_point().unwrap(); - assert_eq!(db.num_undo_points().unwrap(), 1); - db.add_undo_point().unwrap(); - assert_eq!(db.num_undo_points().unwrap(), 2); - } - - fn newdb() -> TaskDb { - TaskDb::new(Box::new(InMemoryStorage::new())) - } - - #[derive(Debug)] - enum Action { - Op(SyncOp), - Sync, - } - - fn action_sequence_strategy() -> impl Strategy> { - // Create, Update, Delete, or Sync on client 1, 2, .., followed by a round of syncs - "([CUDS][123])*S1S2S3S1S2".prop_map(|seq| { - let uuid = Uuid::parse_str("83a2f9ef-f455-4195-b92e-a54c161eebfc").unwrap(); - seq.as_bytes() - .chunks(2) - .map(|action_on| { - let action = match action_on[0] { - b'C' => Action::Op(SyncOp::Create { uuid }), - b'U' => Action::Op(SyncOp::Update { - uuid, - property: "title".into(), - value: Some("foo".into()), - timestamp: Utc::now(), - }), - b'D' => Action::Op(SyncOp::Delete { uuid }), - b'S' => Action::Sync, - _ => unreachable!(), - }; - let acton = action_on[1] - b'1'; - (action, acton) - }) - .collect::>() - }) - } - - proptest! { - #[test] - // check that various sequences of operations on mulitple db's do not get the db's into an - // incompatible state. The main concern here is that there might be a sequence of create - // and delete operations that results in a task existing in one TaskDb but not existing in - // another. So, the generated sequences focus on a single task UUID. - fn transform_sequences_of_operations(action_sequence in action_sequence_strategy()) { - let mut server: Box = Box::new(TestServer::new()); - let mut dbs = [newdb(), newdb(), newdb()]; - - for (action, db) in action_sequence { - println!("{:?} on db {}", action, db); - - let db = &mut dbs[db as usize]; - match action { - Action::Op(op) => { - if let Err(e) = db.apply(op) { - println!(" {:?} (ignored)", e); - } - }, - Action::Sync => db.sync(&mut server, false).unwrap(), - } - } - - assert_eq!(dbs[0].sorted_tasks(), dbs[0].sorted_tasks()); - assert_eq!(dbs[1].sorted_tasks(), dbs[2].sorted_tasks()); - } - } -} diff --git a/taskchampion/taskchampion/src/taskdb/snapshot.rs b/taskchampion/taskchampion/src/taskdb/snapshot.rs deleted file mode 100644 index 3bff2eb2a..000000000 --- a/taskchampion/taskchampion/src/taskdb/snapshot.rs +++ /dev/null @@ -1,181 +0,0 @@ -use crate::errors::{Error, Result}; -use crate::storage::{StorageTxn, TaskMap, VersionId}; -use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression}; -use serde::de::{Deserialize, Deserializer, MapAccess, Visitor}; -use serde::ser::{Serialize, SerializeMap, Serializer}; -use std::fmt; -use uuid::Uuid; - -/// A newtype to wrap the result of [`crate::storage::StorageTxn::all_tasks`] -pub(super) struct SnapshotTasks(Vec<(Uuid, TaskMap)>); - -impl Serialize for SnapshotTasks { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: Serializer, - { - let mut map = serializer.serialize_map(Some(self.0.len()))?; - for (k, v) in &self.0 { - map.serialize_entry(k, v)?; - } - map.end() - } -} - -struct TaskDbVisitor; - -impl<'de> Visitor<'de> for TaskDbVisitor { - type Value = SnapshotTasks; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a map representing a task snapshot") - } - - fn visit_map(self, mut access: M) -> std::result::Result - where - M: MapAccess<'de>, - { - let mut map = SnapshotTasks(Vec::with_capacity(access.size_hint().unwrap_or(0))); - - while let Some((key, value)) = access.next_entry()? { - map.0.push((key, value)); - } - - Ok(map) - } -} - -impl<'de> Deserialize<'de> for SnapshotTasks { - fn deserialize(deserializer: D) -> std::result::Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_map(TaskDbVisitor) - } -} - -impl SnapshotTasks { - pub(super) fn encode(&self) -> Result> { - let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); - serde_json::to_writer(&mut encoder, &self)?; - Ok(encoder.finish()?) - } - - pub(super) fn decode(snapshot: &[u8]) -> Result { - let decoder = ZlibDecoder::new(snapshot); - Ok(serde_json::from_reader(decoder)?) - } - - pub(super) fn into_inner(self) -> Vec<(Uuid, TaskMap)> { - self.0 - } -} - -/// Generate a snapshot (compressed, unencrypted) for the current state of the taskdb in the given -/// storage. -pub(super) fn make_snapshot(txn: &mut dyn StorageTxn) -> Result> { - let all_tasks = SnapshotTasks(txn.all_tasks()?); - all_tasks.encode() -} - -/// Apply the given snapshot (compressed, unencrypted) to the taskdb's storage. -pub(super) fn apply_snapshot( - txn: &mut dyn StorageTxn, - version: VersionId, - snapshot: &[u8], -) -> Result<()> { - let all_tasks = SnapshotTasks::decode(snapshot)?; - - // double-check emptiness - if !txn.is_empty()? { - return Err(Error::Database(String::from( - "Cannot apply snapshot to a non-empty task database", - ))); - } - - for (uuid, task) in all_tasks.into_inner().drain(..) { - txn.set_task(uuid, task)?; - } - txn.set_base_version(version)?; - - Ok(()) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::storage::{InMemoryStorage, Storage, TaskMap}; - use pretty_assertions::assert_eq; - - #[test] - fn test_serialize_empty() -> Result<()> { - let empty = SnapshotTasks(vec![]); - assert_eq!(serde_json::to_vec(&empty)?, b"{}".to_owned()); - Ok(()) - } - - #[test] - fn test_serialize_tasks() -> Result<()> { - let u = Uuid::new_v4(); - let m: TaskMap = vec![("description".to_owned(), "my task".to_owned())] - .drain(..) - .collect(); - let all_tasks = SnapshotTasks(vec![(u, m)]); - assert_eq!( - serde_json::to_vec(&all_tasks)?, - format!("{{\"{}\":{{\"description\":\"my task\"}}}}", u).into_bytes(), - ); - Ok(()) - } - - #[test] - fn test_round_trip() -> Result<()> { - let mut storage = InMemoryStorage::new(); - let version = Uuid::new_v4(); - - let task1 = ( - Uuid::new_v4(), - vec![("description".to_owned(), "one".to_owned())] - .drain(..) - .collect::(), - ); - let task2 = ( - Uuid::new_v4(), - vec![("description".to_owned(), "two".to_owned())] - .drain(..) - .collect::(), - ); - - { - let mut txn = storage.txn()?; - txn.set_task(task1.0, task1.1.clone())?; - txn.set_task(task2.0, task2.1.clone())?; - txn.commit()?; - } - - let snap = { - let mut txn = storage.txn()?; - make_snapshot(txn.as_mut())? - }; - - // apply that snapshot to a fresh bit of fake - let mut storage = InMemoryStorage::new(); - { - let mut txn = storage.txn()?; - apply_snapshot(txn.as_mut(), version, &snap)?; - txn.commit()? - } - - { - let mut txn = storage.txn()?; - assert_eq!(txn.get_task(task1.0)?, Some(task1.1)); - assert_eq!(txn.get_task(task2.0)?, Some(task2.1)); - assert_eq!(txn.all_tasks()?.len(), 2); - assert_eq!(txn.base_version()?, version); - assert_eq!(txn.operations()?.len(), 0); - assert_eq!(txn.get_working_set()?.len(), 1); - } - - Ok(()) - } -} diff --git a/taskchampion/taskchampion/src/taskdb/sync.rs b/taskchampion/taskchampion/src/taskdb/sync.rs deleted file mode 100644 index 95e2938c4..000000000 --- a/taskchampion/taskchampion/src/taskdb/sync.rs +++ /dev/null @@ -1,386 +0,0 @@ -use super::{apply, snapshot}; -use crate::errors::Result; -use crate::server::{AddVersionResult, GetVersionResult, Server, SnapshotUrgency, SyncOp}; -use crate::storage::StorageTxn; -use crate::Error; -use log::{info, trace, warn}; -use serde::{Deserialize, Serialize}; -use std::str; - -#[derive(Serialize, Deserialize, Debug)] -struct Version { - operations: Vec, -} - -/// Sync to the given server, pulling remote changes and pushing local changes. -pub(super) fn sync( - server: &mut Box, - txn: &mut dyn StorageTxn, - avoid_snapshots: bool, -) -> Result<()> { - // if this taskdb is entirely empty, then start by getting and applying a snapshot - if txn.is_empty()? { - trace!("storage is empty; attempting to apply a snapshot"); - if let Some((version, snap)) = server.get_snapshot()? { - snapshot::apply_snapshot(txn, version, snap.as_ref())?; - trace!("applied snapshot for version {}", version); - } - } - - // retry synchronizing until the server accepts our version (this allows for races between - // replicas trying to sync to the same server). If the server insists on the same base - // version twice, then we have diverged. - let mut requested_parent_version_id = None; - loop { - trace!("beginning sync outer loop"); - let mut base_version_id = txn.base_version()?; - - let mut local_ops: Vec = txn - .operations()? - .drain(..) - .filter_map(|op| op.into_sync()) - .collect(); - - // first pull changes and "rebase" on top of them - loop { - trace!("beginning sync inner loop"); - if let GetVersionResult::Version { - version_id, - history_segment, - .. - } = server.get_child_version(base_version_id)? - { - let version_str = str::from_utf8(&history_segment).unwrap(); - let version: Version = serde_json::from_str(version_str).unwrap(); - - // apply this verison and update base_version in storage - info!("applying version {:?} from server", version_id); - apply_version(txn, &mut local_ops, version)?; - txn.set_base_version(version_id)?; - base_version_id = version_id; - } else { - info!("no child versions of {:?}", base_version_id); - // at the moment, no more child versions, so we can try adding our own - break; - } - } - - if local_ops.is_empty() { - info!("no changes to push to server"); - // nothing to sync back to the server.. - break; - } - - trace!("sending {} operations to the server", local_ops.len()); - - // now make a version of our local changes and push those - let new_version = Version { - operations: local_ops, - }; - let history_segment = serde_json::to_string(&new_version).unwrap().into(); - info!("sending new version to server"); - let (res, snapshot_urgency) = server.add_version(base_version_id, history_segment)?; - match res { - AddVersionResult::Ok(new_version_id) => { - info!("version {:?} received by server", new_version_id); - txn.set_base_version(new_version_id)?; - - // make a snapshot if the server indicates it is urgent enough - let base_urgency = if avoid_snapshots { - SnapshotUrgency::High - } else { - SnapshotUrgency::Low - }; - if snapshot_urgency >= base_urgency { - let snapshot = snapshot::make_snapshot(txn)?; - server.add_snapshot(new_version_id, snapshot)?; - } - - break; - } - AddVersionResult::ExpectedParentVersion(parent_version_id) => { - info!( - "new version rejected; must be based on {:?}", - parent_version_id - ); - if let Some(requested) = requested_parent_version_id { - if parent_version_id == requested { - return Err(Error::OutOfSync); - } - } - requested_parent_version_id = Some(parent_version_id); - } - } - } - - txn.set_operations(vec![])?; - txn.commit()?; - Ok(()) -} - -fn apply_version( - txn: &mut dyn StorageTxn, - local_ops: &mut Vec, - mut version: Version, -) -> Result<()> { - // The situation here is that the server has already applied all server operations, and we - // have already applied all local operations, so states have diverged by several - // operations. We need to figure out what operations to apply locally and on the server in - // order to return to the same state. - // - // Operational transforms provide this on an operation-by-operation basis. To break this - // down, we treat each server operation individually, in order. For each such operation, - // we start in this state: - // - // - // base state-* - // / \-server op - // * * - // local / \ / - // ops * * - // / \ / new - // * * local - // local / \ / ops - // state-* * - // new-\ / - // server op *-new local state - // - // This is slightly complicated by the fact that the transform function can return None, - // indicating no operation is required. If this happens for a local op, we can just omit - // it. If it happens for server op, then we must copy the remaining local ops. - for server_op in version.operations.drain(..) { - trace!( - "rebasing local operations onto server operation {:?}", - server_op - ); - let mut new_local_ops = Vec::with_capacity(local_ops.len()); - let mut svr_op = Some(server_op); - for local_op in local_ops.drain(..) { - if let Some(o) = svr_op { - let (new_server_op, new_local_op) = SyncOp::transform(o, local_op.clone()); - trace!("local operation {:?} -> {:?}", local_op, new_local_op); - svr_op = new_server_op; - if let Some(o) = new_local_op { - new_local_ops.push(o); - } - } else { - trace!( - "local operation {:?} unchanged (server operation consumed)", - local_op - ); - new_local_ops.push(local_op); - } - } - if let Some(o) = svr_op { - if let Err(e) = apply::apply_op(txn, &o) { - warn!("Invalid operation when syncing: {} (ignored)", e); - } - } - *local_ops = new_local_ops; - } - Ok(()) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::server::{test::TestServer, SyncOp}; - use crate::storage::InMemoryStorage; - use crate::taskdb::{snapshot::SnapshotTasks, TaskDb}; - use chrono::Utc; - use pretty_assertions::assert_eq; - use uuid::Uuid; - - fn newdb() -> TaskDb { - TaskDb::new(Box::new(InMemoryStorage::new())) - } - - #[test] - fn test_sync() -> Result<()> { - let mut server: Box = TestServer::new().server(); - - let mut db1 = newdb(); - sync(&mut server, db1.storage.txn()?.as_mut(), false).unwrap(); - - let mut db2 = newdb(); - sync(&mut server, db2.storage.txn()?.as_mut(), false).unwrap(); - - // make some changes in parallel to db1 and db2.. - let uuid1 = Uuid::new_v4(); - db1.apply(SyncOp::Create { uuid: uuid1 }).unwrap(); - db1.apply(SyncOp::Update { - uuid: uuid1, - property: "title".into(), - value: Some("my first task".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - let uuid2 = Uuid::new_v4(); - db2.apply(SyncOp::Create { uuid: uuid2 }).unwrap(); - db2.apply(SyncOp::Update { - uuid: uuid2, - property: "title".into(), - value: Some("my second task".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - // and synchronize those around - sync(&mut server, db1.storage.txn()?.as_mut(), false).unwrap(); - sync(&mut server, db2.storage.txn()?.as_mut(), false).unwrap(); - sync(&mut server, db1.storage.txn()?.as_mut(), false).unwrap(); - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); - - // now make updates to the same task on both sides - db1.apply(SyncOp::Update { - uuid: uuid2, - property: "priority".into(), - value: Some("H".into()), - timestamp: Utc::now(), - }) - .unwrap(); - db2.apply(SyncOp::Update { - uuid: uuid2, - property: "project".into(), - value: Some("personal".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - // and synchronize those around - sync(&mut server, db1.storage.txn()?.as_mut(), false).unwrap(); - sync(&mut server, db2.storage.txn()?.as_mut(), false).unwrap(); - sync(&mut server, db1.storage.txn()?.as_mut(), false).unwrap(); - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); - - Ok(()) - } - - #[test] - fn test_sync_create_delete() -> Result<()> { - let mut server: Box = TestServer::new().server(); - - let mut db1 = newdb(); - sync(&mut server, db1.storage.txn()?.as_mut(), false).unwrap(); - - let mut db2 = newdb(); - sync(&mut server, db2.storage.txn()?.as_mut(), false).unwrap(); - - // create and update a task.. - let uuid = Uuid::new_v4(); - db1.apply(SyncOp::Create { uuid }).unwrap(); - db1.apply(SyncOp::Update { - uuid, - property: "title".into(), - value: Some("my first task".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - // and synchronize those around - sync(&mut server, db1.storage.txn()?.as_mut(), false).unwrap(); - sync(&mut server, db2.storage.txn()?.as_mut(), false).unwrap(); - sync(&mut server, db1.storage.txn()?.as_mut(), false).unwrap(); - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); - - // delete and re-create the task on db1 - db1.apply(SyncOp::Delete { uuid }).unwrap(); - db1.apply(SyncOp::Create { uuid }).unwrap(); - db1.apply(SyncOp::Update { - uuid, - property: "title".into(), - value: Some("my second task".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - // and on db2, update a property of the task - db2.apply(SyncOp::Update { - uuid, - property: "project".into(), - value: Some("personal".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - sync(&mut server, db1.storage.txn()?.as_mut(), false).unwrap(); - sync(&mut server, db2.storage.txn()?.as_mut(), false).unwrap(); - sync(&mut server, db1.storage.txn()?.as_mut(), false).unwrap(); - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); - - Ok(()) - } - - #[test] - fn test_sync_add_snapshot_start_with_snapshot() -> Result<()> { - let mut test_server = TestServer::new(); - - let mut server: Box = test_server.server(); - let mut db1 = newdb(); - - let uuid = Uuid::new_v4(); - db1.apply(SyncOp::Create { uuid })?; - db1.apply(SyncOp::Update { - uuid, - property: "title".into(), - value: Some("my first task".into()), - timestamp: Utc::now(), - })?; - - test_server.set_snapshot_urgency(SnapshotUrgency::High); - sync(&mut server, db1.storage.txn()?.as_mut(), false)?; - - // assert that a snapshot was added - let base_version = db1.storage.txn()?.base_version()?; - let (v, s) = test_server - .snapshot() - .ok_or_else(|| anyhow::anyhow!("no snapshot"))?; - assert_eq!(v, base_version); - - let tasks = SnapshotTasks::decode(&s)?.into_inner(); - assert_eq!(tasks[0].0, uuid); - - // update the taskdb and sync again - db1.apply(SyncOp::Update { - uuid, - property: "title".into(), - value: Some("my first task, updated".into()), - timestamp: Utc::now(), - })?; - sync(&mut server, db1.storage.txn()?.as_mut(), false)?; - - // delete the first version, so that db2 *must* initialize from - // the snapshot - test_server.delete_version(Uuid::nil()); - - // sync to a new DB and check that we got the expected results - let mut db2 = newdb(); - sync(&mut server, db2.storage.txn()?.as_mut(), false)?; - - let task = db2.get_task(uuid)?.unwrap(); - assert_eq!(task.get("title").unwrap(), "my first task, updated"); - - Ok(()) - } - - #[test] - fn test_sync_avoids_snapshot() -> Result<()> { - let test_server = TestServer::new(); - - let mut server: Box = test_server.server(); - let mut db1 = newdb(); - - let uuid = Uuid::new_v4(); - db1.apply(SyncOp::Create { uuid }).unwrap(); - - test_server.set_snapshot_urgency(SnapshotUrgency::Low); - sync(&mut server, db1.storage.txn()?.as_mut(), true).unwrap(); - - // assert that a snapshot was not added, because we indicated - // we wanted to avoid snapshots and it was only low urgency - assert_eq!(test_server.snapshot(), None); - - Ok(()) - } -} diff --git a/taskchampion/taskchampion/src/taskdb/undo.rs b/taskchampion/taskchampion/src/taskdb/undo.rs deleted file mode 100644 index c193d96bb..000000000 --- a/taskchampion/taskchampion/src/taskdb/undo.rs +++ /dev/null @@ -1,154 +0,0 @@ -use super::apply; -use crate::errors::Result; -use crate::storage::{ReplicaOp, StorageTxn}; -use log::{debug, info, trace}; - -/// Local operations until the most recent UndoPoint. -pub fn get_undo_ops(txn: &mut dyn StorageTxn) -> Result> { - let mut local_ops = txn.operations().unwrap(); - let mut undo_ops: Vec = Vec::new(); - - while let Some(op) = local_ops.pop() { - if op == ReplicaOp::UndoPoint { - break; - } - undo_ops.push(op); - } - - Ok(undo_ops) -} - -/// Commit operations to storage, returning a boolean indicating success. -pub fn commit_undo_ops(txn: &mut dyn StorageTxn, mut undo_ops: Vec) -> Result { - let mut applied = false; - let mut local_ops = txn.operations().unwrap(); - - // Add UndoPoint to undo_ops unless this undo will empty the operations database, in which case - // there is no UndoPoint. - if local_ops.len() > undo_ops.len() { - undo_ops.push(ReplicaOp::UndoPoint); - } - - // Drop undo_ops iff they're the latest operations. - // TODO Support concurrent undo by adding the reverse of undo_ops rather than popping from operations. - let old_len = local_ops.len(); - let undo_len = undo_ops.len(); - let new_len = old_len - undo_len; - let local_undo_ops = &local_ops[new_len..old_len]; - undo_ops.reverse(); - if local_undo_ops != undo_ops { - info!("Undo failed: concurrent changes to the database occurred."); - debug!( - "local_undo_ops={:#?}\nundo_ops={:#?}", - local_undo_ops, undo_ops - ); - return Ok(applied); - } - undo_ops.reverse(); - local_ops.truncate(new_len); - - for op in undo_ops { - debug!("Reversing operation {:?}", op); - let rev_ops = op.reverse_ops(); - for op in rev_ops { - trace!("Applying reversed operation {:?}", op); - apply::apply_op(txn, &op)?; - applied = true; - } - } - - if undo_len != 0 { - txn.set_operations(local_ops)?; - txn.commit()?; - } - - Ok(applied) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::server::SyncOp; - use crate::taskdb::TaskDb; - use chrono::Utc; - use pretty_assertions::assert_eq; - use uuid::Uuid; - - #[test] - fn test_apply_create() -> Result<()> { - let mut db = TaskDb::new_inmemory(); - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - let timestamp = Utc::now(); - - // apply a few ops, capture the DB state, make an undo point, and then apply a few more - // ops. - db.apply(SyncOp::Create { uuid: uuid1 })?; - db.apply(SyncOp::Update { - uuid: uuid1, - property: "prop".into(), - value: Some("v1".into()), - timestamp, - })?; - db.apply(SyncOp::Create { uuid: uuid2 })?; - db.apply(SyncOp::Update { - uuid: uuid2, - property: "prop".into(), - value: Some("v2".into()), - timestamp, - })?; - db.apply(SyncOp::Update { - uuid: uuid2, - property: "prop2".into(), - value: Some("v3".into()), - timestamp, - })?; - - let db_state = db.sorted_tasks(); - - db.add_undo_point()?; - db.apply(SyncOp::Delete { uuid: uuid1 })?; - db.apply(SyncOp::Update { - uuid: uuid2, - property: "prop".into(), - value: None, - timestamp, - })?; - db.apply(SyncOp::Update { - uuid: uuid2, - property: "prop2".into(), - value: Some("new-value".into()), - timestamp, - })?; - - assert_eq!(db.operations().len(), 9, "{:#?}", db.operations()); - - let undo_ops = get_undo_ops(db.storage.txn()?.as_mut())?; - assert_eq!(undo_ops.len(), 3, "{:#?}", undo_ops); - - assert!(commit_undo_ops(db.storage.txn()?.as_mut(), undo_ops)?); - - // Note that we've subtracted the length of undo_ops plus one for the UndoPoint. - assert_eq!(db.operations().len(), 5, "{:#?}", db.operations()); - assert_eq!(db.sorted_tasks(), db_state, "{:#?}", db.sorted_tasks()); - - // Note that the number of undo operations is equal to the number of operations in the - // database here because there are no UndoPoints. - let undo_ops = get_undo_ops(db.storage.txn()?.as_mut())?; - assert_eq!(undo_ops.len(), 5, "{:#?}", undo_ops); - - assert!(commit_undo_ops(db.storage.txn()?.as_mut(), undo_ops)?); - - // empty db - assert_eq!(db.operations().len(), 0, "{:#?}", db.operations()); - assert_eq!(db.sorted_tasks(), vec![], "{:#?}", db.sorted_tasks()); - - let undo_ops = get_undo_ops(db.storage.txn()?.as_mut())?; - assert_eq!(undo_ops.len(), 0, "{:#?}", undo_ops); - - // nothing left to undo, so commit_undo_ops() returns false - assert!(!commit_undo_ops(db.storage.txn()?.as_mut(), undo_ops)?); - - Ok(()) - } -} diff --git a/taskchampion/taskchampion/src/taskdb/working_set.rs b/taskchampion/taskchampion/src/taskdb/working_set.rs deleted file mode 100644 index c79e488e9..000000000 --- a/taskchampion/taskchampion/src/taskdb/working_set.rs +++ /dev/null @@ -1,152 +0,0 @@ -use crate::errors::Result; -use crate::storage::{StorageTxn, TaskMap}; -use std::collections::HashSet; - -/// Rebuild the working set using a function to identify tasks that should be in the set. This -/// renumbers the existing working-set tasks to eliminate gaps, and also adds any tasks that -/// are not already in the working set but should be. The rebuild occurs in a single -/// trasnsaction against the storage backend. -pub fn rebuild(txn: &mut dyn StorageTxn, in_working_set: F, renumber: bool) -> Result<()> -where - F: Fn(&TaskMap) -> bool, -{ - let mut new_ws = vec![None]; // index 0 is always None - let mut seen = HashSet::new(); - - // The goal here is for existing working-set items to be "compressed' down to index 1, so - // we begin by scanning the current working set and inserting any tasks that should still - // be in the set into new_ws, implicitly dropping any tasks that are no longer in the - // working set. - for elt in txn.get_working_set()?.drain(1..) { - if let Some(uuid) = elt { - if let Some(task) = txn.get_task(uuid)? { - if in_working_set(&task) { - new_ws.push(Some(uuid)); - seen.insert(uuid); - continue; - } - } - } - - // if we are not renumbering, then insert a blank working-set entry here - if !renumber { - new_ws.push(None); - } - } - - // if renumbering, clear the working set and re-add - if renumber { - txn.clear_working_set()?; - for elt in new_ws.drain(1..new_ws.len()).flatten() { - txn.add_to_working_set(elt)?; - } - } else { - // ..otherwise, just clear the None items determined above from the working set - for (i, elt) in new_ws.iter().enumerate().skip(1) { - if elt.is_none() { - txn.set_working_set_item(i, None)?; - } - } - } - - // Now go hunting for tasks that should be in this list but are not, adding them at the - // end of the list, whether renumbering or not - for (uuid, task) in txn.all_tasks()? { - if !seen.contains(&uuid) && in_working_set(&task) { - txn.add_to_working_set(uuid)?; - } - } - - txn.commit()?; - Ok(()) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::server::SyncOp; - use crate::taskdb::TaskDb; - use chrono::Utc; - use uuid::Uuid; - - #[test] - fn rebuild_working_set_renumber() -> Result<()> { - rebuild_working_set(true) - } - - #[test] - fn rebuild_working_set_no_renumber() -> Result<()> { - rebuild_working_set(false) - } - - fn rebuild_working_set(renumber: bool) -> Result<()> { - let mut db = TaskDb::new_inmemory(); - let mut uuids = vec![]; - uuids.push(Uuid::new_v4()); - println!("uuids[0]: {:?} - pending, not in working set", uuids[0]); - uuids.push(Uuid::new_v4()); - println!("uuids[1]: {:?} - pending, in working set", uuids[1]); - uuids.push(Uuid::new_v4()); - println!("uuids[2]: {:?} - not pending, not in working set", uuids[2]); - uuids.push(Uuid::new_v4()); - println!("uuids[3]: {:?} - not pending, in working set", uuids[3]); - uuids.push(Uuid::new_v4()); - println!("uuids[4]: {:?} - pending, in working set", uuids[4]); - - // add everything to the TaskDb - for uuid in &uuids { - db.apply(SyncOp::Create { uuid: *uuid })?; - } - for i in &[0usize, 1, 4] { - db.apply(SyncOp::Update { - uuid: uuids[*i], - property: String::from("status"), - value: Some("pending".into()), - timestamp: Utc::now(), - })?; - } - - // set the existing working_set as we want it - { - let mut txn = db.storage.txn()?; - txn.clear_working_set()?; - - for i in &[1usize, 3, 4] { - txn.add_to_working_set(uuids[*i])?; - } - - txn.commit()?; - } - - assert_eq!( - db.working_set()?, - vec![None, Some(uuids[1]), Some(uuids[3]), Some(uuids[4])] - ); - - rebuild( - db.storage.txn()?.as_mut(), - |t| { - if let Some(status) = t.get("status") { - status == "pending" - } else { - false - } - }, - renumber, - )?; - - let exp = if renumber { - // uuids[1] and uuids[4] are already in the working set, so are compressed - // to the top, and then uuids[0] is added. - vec![None, Some(uuids[1]), Some(uuids[4]), Some(uuids[0])] - } else { - // uuids[1] and uuids[4] are already in the working set, at indexes 1 and 3, - // and then uuids[0] is added. - vec![None, Some(uuids[1]), None, Some(uuids[4]), Some(uuids[0])] - }; - - assert_eq!(db.working_set()?, exp); - - Ok(()) - } -} diff --git a/taskchampion/taskchampion/src/utils.rs b/taskchampion/taskchampion/src/utils.rs deleted file mode 100644 index 7eb0885dc..000000000 --- a/taskchampion/taskchampion/src/utils.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::convert::TryInto; -use uuid::Uuid; - -/// A representation of a UUID as a key. This is just a newtype wrapping the 128-bit packed form -/// of a UUID. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) struct Key(uuid::Bytes); - -impl From<&[u8]> for Key { - fn from(bytes: &[u8]) -> Key { - Key(bytes.try_into().expect("expected 16 bytes")) - } -} - -impl From<&Uuid> for Key { - fn from(uuid: &Uuid) -> Key { - let key = Key(*uuid.as_bytes()); - key - } -} - -impl From for Key { - fn from(uuid: Uuid) -> Key { - let key = Key(*uuid.as_bytes()); - key - } -} - -impl From for Uuid { - fn from(key: Key) -> Uuid { - Uuid::from_bytes(key.0) - } -} - -impl AsRef<[u8]> for Key { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn test_from_bytes() { - let k: Key = (&[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16][..]).into(); - let u: Uuid = k.into(); - assert_eq!( - u, - Uuid::parse_str("01020304-0506-0708-090a-0b0c0d0e0f10").unwrap() - ); - } - - #[test] - #[should_panic] - fn test_from_bytes_bad_len() { - let _: Key = (&[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11][..]).into(); - } -} diff --git a/taskchampion/taskchampion/src/workingset.rs b/taskchampion/taskchampion/src/workingset.rs deleted file mode 100644 index 15a509753..000000000 --- a/taskchampion/taskchampion/src/workingset.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::collections::HashMap; -use uuid::Uuid; - -/// A WorkingSet represents a snapshot of the working set from a replica. -/// -/// A replica's working set is a mapping from small integers to task uuids for all pending tasks. -/// The small integers are meant to be stable, easily-typed identifiers for users to interact with -/// important tasks. -/// -/// IMPORTANT: the content of the working set may change at any time that a DB transaction is not -/// in progress, and the data in this type will not be updated automatically. It is up to the -/// caller to decide how long to keep this value, and how much to trust the accuracy of its -/// contents. In practice, the answers are usually "a few milliseconds" and treating unexpected -/// results as non-fatal. -pub struct WorkingSet { - by_index: Vec>, - by_uuid: HashMap, -} - -impl WorkingSet { - /// Create a new WorkingSet. Typically this is acquired via `replica.working_set()` - pub(crate) fn new(by_index: Vec>) -> Self { - let mut by_uuid = HashMap::new(); - - // working sets are 1-indexed, so element 0 should always be None - assert!(by_index.is_empty() || by_index[0].is_none()); - - for (index, uuid) in by_index.iter().enumerate() { - if let Some(uuid) = uuid { - by_uuid.insert(*uuid, index); - } - } - Self { by_index, by_uuid } - } - - /// Get the "length" of the working set: the total number of uuids in the set. - pub fn len(&self) -> usize { - self.by_index.iter().filter(|e| e.is_some()).count() - } - - /// Get the largest index in the working set, or zero if the set is empty. - pub fn largest_index(&self) -> usize { - self.by_index.len().saturating_sub(1) - } - - /// True if the length is zero - pub fn is_empty(&self) -> bool { - self.by_index.iter().all(|e| e.is_none()) - } - - /// Get the uuid with the given index, if any exists. - pub fn by_index(&self, index: usize) -> Option { - if let Some(Some(uuid)) = self.by_index.get(index) { - Some(*uuid) - } else { - None - } - } - - /// Get the index for the given uuid, if any - pub fn by_uuid(&self, uuid: Uuid) -> Option { - self.by_uuid.get(&uuid).copied() - } - - /// Iterate over pairs (index, uuid), in order by index. - pub fn iter(&self) -> impl Iterator + '_ { - self.by_index - .iter() - .enumerate() - .filter_map(|(index, uuid)| uuid.as_ref().map(|uuid| (index, *uuid))) - } -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - fn make() -> (Uuid, Uuid, WorkingSet) { - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - ( - uuid1, - uuid2, - WorkingSet::new(vec![None, Some(uuid1), None, Some(uuid2), None]), - ) - } - - #[test] - fn test_new() { - let (_, uuid2, ws) = make(); - assert_eq!(ws.by_index[3], Some(uuid2)); - assert_eq!(ws.by_uuid.get(&uuid2), Some(&3)); - } - - #[test] - fn test_len_and_is_empty() { - let (_, _, ws) = make(); - assert_eq!(ws.len(), 2); - assert_eq!(ws.is_empty(), false); - - let ws = WorkingSet::new(vec![]); - assert_eq!(ws.len(), 0); - assert_eq!(ws.is_empty(), true); - - let ws = WorkingSet::new(vec![None, None, None]); - assert_eq!(ws.len(), 0); - assert_eq!(ws.is_empty(), true); - } - - #[test] - fn test_largest_index() { - let uuid1 = Uuid::new_v4(); - let uuid2 = Uuid::new_v4(); - - let ws = WorkingSet::new(vec![]); - assert_eq!(ws.largest_index(), 0); - - let ws = WorkingSet::new(vec![None, Some(uuid1)]); - assert_eq!(ws.largest_index(), 1); - - let ws = WorkingSet::new(vec![None, Some(uuid1), None, Some(uuid2)]); - assert_eq!(ws.largest_index(), 3); - - let ws = WorkingSet::new(vec![None, Some(uuid1), None, Some(uuid2), None]); - assert_eq!(ws.largest_index(), 4); - } - - #[test] - fn test_by_index() { - let (uuid1, uuid2, ws) = make(); - assert_eq!(ws.by_index(0), None); - assert_eq!(ws.by_index(1), Some(uuid1)); - assert_eq!(ws.by_index(2), None); - assert_eq!(ws.by_index(3), Some(uuid2)); - assert_eq!(ws.by_index(4), None); - assert_eq!(ws.by_index(100), None); // past the end of the vector - } - - #[test] - fn test_by_uuid() { - let (uuid1, uuid2, ws) = make(); - let nosuch = Uuid::new_v4(); - assert_eq!(ws.by_uuid(uuid1), Some(1)); - assert_eq!(ws.by_uuid(uuid2), Some(3)); - assert_eq!(ws.by_uuid(nosuch), None); - } - - #[test] - fn test_iter() { - let (uuid1, uuid2, ws) = make(); - assert_eq!(ws.iter().collect::>(), vec![(1, uuid1), (3, uuid2),]); - } -} diff --git a/taskchampion/xtask/src/main.rs b/taskchampion/xtask/src/main.rs deleted file mode 100644 index 446787db6..000000000 --- a/taskchampion/xtask/src/main.rs +++ /dev/null @@ -1,124 +0,0 @@ -//! This executable defines the `cargo xtask` subcommands. -//! -//! At the moment it is very simple, but if this grows more subcommands then -//! it will be sensible to use `clap` or another similar library. - -use regex::Regex; -use std::env; -use std::fs::File; -use std::io::{BufRead, BufReader, Seek, Write}; -use std::path::{Path, PathBuf}; - -/// Tuples of the form (PATH, REGEX) where PATH and REGEX are literals where PATH is a file that -/// conains the Minimum Supported Rust Version and REGEX is the pattern to find the appropriate -/// line in the file. PATH is relative to the `taskchampion/` directory in the repo. -const MSRV_PATH_REGEX: &[(&str, &str)] = &[ - ( - "../.github/workflows/checks.yml", - r#"toolchain: "[0-9.]+*" # MSRV"#, - ), - ("../.github/workflows/rust-tests.yml", r#""[0-9.]+" # MSRV"#), - ( - "taskchampion/src/lib.rs", - r#"Rust version [0-9.]* and higher"#, - ), - ("taskchampion/Cargo.toml", r#"^rust-version = "[0-9.]"#), -]; - -pub fn main() -> anyhow::Result<()> { - let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); - let workspace_dir = manifest_dir.parent().unwrap(); - let arguments: Vec = env::args().collect(); - - if arguments.len() < 2 { - anyhow::bail!("xtask: Valid arguments are: `codegen`, `msrv `"); - } - - match arguments[1].as_str() { - "codegen" => codegen(workspace_dir), - "msrv" => msrv(arguments, workspace_dir), - _ => anyhow::bail!("xtask: unknown xtask"), - } -} - -/// `cargo xtask codegen` -/// -/// This uses ffizz-header to generate `lib/taskchampion.h`. -fn codegen(workspace_dir: &Path) -> anyhow::Result<()> { - let lib_crate_dir = workspace_dir.join("lib"); - let mut file = File::create(lib_crate_dir.join("taskchampion.h")).unwrap(); - write!(&mut file, "{}", ::taskchampion_lib::generate_header()).unwrap(); - - Ok(()) -} - -/// `cargo xtask msrv (X.Y)` -/// -/// This checks and updates the Minimum Supported Rust Version for all files specified in MSRV_PATH_REGEX`. -/// Each line where the regex matches will have all values of the form `#.##` replaced with the given MSRV. -fn msrv(args: Vec, workspace_dir: &Path) -> anyhow::Result<()> { - // check that (X.Y) argument is (mostly) valid: - if args.len() < 3 || !args[2].chars().all(|c| c.is_numeric() || c == '.') { - anyhow::bail!("xtask: Invalid argument format. Xtask msrv argument takes the form \"X.Y(y)\", where XYy are numbers. eg: `cargo run xtask msrv 1.68`"); - } - let version_replacement_string = &args[2]; - - // set regex for replacing version number only within the pattern found within a line - let re_msrv_version = Regex::new(r"([0-9]+(\.|[0-9]+|))+")?; - - // for each file in const paths tuple - for msrv_file in MSRV_PATH_REGEX { - let mut is_pattern_in_file = false; - - let path = workspace_dir.join(msrv_file.0); - let path = Path::new(&path); - if !path.exists() { - anyhow::bail!("xtask: path does not exist {}", &path.display()); - }; - - let mut file = File::options().read(true).write(true).open(path)?; - let reader = BufReader::new(&file); - - // set search string and the replacement string for version number content - let re_msrv_pattern = Regex::new(msrv_file.1)?; - - // for each line in file - let mut file_string = String::new(); - for line in reader.lines() { - let line = &line?; - - // if rust version pattern is found and is different, update it - if let Some(pattern_offset) = re_msrv_pattern.find(line) { - if !pattern_offset.as_str().contains(version_replacement_string) { - file_string += &re_msrv_version.replace(line, version_replacement_string); - - file_string += "\n"; - - is_pattern_in_file = true; - continue; - } - } - - file_string += line; - file_string += "\n"; - } - - // if pattern was found and updated, write to disk - if is_pattern_in_file { - // Set the file length to the file_string length - file.set_len(file_string.len() as u64)?; - - // set the cursor to the beginning of the file and write - file.seek(std::io::SeekFrom::Start(0))?; - file.write_all(file_string.as_bytes())?; - - // notify user this file was updated - println!( - "xtask: Updated MSRV in {}", - re_msrv_version.replace(msrv_file.0, version_replacement_string) - ); - } - } - - Ok(()) -} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7f7d9d6db..dadca6bc8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,12 +10,11 @@ endif() include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src - ${CMAKE_SOURCE_DIR}/src/taskchampion/lib ${CMAKE_SOURCE_DIR}/test - ${CMAKE_SOURCE_DIR}/taskchampion/lib ${TASK_INCLUDE_DIRS}) set (test_SRCS @@ -203,4 +202,4 @@ endforeach(python_Test) #SET(CMAKE_BUILD_TYPE gcov) #SET(CMAKE_CXX_FLAGS_GCOV "--coverage") #SET(CMAKE_C_FLAGS_GCOV "--coverage") -#SET(CMAKE_EXE_LINKER_FLAGS_GCOV "--coverage") \ No newline at end of file +#SET(CMAKE_EXE_LINKER_FLAGS_GCOV "--coverage") diff --git a/taskchampion/xtask/Cargo.toml b/xtask/Cargo.toml similarity index 60% rename from taskchampion/xtask/Cargo.toml rename to xtask/Cargo.toml index ad3d76837..e21069966 100644 --- a/taskchampion/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -4,6 +4,6 @@ version = "0.4.1" edition = "2021" [dependencies] -anyhow.workspace = true -taskchampion-lib = { path = "../lib" } +anyhow = "1.0" +taskchampion-lib = { path = "../src/tc/lib" } regex = "^1.10.2" diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 000000000..003844ea2 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,35 @@ +//! This executable defines the `cargo xtask` subcommands. +//! +//! At the moment it is very simple, but if this grows more subcommands then +//! it will be sensible to use `clap` or another similar library. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::{Path, PathBuf}; + +pub fn main() -> anyhow::Result<()> { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); + let workspace_dir = manifest_dir.parent().unwrap(); + let arguments: Vec = env::args().collect(); + + if arguments.len() < 2 { + anyhow::bail!("xtask: Valid arguments are: `codegen`"); + } + + match arguments[1].as_str() { + "codegen" => codegen(workspace_dir), + _ => anyhow::bail!("xtask: unknown xtask"), + } +} + +/// `cargo xtask codegen` +/// +/// This uses ffizz-header to generate `lib/taskchampion.h`. +fn codegen(workspace_dir: &Path) -> anyhow::Result<()> { + let lib_crate_dir = workspace_dir.join("src/tc/lib"); + let mut file = File::create(lib_crate_dir.join("taskchampion.h")).unwrap(); + write!(&mut file, "{}", ::taskchampion_lib::generate_header()).unwrap(); + + Ok(()) +} From 380c740ff0740cef24711e71429170b5282d4fdf Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 2 May 2024 19:35:00 -0400 Subject: [PATCH 010/242] remove pull req template (#3432) --- .github/pull_request_template.md | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 221297751..000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,11 +0,0 @@ -#### Description - -Replace this text with a description of the PR. - -#### Additional information... - -- [ ] I changed C++ code or build infrastructure. - Please run the test suite and include the output of `cd build/test && make && ./problems`. - -- [ ] I changed Rust code or build infrastructure. - Please run `cargo test` and address any failures before submitting. From b7551cbba6200e9257fd1a5e615acf596c0fd01f Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 3 May 2024 02:22:33 +0200 Subject: [PATCH 011/242] Fix SyntaxWarning invalid escape sequence in Python code (#3433) --- performance/compare_runs.py | 2 +- test/alias.test.py | 6 +-- test/annotate.test.py | 24 ++++++------ test/append.test.py | 6 +-- test/calc.test.py | 2 +- test/color.cmd.test.py | 6 +-- test/color.rules.test.py | 2 +- test/columns.test.py | 8 ++-- test/commands.test.py | 12 +++--- test/context.test.py | 2 +- test/custom.tag_ind.test.py | 4 +- test/custom.test.py | 2 +- test/default.test.py | 2 +- test/diag.test.py | 2 +- test/due.test.py | 4 +- test/encoding.test.py | 4 +- test/feature.default.project.test.py | 10 ++--- test/feature.recurrence.test.py | 4 +- test/format.test.py | 4 +- test/gc.test.py | 8 ++-- test/history.test.py | 56 ++++++++++++++-------------- test/info.test.py | 36 +++++++++--------- test/logo.test.py | 2 +- test/project.test.py | 14 +++---- test/quotes.test.py | 4 +- test/recurrence.test.py | 10 ++--- test/search.test.py | 2 +- test/sequence.test.py | 2 +- test/stats.test.py | 10 ++--- test/template.test.py | 2 +- test/tw-1379.test.py | 4 +- test/uda.test.py | 26 ++++++------- test/uda_orphan.test.py | 4 +- test/uda_sort.test.py | 2 +- test/undo.test.py | 8 ++-- test/upgrade.test.py | 10 ++--- test/version.test.py | 6 +-- 37 files changed, 156 insertions(+), 156 deletions(-) diff --git a/performance/compare_runs.py b/performance/compare_runs.py index da2339740..834332a18 100755 --- a/performance/compare_runs.py +++ b/performance/compare_runs.py @@ -29,7 +29,7 @@ def parse_perf(input): tests[command] = [] # Parse concatenated run_perf output - for i in re.findall("^ - task %s\.\.\.\n" + for i in re.findall("^ - task %s\\.\\.\\.\n" "Perf task ([^ ]+) ([^ ]+) ([^ ]+) (.+)$" % command, input, re.MULTILINE): info = i[0:3] + ({k:v for k, v in (i.split(":") for i in i[-1].split())},) diff --git a/test/alias.test.py b/test/alias.test.py index 544ed3801..4ba584514 100755 --- a/test/alias.test.py +++ b/test/alias.test.py @@ -171,7 +171,7 @@ class TestBug1031(TestCase): self.t("add from") code, out, err = self.t("1 info") - expected = "Description\s+to" + expected = r"Description\s+to" self.assertRegex(out, expected) def test_alias_to_to(self): @@ -179,7 +179,7 @@ class TestBug1031(TestCase): self.t("add from -- to") code, out, err = self.t("1 info") - expected = "Description\s+to to" + expected = r"Description\s+to to" self.assertRegex(out, expected) def test_alias_to_from(self): @@ -187,7 +187,7 @@ class TestBug1031(TestCase): self.t("add to -- from") code, out, err = self.t("1 info") - expected = "Description\s+to from" + expected = r"Description\s+to from" self.assertRegex(out, expected) diff --git a/test/annotate.test.py b/test/annotate.test.py index c4fbd6caf..588052bfc 100755 --- a/test/annotate.test.py +++ b/test/annotate.test.py @@ -87,17 +87,17 @@ class TestAnnotate(TestCase): self.assertTasksExist(out) - self.assertRegex(out, "one\n.+\d{1,2}/\d{1,2}/\d{4}\s+foo1", + self.assertRegex(out, "one\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+foo1", msg='full - first annotation task 1') - self.assertRegex(out, "foo1\n.+\d{1,2}/\d{1,2}/\d{4}\s+foo2", + self.assertRegex(out, "foo1\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+foo2", msg='full - first annotation task 1') - self.assertRegex(out, "foo2\n.+\d{1,2}/\d{1,2}/\d{4}\s+foo3", + self.assertRegex(out, "foo2\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+foo3", msg='full - first annotation task 1') - self.assertRegex(out, "two\n.+\d{1,2}/\d{1,2}/\d{4}\s+bar1", + self.assertRegex(out, "two\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+bar1", msg='full - first annotation task 1') - self.assertRegex(out, "bar1\n.+\d{1,2}/\d{1,2}/\d{4}\s+bar2", + self.assertRegex(out, "bar1\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+bar2", msg='full - first annotation task 1') - self.assertRegex(out, "three\n.+\d{1,2}/\d{1,2}/\d{4}\s+baz1", + self.assertRegex(out, "three\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+baz1", msg='full - first annotation task 1') def test_annotate_dateformat(self): @@ -114,17 +114,17 @@ class TestAnnotate(TestCase): self.assertTasksExist(out) - self.assertRegex(out, "one\n.+\d{1,6}\s+\d{1,6}\s+foo1", + self.assertRegex(out, "one\n.+\\d{1,6}\\s+\\d{1,6}\\s+foo1", msg="dateformat - first annotation task 1") - self.assertRegex(out, "foo1\n.+\d{1,6}\s+\d{1,6}\s+foo2", + self.assertRegex(out, "foo1\n.+\\d{1,6}\\s+\\d{1,6}\\s+foo2", msg="dateformat - second annotation task 1") - self.assertRegex(out, "foo2\n.+\d{1,6}\s+\d{1,6}\s+foo3", + self.assertRegex(out, "foo2\n.+\\d{1,6}\\s+\\d{1,6}\\s+foo3", msg="dateformat - third annotation task 1") - self.assertRegex(out, "two\n.+\d{1,6}\s+\d{1,6}\s+bar1", + self.assertRegex(out, "two\n.+\\d{1,6}\\s+\\d{1,6}\\s+bar1", msg="dateformat - first annotation task 2") - self.assertRegex(out, "bar1\n.+\d{1,6}\s+\d{1,6}\s+bar2", + self.assertRegex(out, "bar1\n.+\\d{1,6}\\s+\\d{1,6}\\s+bar2", msg="dateformat - second annotation task 2") - self.assertRegex(out, "three\n.+\d{1,6}\s+\d{1,6}\s+baz1", + self.assertRegex(out, "three\n.+\\d{1,6}\\s+\\d{1,6}\\s+baz1", msg="dateformat - first annotation task 3") class TestAnnotationPropagation(TestCase): diff --git a/test/append.test.py b/test/append.test.py index b2fa85f66..4dccf0329 100755 --- a/test/append.test.py +++ b/test/append.test.py @@ -50,7 +50,7 @@ class TestAppend(TestCase): code, out, err = self.t("info 1") - expected = "Description\s+foo\sbar\n" + expected = "Description\\s+foo\\sbar\n" self.assertRegex(out, expected) def test_append_error_on_empty(self): @@ -82,10 +82,10 @@ class TestBug440(TestCase): code2, out2, err2 = self.t("2 ls") self.assertNotIn("Foo", out1) - self.assertRegex(out1, "\w+ Appendtext") + self.assertRegex(out1, r"\w+ Appendtext") self.assertNotIn("Foo", out2) - self.assertRegex(out2, "\w+ Appendtext") + self.assertRegex(out2, r"\w+ Appendtext") if __name__ == "__main__": diff --git a/test/calc.test.py b/test/calc.test.py index a31746d3b..f18a71e7d 100755 --- a/test/calc.test.py +++ b/test/calc.test.py @@ -90,7 +90,7 @@ class TestCalc(TestCase): """version""" code, out, err = run_cmd_wait_nofail((CALC, "--version")) - self.assertRegex(out, "calc \d\.\d+\.\d+") + self.assertRegex(out, r"calc \d\.\d+\.\d+") self.assertIn("Copyright", out) self.assertGreaterEqual(code, 1) diff --git a/test/color.cmd.test.py b/test/color.cmd.test.py index 0145d21bf..9e1857648 100755 --- a/test/color.cmd.test.py +++ b/test/color.cmd.test.py @@ -63,17 +63,17 @@ class TestColorCommand(TestCase): def test_colors_sample(self): """ Verify 'task colors red' shows a sample""" code, out, err = self.t("colors rc._forcecolor:on red") - self.assertRegex(out, "Your sample:\n\n .\[31mtask color red.\[0m") + self.assertRegex(out, "Your sample:\n\n .\\[31mtask color red.\\[0m") def test_colors_legend(self): """ Verify 'task colors legend' shows theme colors""" code, out, err = self.t("colors rc._forcecolor:on legend") - self.assertRegex(out, "color.debug\s+.\[0m\s.\[38;5;4mcolor4\s+.\[0m") + self.assertRegex(out, r"color.debug\s+.\[0m\s.\[38;5;4mcolor4\s+.\[0m") def test_colors_legend_override(self): """Verify 'task colors legend' obeys rc overrides""" code, out, err = self.t("colors rc._forcecolor:on rc.color.debug:red legend") - self.assertRegex(out, "color.debug\s+.\[0m\s.\[31mred\s+.\[0m") + self.assertRegex(out, r"color.debug\s+.\[0m\s.\[31mred\s+.\[0m") if __name__ == "__main__": from simpletap import TAPTestRunner diff --git a/test/color.rules.test.py b/test/color.rules.test.py index f6a52ee21..394557c36 100755 --- a/test/color.rules.test.py +++ b/test/color.rules.test.py @@ -127,7 +127,7 @@ class TestColorRules(TestCase): """Overdue color rule from an hour ago.""" code, out, err = self.t('/anhourago/ info') # Match 4-bit or 8-bit blue color code - self.assertRegex(out, '\x1b\[(38;5;4|34)m') + self.assertRegex(out, '\x1b\\[(38;5;4|34)m') def test_due_tomorrow(self): """Due tomorrow color rule.""" diff --git a/test/columns.test.py b/test/columns.test.py index 4128f9459..89ab32a4c 100755 --- a/test/columns.test.py +++ b/test/columns.test.py @@ -243,14 +243,14 @@ class TestRecurringAttributeFormats(TestCase): def test_recurrence_formats_short(self): """Verify formatting of assorted short recurrence columns""" code, out, err = self.t("xxx rc.report.xxx.columns:id,status,due,recur.indicator,mask,imask,parent.short") - self.assertRegex(out, "1\sRecurring\s+\d{4}-\d{2}-\d{2}\s+R\s+-") - self.assertRegex(out, "2\sPending\s+\d{4}-\d{2}-\d{2}\s+R\s+0\s+[0-9a-fA-F]{8}") + self.assertRegex(out, r"1\sRecurring\s+\d{4}-\d{2}-\d{2}\s+R\s+-") + self.assertRegex(out, r"2\sPending\s+\d{4}-\d{2}-\d{2}\s+R\s+0\s+[0-9a-fA-F]{8}") def test_recurrence_formats_long(self): """Verify formatting of assorted long recurrence columns""" code, out, err = self.t("xxx rc.report.xxx.columns:id,status,due,recur.duration,mask,imask,parent.long") - self.assertRegex(out, "1\sRecurring\s+\d{4}-\d{2}-\d{2}\s+P30D\s+-") - self.assertRegex(out, "2\sPending\s+\d{4}-\d{2}-\d{2}\s+P30D\s+0\s+[0-9a-fA-F-]{36}") + self.assertRegex(out, r"1\sRecurring\s+\d{4}-\d{2}-\d{2}\s+P30D\s+-") + self.assertRegex(out, r"2\sPending\s+\d{4}-\d{2}-\d{2}\s+P30D\s+0\s+[0-9a-fA-F-]{36}") def test_recurrence_format_unrecognized(self): """Verify *.donkey formatting fails""" diff --git a/test/commands.test.py b/test/commands.test.py index f59bf6ec9..9dfafd0bb 100755 --- a/test/commands.test.py +++ b/test/commands.test.py @@ -42,16 +42,16 @@ class TestCommands(TestCase): def test_command_dna(self): """Verify 'add', 'modify', 'list' dna""" code, out, err = self.t("commands") - self.assertRegex(out, "add\s+operation\s+RW\s+Ctxt\s+Mods\s+Adds a new task") - self.assertRegex(out, "list\s+report\s+RO\s+ID\s+GC\s+Ctxt\s+Filt\s+Most details of") - self.assertRegex(out, "modify\s+operation\s+RW\s+Filt\s+Mods\s+Modifies the") + self.assertRegex(out, r"add\s+operation\s+RW\s+Ctxt\s+Mods\s+Adds a new task") + self.assertRegex(out, r"list\s+report\s+RO\s+ID\s+GC\s+Ctxt\s+Filt\s+Most details of") + self.assertRegex(out, r"modify\s+operation\s+RW\s+Filt\s+Mods\s+Modifies the") def test_command_dna_color(self): """Verify 'add', 'modify', 'list' dna""" code, out, err = self.t("commands rc._forcecolor:on") - self.assertRegex(out, "add\s+operation\s+RW\s+Ctxt\s+Mods\s+Adds a new task") - self.assertRegex(out, "list\s+report\s+RO\s+ID\s+GC\s+Ctxt\s+Filt\s+Most details of") - self.assertRegex(out, "modify\s+operation\s+RW\s+Filt\s+Mods\s+Modifies the") + self.assertRegex(out, r"add\s+operation\s+RW\s+Ctxt\s+Mods\s+Adds a new task") + self.assertRegex(out, r"list\s+report\s+RO\s+ID\s+GC\s+Ctxt\s+Filt\s+Most details of") + self.assertRegex(out, r"modify\s+operation\s+RW\s+Filt\s+Mods\s+Modifies the") if __name__ == "__main__": diff --git a/test/context.test.py b/test/context.test.py index cd9234596..d2ae70cd3 100755 --- a/test/context.test.py +++ b/test/context.test.py @@ -204,7 +204,7 @@ class ContextManagementTest(TestCase): """Test that context is unset if its definition has been removed.""" self.t('context define work project:Work', input='y\ny\n') self.t('context work') - code, out, err = self.t('context delete work', input='y\n\y\n') + code, out, err = self.t('context delete work', input='y\ny\n') self.assertIn("Context 'work' deleted.", out) # Assert that taskrc does not countain context work definition diff --git a/test/custom.tag_ind.test.py b/test/custom.tag_ind.test.py index 7bb8e8cdd..04da051ab 100755 --- a/test/custom.tag_ind.test.py +++ b/test/custom.tag_ind.test.py @@ -52,12 +52,12 @@ class TestCustomTagIndicator(TestCase): """Verify default tag indicator (+) is shown""" code, out, err = self.t("foo") self.assertRegex(out, "ID.+T") - self.assertRegex(out, "1\s+\+") + self.assertRegex(out, r"1\s+\+") def test_custom_indicator(self): """Verify custom tag indicator (TAG) is shown""" code, out, err = self.t("rc.tag.indicator:TAG foo") - self.assertRegex(out, "1\s+TAG") + self.assertRegex(out, r"1\s+TAG") if __name__ == "__main__": diff --git a/test/custom.test.py b/test/custom.test.py index ca6a36b6b..2c8949057 100755 --- a/test/custom.test.py +++ b/test/custom.test.py @@ -48,7 +48,7 @@ class TestCustomReports(TestCase): def test_custom_report_help(self): """Verify custom report description is shown in help""" code, out, err = self.t("help") - self.assertRegex(out, "task foo\s+DESC\n") + self.assertRegex(out, "task foo\\s+DESC\n") def test_custom_filter(self): """Verify custome report filtr is applied""" diff --git a/test/default.test.py b/test/default.test.py index 8d7339a3b..daed3107f 100755 --- a/test/default.test.py +++ b/test/default.test.py @@ -53,7 +53,7 @@ class TestCMD(TestCase): def test_info_command(self): """info command""" code, out, err = self.t('1') - self.assertRegex(out, 'Description\s+one') + self.assertRegex(out, r'Description\s+one') class TestDefaults(TestCase): diff --git a/test/diag.test.py b/test/diag.test.py index f19b3e8ce..85d781df9 100755 --- a/test/diag.test.py +++ b/test/diag.test.py @@ -50,7 +50,7 @@ class TestDiagnostics(TestCase): self.t.activate_hooks() code, out, err = self.t.diag() self.tap(out) - self.assertRegex(out, "Compliance:\s+C\+\+17") + self.assertRegex(out, r"Compliance:\s+C\+\+17") self.assertIn("edlin", out) self.assertIn("Locking", out) diff --git a/test/due.test.py b/test/due.test.py index 82792473f..060980135 100755 --- a/test/due.test.py +++ b/test/due.test.py @@ -64,8 +64,8 @@ class TestDue(TestCase): def test_due(self): """due tasks displayed correctly""" code, out, err = self.t("list") - self.assertRegex(out, "\033\[31m.+{0}.+\033\[0m".format(self.just)) - self.assertRegex(out, "\s+{0}\s+".format(self.almost)) + self.assertRegex(out, "\033\\[31m.+{0}.+\033\\[0m".format(self.just)) + self.assertRegex(out, r"\s+{0}\s+".format(self.almost)) class TestBug418(TestCase): diff --git a/test/encoding.test.py b/test/encoding.test.py index 9fc3409a4..89e55e0ad 100755 --- a/test/encoding.test.py +++ b/test/encoding.test.py @@ -70,9 +70,9 @@ class TestUtf8(TestCase): code, out, err = self.t("ls") - expected = re.compile("\S\s{4}abc", re.MULTILINE) + expected = re.compile(r"\S\s{4}abc", re.MULTILINE) self.assertRegex(out, expected) - expected = re.compile("\S\s{5}def", re.MULTILINE) + expected = re.compile(r"\S\s{5}def", re.MULTILINE) self.assertRegex(out, expected) diff --git a/test/feature.default.project.test.py b/test/feature.default.project.test.py index 394ddda8a..4e32f6c08 100755 --- a/test/feature.default.project.test.py +++ b/test/feature.default.project.test.py @@ -55,7 +55,7 @@ class TestDefaultProject(TestCase): self.assertIn("foobar", out) - expected = "Project\s+garden" + expected = r"Project\s+garden" self.assertRegex(out, expected) self.t("1 modify project:") @@ -64,7 +64,7 @@ class TestDefaultProject(TestCase): self.assertIn("foobar", out) self.assertNotRegex(out, expected) - notexpected = "Project\s+" + self.default_project + notexpected = r"Project\s+" + self.default_project self.assertNotRegex(out, notexpected) def test_without_project(self): @@ -76,7 +76,7 @@ class TestDefaultProject(TestCase): self.assertIn("foobar", out) - expected = "Project\s+" + self.default_project + expected = r"Project\s+" + self.default_project self.assertRegex(out, expected) def test_default_project_inline_override(self): @@ -124,7 +124,7 @@ class TestDefaultProject(TestCase): self.t("1 annotate Hello") code, out, err = self.t("1 info") - expected = "Description\s+foobar\n[0-9-: ]+ Hello" + expected = "Description\\s+foobar\n[0-9-: ]+ Hello" self.assertRegex(out, expected) self.assertNotIn("Project", out) @@ -158,7 +158,7 @@ class TestDefaultProject(TestCase): code, out, err = self.t("1 info") self.assertIn(DESC, out) - self.assertRegex(out, "Status\s+Recurring") # is a parent task + self.assertRegex(out, r"Status\s+Recurring") # is a parent task self.assertIn(self.default_project, out) self.t.faketime("+1d") diff --git a/test/feature.recurrence.test.py b/test/feature.recurrence.test.py index fd97bc341..69e18e0ca 100755 --- a/test/feature.recurrence.test.py +++ b/test/feature.recurrence.test.py @@ -62,8 +62,8 @@ class TestRecurrenceProblems(TestCase): self.t("add foo due:today recur:yearly until:eom") code, out, err = self.t("info 1") - self.assertNotRegex(out, "Until\s+\d{10}") - self.assertRegex(out, "Until\s+\d+\/\d+\/\d{4}") + self.assertNotRegex(out, r"Until\s+\d{10}") + self.assertRegex(out, r"Until\s+\d+\/\d+\/\d{4}") if __name__ == "__main__": diff --git a/test/format.test.py b/test/format.test.py index 30b192292..b82a7e6b9 100755 --- a/test/format.test.py +++ b/test/format.test.py @@ -118,12 +118,12 @@ class TestFormatDepends(TestCase): def test_depends_default(self): self.t.config("report.formatdep.columns", "description,depends") code, out, err = self.t("formatdep") - self.assertRegex(out, "one\s+1") + self.assertRegex(out, r"one\s+1") def test_depends_count(self): self.t.config("report.formatdep.columns", "description,depends.count") code, out, err = self.t("formatdep") - self.assertRegex(out, "one\s+\[1\]") + self.assertRegex(out, r"one\s+\[1\]") class TestBug101(TestCase): diff --git a/test/gc.test.py b/test/gc.test.py index 5b38e1f86..06ec60f78 100755 --- a/test/gc.test.py +++ b/test/gc.test.py @@ -50,7 +50,7 @@ class TestGC(TestCase): self.t.config("gc", "0") self.t("1 done") code, out, err = self.t("gctest") - self.assertRegex(out, "1\s+one", "should still have ID") + self.assertRegex(out, r"1\s+one", "should still have ID") def test_gc_off_mod(self): """mod by ID after done with gc off""" @@ -59,7 +59,7 @@ class TestGC(TestCase): self.t("gctest") self.t("2 mod +TWO") code, out, err = self.t("gctest") - self.assertRegex(out, "2\s+two\s+TWO", "modified 'two'") + self.assertRegex(out, r"2\s+two\s+TWO", "modified 'two'") def test_gc_on_id(self): """IDs reshuffle after report when GC on""" @@ -67,8 +67,8 @@ class TestGC(TestCase): self.t("1 done") self.t("2 mod +TWO") code, out, err = self.t("gctest") - self.assertRegex(out, "1\s+two\s+TWO") - self.assertRegex(out, "2\s+three") + self.assertRegex(out, r"1\s+two\s+TWO") + self.assertRegex(out, r"2\s+three") if __name__ == "__main__": diff --git a/test/history.test.py b/test/history.test.py index 1232eaf65..27faedfb6 100755 --- a/test/history.test.py +++ b/test/history.test.py @@ -55,17 +55,17 @@ class TestHistoryDaily(TestCase): def test_history_daily(self): """Verify 'history.daily' correctly categorizes data""" code, out, err = self.t("history.daily") - self.assertRegex(out, "7\s+1\s+0\s+6") - self.assertRegex(out, "2\s+3\s+3\s+-4") - self.assertRegex(out, "4\s+2\s+1\s+1") + self.assertRegex(out, r"7\s+1\s+0\s+6") + self.assertRegex(out, r"2\s+3\s+3\s+-4") + self.assertRegex(out, r"4\s+2\s+1\s+1") code, out, err = self.t("ghistory.daily rc._forcecolor:on") - self.assertRegex(out, "\s7.+\s1.+") - self.assertRegex(out, "\s2.+\s3.+\s3.+") + self.assertRegex(out, r"\s7.+\s1.+") + self.assertRegex(out, r"\s2.+\s3.+\s3.+") code, out, err = self.t("ghistory.daily") - self.assertRegex(out, "2015\s+January\s+2\s+\++X+\s") - self.assertRegex(out, "\s+February\s+2\s+\++X+\-+") + self.assertRegex(out, r"2015\s+January\s+2\s+\++X+\s") + self.assertRegex(out, r"\s+February\s+2\s+\++X+\-+") class TestHistoryWeekly(TestCase): def setUp(self): @@ -88,17 +88,17 @@ class TestHistoryWeekly(TestCase): def test_history_weekly(self): """Verify 'history.weekly' correctly categorizes data""" code, out, err = self.t("history.weekly") - self.assertRegex(out, "7\s+1\s+0\s+6") - self.assertRegex(out, "2\s+3\s+3\s+-4") - self.assertRegex(out, "4\s+2\s+1\s+1") + self.assertRegex(out, r"7\s+1\s+0\s+6") + self.assertRegex(out, r"2\s+3\s+3\s+-4") + self.assertRegex(out, r"4\s+2\s+1\s+1") code, out, err = self.t("ghistory.weekly rc._forcecolor:on") - self.assertRegex(out, "\s7.+\s1.+") - self.assertRegex(out, "\s2.+\s3.+\s3.+") + self.assertRegex(out, r"\s7.+\s1.+") + self.assertRegex(out, r"\s2.+\s3.+\s3.+") code, out, err = self.t("ghistory.weekly") - self.assertRegex(out, "2014\s+December\s+28\s+\++X+\s") - self.assertRegex(out, "2015\s+February\s+1\s+\++X+\-+") + self.assertRegex(out, r"2014\s+December\s+28\s+\++X+\s") + self.assertRegex(out, r"2015\s+February\s+1\s+\++X+\-+") class TestHistoryMonthly(TestCase): @@ -122,17 +122,17 @@ class TestHistoryMonthly(TestCase): def test_history_monthly(self): """Verify 'history.monthly' correctly categorizes data""" code, out, err = self.t("history.monthly") - self.assertRegex(out, "7\s+1\s+0\s+6") - self.assertRegex(out, "2\s+3\s+3\s+-4") - self.assertRegex(out, "4\s+2\s+1\s+1") + self.assertRegex(out, r"7\s+1\s+0\s+6") + self.assertRegex(out, r"2\s+3\s+3\s+-4") + self.assertRegex(out, r"4\s+2\s+1\s+1") code, out, err = self.t("ghistory.monthly rc._forcecolor:on") - self.assertRegex(out, "\s7.+\s1.+") - self.assertRegex(out, "\s2.+\s3.+\s3.+") + self.assertRegex(out, r"\s7.+\s1.+") + self.assertRegex(out, r"\s2.+\s3.+\s3.+") code, out, err = self.t("ghistory.monthly") - self.assertRegex(out, "2015\s+January\s+\++X+\s") - self.assertRegex(out, "\s+February\s+\++X+\-+") + self.assertRegex(out, r"2015\s+January\s+\++X+\s") + self.assertRegex(out, r"\s+February\s+\++X+\-+") class TestHistoryAnnual(TestCase): @@ -160,17 +160,17 @@ class TestHistoryAnnual(TestCase): def test_history_annual(self): """Verify 'history.annual' correctly categorizes data""" code, out, err = self.t("history.annual") - self.assertRegex(out, "7\s+1\s+0\s+6") - self.assertRegex(out, "2\s+3\s+3\s+-4") - self.assertRegex(out, "4\s+2\s+1\s+1") + self.assertRegex(out, r"7\s+1\s+0\s+6") + self.assertRegex(out, r"2\s+3\s+3\s+-4") + self.assertRegex(out, r"4\s+2\s+1\s+1") code, out, err = self.t("ghistory.annual rc._forcecolor:on") - self.assertRegex(out, "\s7.+\s1.+") - self.assertRegex(out, "\s2.+\s3.+\s3.+") + self.assertRegex(out, r"\s7.+\s1.+") + self.assertRegex(out, r"\s2.+\s3.+\s3.+") code, out, err = self.t("ghistory.annual") - self.assertRegex(out, "2014\s+\++X+\s") - self.assertRegex(out, "2015\s+\++X+\-+") + self.assertRegex(out, r"2014\s+\++X+\s") + self.assertRegex(out, r"2015\s+\++X+\-+") if __name__ == "__main__": from simpletap import TAPTestRunner diff --git a/test/info.test.py b/test/info.test.py index 23181db32..42194757b 100755 --- a/test/info.test.py +++ b/test/info.test.py @@ -64,21 +64,21 @@ class TestInfoCommand(TestCase): self.t("1 annotate bar", input="n\n") code, out, err = self.t("1 info") - self.assertRegex(out, "ID\s+1") - self.assertRegex(out, "Description\s+foo") - self.assertRegex(out, "\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s+bar") - self.assertRegex(out, "Status\s+Recurring") - self.assertRegex(out, "Project\s+P") - self.assertRegex(out, "Recurrence\s+P1M") - self.assertRegex(out, "Entered\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") - self.assertRegex(out, "Waiting until\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") - self.assertRegex(out, "Scheduled\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") - self.assertRegex(out, "Start\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") - self.assertRegex(out, "Due\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") - self.assertRegex(out, "Until\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") - self.assertRegex(out, "Last modified\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") + self.assertRegex(out, r"ID\s+1") + self.assertRegex(out, r"Description\s+foo") + self.assertRegex(out, r"\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s+bar") + self.assertRegex(out, r"Status\s+Recurring") + self.assertRegex(out, r"Project\s+P") + self.assertRegex(out, r"Recurrence\s+P1M") + self.assertRegex(out, r"Entered\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") + self.assertRegex(out, r"Waiting until\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") + self.assertRegex(out, r"Scheduled\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") + self.assertRegex(out, r"Start\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") + self.assertRegex(out, r"Due\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") + self.assertRegex(out, r"Until\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") + self.assertRegex(out, r"Last modified\s+\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}") - self.assertRegex(out, "Tags\s+tag") + self.assertRegex(out, r"Tags\s+tag") self.assertIn("ACTIVE", out) self.assertIn("ANNOTATED", out) self.assertIn("MONTH", out) @@ -89,9 +89,9 @@ class TestInfoCommand(TestCase): self.assertIn("YEAR", out) self.assertIn("UDA", out) - self.assertRegex(out, "UUID\s+[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") - self.assertRegex(out, "Urgency\s+\d+(\.\d+)?") - self.assertRegex(out, "Priority\s+H") + self.assertRegex(out, r"UUID\s+[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") + self.assertRegex(out, r"Urgency\s+\d+(\.\d+)?") + self.assertRegex(out, r"Priority\s+H") self.assertIn("project", out) self.assertIn("active", out) @@ -116,7 +116,7 @@ class TestBug425(TestCase): self.t("1 modify Bar in Bar") code, out, err = self.t("1 ls") - self.assertRegex(out, "1\s+Bar in Bar") + self.assertRegex(out, r"1\s+Bar in Bar") if __name__ == "__main__": diff --git a/test/logo.test.py b/test/logo.test.py index a1a257682..6cd1e8233 100755 --- a/test/logo.test.py +++ b/test/logo.test.py @@ -43,7 +43,7 @@ class TestLogoCommand(TestCase): def test_logo_command(self): """Check that there are colors. For coverage""" code, out, err = self.t("logo rc._forcecolor:on") - self.assertRegex(out, ".\[48;5;\d+m .\[0m") + self.assertRegex(out, r".\[48;5;\d+m .\[0m") def test_logo_command_no_color(self): """Check that it only works with color. For coverage""" diff --git a/test/project.test.py b/test/project.test.py index 626102f2e..c7cfbbdf5 100755 --- a/test/project.test.py +++ b/test/project.test.py @@ -39,8 +39,8 @@ class TestProjects(TestCase): def setUp(self): self.t = Task() - self.STATUS = ("The project '{0}' has changed\. " - "Project '{0}' is {1} complete \({2} remaining\)\.") + self.STATUS = (r"The project '{0}' has changed\. " + r"Project '{0}' is {1} complete \({2} remaining\)\.") def test_project_summary_count(self): """'task projects' shouldn't consider deleted tasks in summary. @@ -52,7 +52,7 @@ class TestProjects(TestCase): self.t("3 delete", input="y\n") code, out, err = self.t("project:B projects") - expected = "1 project \(1 task\)" + expected = r"1 project \(1 task\)" self.assertRegex(out, expected) def test_project_progress(self): @@ -415,16 +415,16 @@ class TestBug899(TestCase): def test_log_project(self): """899: Verify task log behaves correctly when logging into a project""" code, out, err = self.t("add one pro:A") - self.assertRegex(err, " 0% complete \(1 task ") + self.assertRegex(err, r" 0% complete \(1 task ") code, out, err = self.t("add two pro:A") - self.assertRegex(err, " 0% complete \(2 of 2 ") + self.assertRegex(err, r" 0% complete \(2 of 2 ") code, out, err = self.t("1 done") - self.assertRegex(err, " 50% complete \(1 of 2 ") + self.assertRegex(err, r" 50% complete \(1 of 2 ") code, out, err = self.t("log three pro:A") - self.assertRegex(err, " 66% complete \(1 of 3 ") + self.assertRegex(err, r" 66% complete \(1 of 3 ") class TestBug1267(TestCase): diff --git a/test/quotes.test.py b/test/quotes.test.py index 9f962b595..e35921ac6 100755 --- a/test/quotes.test.py +++ b/test/quotes.test.py @@ -61,7 +61,7 @@ class TestBug268(TestCase): """escaped backslashes do not work with 'modify'""" self.t("add a b or c") - self.t('1 modify "/a b/a\/b/"') + self.t(r'1 modify "/a b/a\/b/"') code, out, err = self.t("1 info") self.assertIn("a/b or c", out) @@ -125,7 +125,7 @@ class TestBug1436(TestCase): def test_backslashes(self): """1436: Prove to the reader that backslashes are eaten twice (which means - \\ --> \) once by Python, and once more by some mystery process + two backslashes to one) once by Python, and once more by some mystery process launch thing. This problem is entirely testing artifact, and not Taskwarrior. diff --git a/test/recurrence.test.py b/test/recurrence.test.py index fa6268431..2a5c4e248 100755 --- a/test/recurrence.test.py +++ b/test/recurrence.test.py @@ -59,12 +59,12 @@ class TestRecurrenceSorting(TestCase): def test_sort_ascending(self): """Verify sorting by 'recur+' is correct""" code, out, err = self.t("asc rc.verbose:nothing") - self.assertRegex(out, "4\s+P1D\s+one\s+6\s+P3D\s+three\s+5\s+P7D\s+two") + self.assertRegex(out, r"4\s+P1D\s+one\s+6\s+P3D\s+three\s+5\s+P7D\s+two") def test_sort_descending(self): """Verify sorting by 'recur-' is correct""" code, out, err = self.t("desc rc.verbose:nothing") - self.assertRegex(out, "5\s+P7D\s+two\s+6\s+P3D\s+three\s+4\s+P1D\s+one") + self.assertRegex(out, r"5\s+P7D\s+two\s+6\s+P3D\s+three\s+4\s+P1D\s+one") class TestRecurrenceDisabled(TestCase): @@ -179,9 +179,9 @@ class TestRecurrenceTasks(TestCase): # 3 complex # 4 complex code, out, err = self.t("minimal rc.verbose:nothing") - self.assertRegex(out, "1\s+simple") - self.assertRegex(out, "3\s+complex") - self.assertRegex(out, "4\s+complex") + self.assertRegex(out, r"1\s+simple") + self.assertRegex(out, r"3\s+complex") + self.assertRegex(out, r"4\s+complex") # Modify a child task and do not propagate the change. self.t("3 modify complex2", input="n\n") diff --git a/test/search.test.py b/test/search.test.py index 08491e6b6..7cf460ce2 100755 --- a/test/search.test.py +++ b/test/search.test.py @@ -263,7 +263,7 @@ class TestBug1479(TestCase): self.t("add project:P1 one") self.t("add project:P2 one two") - code, out, err = self.t("description:one\ two list") + code, out, err = self.t(r"description:one\ two list") self.assertNotIn("P1", out) self.assertIn("P2", out) diff --git a/test/sequence.test.py b/test/sequence.test.py index 33a6e826c..ef14a5b19 100755 --- a/test/sequence.test.py +++ b/test/sequence.test.py @@ -58,7 +58,7 @@ class TestSequences(TestCase): """Test sequences in start/stop""" self.t("1,2 start") code, out, err = self.t("_get 1.start 2.start") - self.assertRegex(out, "\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2} \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\n") + self.assertRegex(out, "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2} \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\n") self.t("1,2 stop") code, out, err = self.t("_get 1.start 2.start") diff --git a/test/stats.test.py b/test/stats.test.py index cc1257964..8d41e7ea8 100755 --- a/test/stats.test.py +++ b/test/stats.test.py @@ -48,13 +48,13 @@ class TestStatisticsCommand(TestCase): self.t("log three") code, out, err = self.t("stats") - self.assertRegex(out, "Pending\s+1\n") - self.assertRegex(out, "Completed\s+1\n") - self.assertRegex(out, "Deleted\s+1\n") - self.assertRegex(out, "Total\s+3\n") + self.assertRegex(out, "Pending\\s+1\n") + self.assertRegex(out, "Completed\\s+1\n") + self.assertRegex(out, "Deleted\\s+1\n") + self.assertRegex(out, "Total\\s+3\n") code, out, err = self.t("stats rc._forcecolor:on") - self.assertRegex(out, "Pending\s+1\n") + self.assertRegex(out, "Pending\\s+1\n") if __name__ == "__main__": diff --git a/test/template.test.py b/test/template.test.py index a41273f8d..568f3ede5 100755 --- a/test/template.test.py +++ b/test/template.test.py @@ -72,7 +72,7 @@ class TestBugNumber(TestCase): """Copyright is current""" code, out, err = self.t("version") - expected = "Copyright \(C\) \d{4} - %d" % (datetime.now().year,) + expected = r"Copyright \(C\) \d{4} - %d" % (datetime.now().year,) self.assertRegex(out.decode("utf8"), expected) # TAP diagnostics on the bas diff --git a/test/tw-1379.test.py b/test/tw-1379.test.py index 07077ecbc..edb6d5304 100755 --- a/test/tw-1379.test.py +++ b/test/tw-1379.test.py @@ -49,8 +49,8 @@ class TestBug1379(TestCase): self.t.config("color.label.sort", "") # For use with regex - self.RED = "\033\[31m" - self.CLEAR = "\033\[0m" + self.RED = "\033\\[31m" + self.CLEAR = "\033\\[0m" def test_color_BLOCKED(self): """color.tag.BLOCKED changes color of BLOCKED tasks""" diff --git a/test/uda.test.py b/test/uda.test.py index 498642294..b219e964c 100755 --- a/test/uda.test.py +++ b/test/uda.test.py @@ -103,8 +103,8 @@ class TestUdaDate(TestBaseUda): self.assertIn("Created task", out) code, out, err = self.t("uda") - self.assertRegex(out, "1\s+[\d\/]+\s+with") - self.assertRegex(out, "2\s+without") + self.assertRegex(out, r"1\s+[\d\/]+\s+with") + self.assertRegex(out, r"2\s+without") def test_uda_bad_date_task(self): """Add tasks with an invalid UDA date""" @@ -134,7 +134,7 @@ class TestUdaDefault(TestBaseUda): self.assertIn("Created task", out) code, out, err = self.t("uda") - self.assertRegex(out, "1\s+strong\s+one") + self.assertRegex(out, r"1\s+strong\s+one") def test_uda_default_task(self): """Add tasks with default UDA""" @@ -143,7 +143,7 @@ class TestUdaDefault(TestBaseUda): self.assertIn("Created task", out) code, out, err = self.t("uda") - self.assertRegex(out, "1\s+weak\s+two") + self.assertRegex(out, r"1\s+weak\s+two") def test_uda_without_default_task(self): """Add tasks without default UDA""" @@ -152,7 +152,7 @@ class TestUdaDefault(TestBaseUda): self.assertIn("Created task", out) code, out, err = self.t("uda") - self.assertRegex(out, "1\s+weak\s+10\s+three") + self.assertRegex(out, r"1\s+weak\s+10\s+three") class TestUdaDuration(TestBaseUda): @@ -200,8 +200,8 @@ class TestUdaNumeric(TestBaseUda): self.assertIn("Created task", out) code, out, err = self.t("uda") - self.assertRegex(out, "1\s+\d+\s+with") - self.assertRegex(out, "2\s+without") + self.assertRegex(out, r"1\s+\d+\s+with") + self.assertRegex(out, r"2\s+without") def test_uda_bad_numeric_task(self): """Add tasks with an invalid UDA numeric""" @@ -225,8 +225,8 @@ class TestUdaString(TestBaseUda): self.assertIn("Created task", out) code, out, err = self.t("uda") - self.assertRegex(out, "1\s+one two\s+with") - self.assertRegex(out, "2\s+without") + self.assertRegex(out, r"1\s+one two\s+with") + self.assertRegex(out, r"2\s+without") class TestUdaValue(TestBaseUda): @@ -245,8 +245,8 @@ class TestUdaValue(TestBaseUda): self.assertIn("Created task", out) code, out, err = self.t("uda") - self.assertRegex(out, "1\s+weak\s+one") - self.assertRegex(out, "2\s+strong\s+two") + self.assertRegex(out, r"1\s+weak\s+one") + self.assertRegex(out, r"2\s+strong\s+two") def test_uda_invalid_value_task(self): """Add tasks with invalid UDA value""" @@ -259,8 +259,8 @@ class TestUdaValue(TestBaseUda): "'toxic'", err) code, out, err = self.t("uda") - self.assertRegex(out, "1\s+strong\s+one") - self.assertNotRegex(out, "1\s+toxic\s+two") + self.assertRegex(out, r"1\s+strong\s+one") + self.assertNotRegex(out, r"1\s+toxic\s+two") class TestBug1063(TestCase): diff --git a/test/uda_orphan.test.py b/test/uda_orphan.test.py index 99984ca35..edd624630 100755 --- a/test/uda_orphan.test.py +++ b/test/uda_orphan.test.py @@ -56,12 +56,12 @@ class TestUDAOrphans(TestCase): # 'info' should show orphans. code, out, err = self.t("1 info") - self.assertRegex(out, "\[extra\s+foo\]") + self.assertRegex(out, r"\[extra\s+foo\]") # 'modify' should not change the orphan self.t("1 modify /one/two/") code, out, err = self.t("1 info") - self.assertRegex(out, "\[extra\s+foo\]") + self.assertRegex(out, r"\[extra\s+foo\]") # 'export' should include orphans. code, out, err = self.t("1 export") diff --git a/test/uda_sort.test.py b/test/uda_sort.test.py index 92ae964d6..922100d72 100755 --- a/test/uda_sort.test.py +++ b/test/uda_sort.test.py @@ -147,7 +147,7 @@ class TestBug1319(TestCase): self.t("add four when:morning") code, out, err = self.t("rc.verbose:nothing foo") - self.assertRegex(out, "4\s+morning\s+four\s+3\s+noon\s+three\s+2\s+evening\s+two\s+1\s+night\s+one") + self.assertRegex(out, r"4\s+morning\s+four\s+3\s+noon\s+three\s+2\s+evening\s+two\s+1\s+night\s+one") if __name__ == "__main__": diff --git a/test/undo.test.py b/test/undo.test.py index 55fedb789..a2ef50847 100755 --- a/test/undo.test.py +++ b/test/undo.test.py @@ -79,16 +79,16 @@ class TestUndoStyle(TestCase): """Test that 'rc.undo.style:side' generates the right output""" self.t.config("undo.style", "side") code, out, err = self.t("undo", input="n\n") - self.assertNotRegex(out, "-tags:\s*\n\+tags:\s+tag") - self.assertRegex(out, "tags\s+tag\s*") + self.assertNotRegex(out, "-tags:\\s*\n\\+tags:\\s+tag") + self.assertRegex(out, r"tags\s+tag\s*") @unittest.expectedFailure # undo diffs are not supported def test_undo_diff_style(self): """Test that 'rc.undo.style:diff' generates the right output""" self.t.config("undo.style", "diff") code, out, err = self.t("undo", input="n\n") - self.assertRegex(out, "-tags:\s*\n\+tags:\s+tag") - self.assertNotRegex(out, "tags\s+tag\s*") + self.assertRegex(out, "-tags:\\s*\n\\+tags:\\s+tag") + self.assertNotRegex(out, r"tags\s+tag\s*") def test_undo_diff_operations(self): code, out, err = self.t("undo", input="n\n") diff --git a/test/upgrade.test.py b/test/upgrade.test.py index 1fdc52813..f9df76eb3 100755 --- a/test/upgrade.test.py +++ b/test/upgrade.test.py @@ -44,19 +44,19 @@ class TestUpgrade(TestCase): self.t("add one") code, out, err = self.t("1 info") - self.assertRegex(out, "Status\s+Pending") + self.assertRegex(out, r"Status\s+Pending") self.t("1 modify due:tomorrow recur:weekly") self.t("list") code, out, err = self.t("1 info") - self.assertRegex(out, "Status\s+Recurring") - self.assertRegex(out, "Recurrence\s+weekly") + self.assertRegex(out, r"Status\s+Recurring") + self.assertRegex(out, r"Recurrence\s+weekly") # Also check for the presence of a children task with pending state code, out, err = self.t("2 info") - self.assertRegex(out, "Status\s+Pending") - self.assertRegex(out, "Recurrence\s+weekly") + self.assertRegex(out, r"Status\s+Pending") + self.assertRegex(out, r"Recurrence\s+weekly") if __name__ == "__main__": diff --git a/test/version.test.py b/test/version.test.py index 2e4dfc5a0..ecdafa5a3 100755 --- a/test/version.test.py +++ b/test/version.test.py @@ -54,12 +54,12 @@ class TestVersion(TestCase): """Copyright is current""" code, out, err = self.t("version") - expected = "Copyright \(C\) \d{4} - %d" % (datetime.now().year,) + expected = r"Copyright \(C\) \d{4} - %d" % (datetime.now().year,) self.assertRegex(out, expected) def slurp(self, file="../CMakeLists.txt"): - number = "\.".join(["[0-9]+"] * 3) - ver = re.compile("^set \(PROJECT_VERSION \"({0}[^\"]*)\"\)$".format(number)) + number = r"\.".join(["[0-9]+"] * 3) + ver = re.compile("^set \\(PROJECT_VERSION \"({0}[^\"]*)\"\\)$".format(number)) with open(file) as fh: for line in fh: if "PROJECT_VERSION" in line: From 52dbecb515fe6bb750cdbaed16fb8bb8eaf0b3ee Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Fri, 3 May 2024 03:26:10 +0300 Subject: [PATCH 012/242] remove .gitignore files and symbolic links/aliases (#3421) * remove symbollic links in the src directory as they are no longer working with the out-of-source build * remove .gitignore in the documentation (is build in build folder not needed) * remove CMake folders as they are also no longer present in the source directory Closes #3420. --- .gitignore | 5 +---- doc/man/.gitignore | 4 ---- src/.gitignore | 5 ----- src/cal | 1 - src/calendar | 1 - src/tw | 1 - 6 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 doc/man/.gitignore delete mode 120000 src/cal delete mode 120000 src/calendar delete mode 120000 src/tw diff --git a/.gitignore b/.gitignore index af7096a46..4ac379c09 100644 --- a/.gitignore +++ b/.gitignore @@ -4,11 +4,8 @@ commit.h .*.swp Session.vim package-config/osx/binary/task -/build*/ -install_manifest.txt -_CPack_Packages +/*build*/ patches -*.exe tutorials .prove /target/ diff --git a/doc/man/.gitignore b/doc/man/.gitignore deleted file mode 100644 index 811e2c712..000000000 --- a/doc/man/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -task-color.5 -task-sync.5 -task.1 -taskrc.5 diff --git a/src/.gitignore b/src/.gitignore index 99a78c14e..1153e9327 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,6 +1 @@ -*.o -Makefile.in -debug -calc -lex liblibshared.a diff --git a/src/cal b/src/cal deleted file mode 120000 index c077ac0c7..000000000 --- a/src/cal +++ /dev/null @@ -1 +0,0 @@ -task \ No newline at end of file diff --git a/src/calendar b/src/calendar deleted file mode 120000 index c077ac0c7..000000000 --- a/src/calendar +++ /dev/null @@ -1 +0,0 @@ -task \ No newline at end of file diff --git a/src/tw b/src/tw deleted file mode 120000 index c077ac0c7..000000000 --- a/src/tw +++ /dev/null @@ -1 +0,0 @@ -task \ No newline at end of file From 50cfbe8b63409ba0a712c4d111da9b9d9f41bc93 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 3 May 2024 02:30:02 +0200 Subject: [PATCH 013/242] Fix Python SyntaxError in tests/version.test.py (#3424) % `ruff check` ``` error: Failed to parse test/version.test.py:92:54: Expected a statement Error: test/version.test.py:92:54: E999 SyntaxError: Expected a statement ``` --- test/version.test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/version.test.py b/test/version.test.py index ecdafa5a3..cf30f28b4 100755 --- a/test/version.test.py +++ b/test/version.test.py @@ -50,6 +50,7 @@ class TestVersion(TestCase): self.assertIn("You must specify a command or a task to modify", err) + @unittest.skip(reason="See #3424") def test_copyright_up_to_date(self): """Copyright is current""" code, out, err = self.t("version") @@ -68,6 +69,7 @@ class TestVersion(TestCase): return match.group(1) raise ValueError("Couldn't find matching version in {0}".format(file)) + @unittest.skip(reason="See #3424") def test_version(self): """version command outputs expected version and license""" code, out, err = self.t("version") @@ -77,6 +79,7 @@ class TestVersion(TestCase): self.assertIn("MIT license", out) self.assertIn("https://taskwarrior.org", out) + @unittest.skip(reason="See #3424") def test_under_version(self): """_version and diagnostics output expected version and syntax""" code, out, err = self.t("_version") @@ -89,7 +92,7 @@ class TestVersion(TestCase): if os.path.exists("../.git"): if 2 >= len(version) > 0: git = version[1] - self.assertRegex(git, r'\([a-f0-9]*\)')) + self.assertRegex(git, r'\([a-f0-9]*\)') else: raise ValueError("Unexpected output from _version '{0}'".format( out)) From 28a46880a2664c195b274cd7ec182a0fbb3dfbfc Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 3 May 2024 09:58:09 -0400 Subject: [PATCH 014/242] Treat a nonzero exit status as a failure (#3430) And fix the test cases that have been failing ,undetected --- src/Context.cpp | 1 + src/commands/CmdVersion.cpp | 5 +++- test/basetest/utils.py | 3 +++ test/eval.test.cpp | 3 +++ test/lexer.test.cpp | 2 +- test/run_all | 3 +++ test/t.test.cpp | 49 ++++++------------------------------- test/tdb2.test.cpp | 19 +++++++------- test/test.cpp | 30 +++++++++++++++-------- test/util.test.cpp | 2 ++ test/version.test.py | 18 ++++++-------- test/view.test.cpp | 2 ++ 12 files changed, 62 insertions(+), 75 deletions(-) diff --git a/src/Context.cpp b/src/Context.cpp index ddfe9fd19..9d2c16686 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -439,6 +439,7 @@ Context* Context::context; //////////////////////////////////////////////////////////////////////////////// Context& Context::getContext () { + assert (Context::context); return *Context::context; } diff --git a/src/commands/CmdVersion.cpp b/src/commands/CmdVersion.cpp index 0797c02f3..efdf90d6d 100644 --- a/src/commands/CmdVersion.cpp +++ b/src/commands/CmdVersion.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #ifdef HAVE_COMMIT @@ -70,6 +71,8 @@ int CmdVersion::execute (std::string& output) link.add (""); link.set (link.addRow (), 0, "Documentation for Taskwarrior can be found using 'man task', 'man taskrc', 'man task-color', 'man task-sync' or at https://taskwarrior.org"); + Datetime now; + Color bold; if (Context::getContext ().color ()) bold = Color ("bold"); @@ -78,7 +81,7 @@ int CmdVersion::execute (std::string& output) << format ("{1} {2} built for ", bold.colorize (PACKAGE), bold.colorize (VERSION)) << osName () << '\n' - << "Copyright (C) 2006 - 2021 T. Babej, P. Beckingham, F. Hernandez." + << "Copyright (C) 2006 - " << now.year () << " T. Babej, P. Beckingham, F. Hernandez." << '\n' << '\n' << disclaimer.render () diff --git a/test/basetest/utils.py b/test/basetest/utils.py index c6a1970eb..ad68b8701 100644 --- a/test/basetest/utils.py +++ b/test/basetest/utils.py @@ -42,6 +42,9 @@ DEFAULT_HOOK_PATH = os.path.abspath( os.path.join("${CMAKE_SOURCE_DIR}", "test", "test_hooks") ) +# Source directory +SOURCE_DIR = os.path.abspath("${CMAKE_SOURCE_DIR}") + # Environment flags to control skipping of task tests TASKW_SKIP = os.environ.get("TASKW_SKIP", False) diff --git a/test/eval.test.cpp b/test/eval.test.cpp index 987df0617..85ed424c4 100644 --- a/test/eval.test.cpp +++ b/test/eval.test.cpp @@ -26,6 +26,7 @@ #include #include +#include #include //////////////////////////////////////////////////////////////////////////////// @@ -44,6 +45,8 @@ bool get (const std::string& name, Variant& value) int main (int, char**) { UnitTest t (52); + Context context; + Context::setContext(&context); // Test the source independently. Variant v; diff --git a/test/lexer.test.cpp b/test/lexer.test.cpp index 787b9c16a..8ebce5d4c 100644 --- a/test/lexer.test.cpp +++ b/test/lexer.test.cpp @@ -37,7 +37,7 @@ int main (int, char**) { #ifdef PRODUCT_TASKWARRIOR - UnitTest t (1253); + UnitTest t (1255); #else UnitTest t (1235); #endif diff --git a/test/run_all b/test/run_all index f20f092d1..70f060eb0 100755 --- a/test/run_all +++ b/test/run_all @@ -47,6 +47,9 @@ def run_test(testqueue, outqueue, threadname): if sys.version_info > (3,): out, err = out.decode('utf-8'), err.decode('utf-8') + if p.returncode != 0: + out = out + "\nnot ok - test executable failed\n" + output = ("# {0}\n".format(os.path.basename(test)), out, err) log.debug("Collected output %s", output) outqueue.put(output) diff --git a/test/t.test.cpp b/test/t.test.cpp index 69ac90a11..a6ea56268 100644 --- a/test/t.test.cpp +++ b/test/t.test.cpp @@ -33,6 +33,8 @@ int main (int, char**) { UnitTest test (49); + Context context; + Context::setContext(&context); // Ensure environment has no influence. unsetenv ("TASKDATA"); @@ -152,44 +154,10 @@ TODO Task::decode //////////////////////////////////////////////////////////////////////////////// Task task; - // (blank) - good = true; - try {task = Task ("");} - catch (const std::string& e){test.diag (e); good = false;} - test.notok (good, "Task::Task ('')"); - - // [] - good = true; - try {task = Task ("[]");} - catch (const std::string& e){test.diag (e); good = false;} - test.notok (good, "Task::Task ('[]')"); - - // [name:"value"] - good = true; - try {task = Task ("[name:\"value\"]");} - catch (const std::string& e){test.diag (e); good = false;} - test.ok (good, "Task::Task ('[name:\"value\"]')"); - test.is (task.get ("name"), "value", "name=value"); - - // [name:"one two"] - good = true; - try {task = Task ("[name:\"one two\"]");} - catch (const std::string& e){test.diag (e); good = false;} - test.ok (good, "Task::Task ('[name:\"one two\"]')"); - test.is (task.get ("name"), "one two", "name=one two"); - - // [one:two three:four] - good = true; - try {task = Task (R"([one:"two" three:"four"])");} - catch (const std::string& e){test.diag (e); good = false;} - test.ok (good, R"(Task::Task ('[one:"two" three:"four"]'))"); - test.is (task.get ("one"), "two", "one=two"); - test.is (task.get ("three"), "four", "three=four"); - // Task::set task = Task(); task.set ("name", "value"); - test.is (task.composeF4 (), "[name:\"value\"]", "Task::set"); + test.is (task.composeJSON (), "{\"name\":\"value\"}", "Task::set"); // Task::has test.ok (task.has ("name"), "Task::has"); @@ -197,18 +165,18 @@ TODO Task::decode // Task::get_int task.set ("one", 1); - test.is (task.composeF4 (), R"([name:"value" one:"1"])", "Task::set"); + test.is (task.composeJSON (), R"({"name":"value","one":"1"})", "Task::set"); test.is (task.get_int ("one"), 1, "Task::get_int"); // Task::get_ulong task.set ("two", "4294967295"); - test.is (task.composeF4 (), R"([name:"value" one:"1" two:"4294967295"])", "Task::set"); + test.is (task.composeJSON (), R"({"name":"value","one":"1","two":"4294967295"})", "Task::set"); test.is ((size_t)task.get_ulong ("two"), (size_t)4294967295UL, "Task::get_ulong"); // Task::remove task.remove ("one"); task.remove ("two"); - test.is (task.composeF4 (), "[name:\"value\"]", "Task::remove"); + test.is (task.composeJSON (), "{\"name\":\"value\"}", "Task::remove"); // Task::all test.is (task.all ().size (), (size_t)1, "Task::all size"); @@ -230,16 +198,14 @@ TODO Task::decode catch (const std::string& e){test.diag (e); good = false;} test.ok (good, "Task::Task ('{}')"); - // Verify tag handling is correct between F4 and JSON. + // Verify tag handling is correct Task t6; t6.set ("entry", "20130602T224000Z"); t6.set ("description", "DESC"); t6.addTag ("tag1"); - test.is (t6.composeF4 (), R"([description:"DESC" entry:"20130602T224000Z" tags:"tag1"])", "F4 good"); test.is (t6.composeJSON (), R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1"]})", "JSON good"); t6.addTag ("tag2"); - test.is (t6.composeF4 (), R"([description:"DESC" entry:"20130602T224000Z" tags:"tag1,tag2"])", "F4 good"); test.is (t6.composeJSON (), R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1","tag2"]})", "JSON good"); good = true; @@ -247,7 +213,6 @@ TODO Task::decode try {t7 = Task (R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1","tag2"]})");} catch (const std::string& e){test.diag (e); good = false;} test.ok (good, "Task::Task ('{two tags}')"); - test.is (t7.composeF4 (), R"([description:"DESC" entry:"1370212800" tags:"tag1,tag2"])", "F4 good"); test.is (t7.composeJSON (), R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1","tag2"]})", "JSON good"); return 0; diff --git a/test/tdb2.test.cpp b/test/tdb2.test.cpp index 398a4b130..f469dacfb 100644 --- a/test/tdb2.test.cpp +++ b/test/tdb2.test.cpp @@ -37,16 +37,15 @@ void cleardb () { // Remove any residual test files. rmdir ("./extensions"); - unlink ("./pending.data"); - unlink ("./completed.data"); - unlink ("./undo.data"); - unlink ("./backlog.data"); + unlink ("./taskchampion.sqlite3"); } //////////////////////////////////////////////////////////////////////////////// int main (int, char**) { UnitTest t (12); + Context context; + Context::setContext(&context); // Ensure environment has no influence. unsetenv ("TASKDATA"); @@ -84,8 +83,8 @@ int main (int, char**) t.is ((int) pending.size (), 1, "TDB2 after add, 1 pending task"); t.is ((int) completed.size (), 0, "TDB2 after add, 0 completed tasks"); - t.is ((int) num_reverts_possible, 3, "TDB2 after add, 3 undo lines"); - t.is ((int) num_local_changes, 1, "TDB2 after add, 1 backlog task"); + t.is ((int) num_reverts_possible, 1, "TDB2 after add, 1 revert possible"); + t.is ((int) num_local_changes, 6, "TDB2 after add, 6 local changes"); task.set ("description", "This is a test"); context.tdb2.modify (task); @@ -95,10 +94,10 @@ int main (int, char**) num_reverts_possible = context.tdb2.num_reverts_possible (); num_local_changes = context.tdb2.num_local_changes (); - t.is ((int) pending.size (), 1, "TDB2 after add, 1 pending task"); - t.is ((int) completed.size (), 0, "TDB2 after add, 0 completed tasks"); - t.is ((int) num_reverts_possible, 7, "TDB2 after add, 7 undo lines"); - t.is ((int) num_local_changes, 2, "TDB2 after add, 2 backlog task"); + t.is ((int) pending.size (), 1, "TDB2 after set, 1 pending task"); + t.is ((int) completed.size (), 0, "TDB2 after set, 0 completed tasks"); + t.is ((int) num_reverts_possible, 1, "TDB2 after set, 1 revert possible"); + t.is ((int) num_local_changes, 7, "TDB2 after set, 7 local changes"); // Reset for reuse. cleardb (); diff --git a/test/test.cpp b/test/test.cpp index 16a7fb176..7f092af90 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -128,7 +128,8 @@ void UnitTest::ok (bool expression, const std::string& name, bool expfail /* = f } else { - ++_failed; + if (success == expfail) + ++_failed; std::cout << red ("not ok") << " " << _counter @@ -158,7 +159,8 @@ void UnitTest::notok (bool expression, const std::string& name, bool expfail /* } else { - ++_failed; + if (success == expfail) + ++_failed; std::cout << red ("not ok") << " " << _counter @@ -187,7 +189,8 @@ void UnitTest::is (bool actual, bool expected, const std::string& name, bool exp } else { - ++_failed; + if (success == expfail) + ++_failed; std::cout << red ("not ok") << " " << _counter @@ -220,7 +223,8 @@ void UnitTest::is (size_t actual, size_t expected, const std::string& name, bool } else { - ++_failed; + if (success == expfail) + ++_failed; std::cout << red ("not ok") << " " << _counter @@ -253,7 +257,8 @@ void UnitTest::is (int actual, int expected, const std::string& name, bool expfa } else { - ++_failed; + if (success == expfail) + ++_failed; std::cout << red ("not ok") << " " << _counter @@ -286,7 +291,8 @@ void UnitTest::is (double actual, double expected, const std::string& name, bool } else { - ++_failed; + if (success == expfail) + ++_failed; std::cout << red ("not ok") << " " << _counter @@ -319,7 +325,8 @@ void UnitTest::is (double actual, double expected, double tolerance, const std:: } else { - ++_failed; + if (success == expfail) + ++_failed; std::cout << red ("not ok") << " " << _counter @@ -352,7 +359,8 @@ void UnitTest::is (unsigned char actual, unsigned char expected, const std::stri } else { - ++_failed; + if (success == expfail) + ++_failed; std::cout << red ("not ok") << " " << _counter @@ -389,7 +397,8 @@ void UnitTest::is ( } else { - ++_failed; + if (success == expfail) + ++_failed; std::cout << red ("not ok") << " " << _counter @@ -427,7 +436,8 @@ void UnitTest::is ( } else { - ++_failed; + if (success == expfail) + ++_failed; std::cout << red ("not ok") << " " << _counter diff --git a/test/util.test.cpp b/test/util.test.cpp index 6f554f3f6..74901dfd5 100644 --- a/test/util.test.cpp +++ b/test/util.test.cpp @@ -35,6 +35,8 @@ int main (int, char**) { UnitTest t (19); + Context context; + Context::setContext(&context); // Ensure environment has no influence. unsetenv ("TASKDATA"); diff --git a/test/version.test.py b/test/version.test.py index cf30f28b4..747eacac0 100755 --- a/test/version.test.py +++ b/test/version.test.py @@ -35,7 +35,7 @@ from datetime import datetime sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase -from basetest.utils import run_cmd_wait +from basetest.utils import run_cmd_wait, SOURCE_DIR class TestVersion(TestCase): @@ -50,7 +50,6 @@ class TestVersion(TestCase): self.assertIn("You must specify a command or a task to modify", err) - @unittest.skip(reason="See #3424") def test_copyright_up_to_date(self): """Copyright is current""" code, out, err = self.t("version") @@ -58,18 +57,16 @@ class TestVersion(TestCase): expected = r"Copyright \(C\) \d{4} - %d" % (datetime.now().year,) self.assertRegex(out, expected) - def slurp(self, file="../CMakeLists.txt"): - number = r"\.".join(["[0-9]+"] * 3) - ver = re.compile("^set \\(PROJECT_VERSION \"({0}[^\"]*)\"\\)$".format(number)) + def slurp(self, file=os.path.join(SOURCE_DIR, "CMakeLists.txt")): + number = "\.".join(["[0-9]+"] * 3) + ver = re.compile("^ *VERSION ({0}[^\"]*)$".format(number)) with open(file) as fh: for line in fh: - if "PROJECT_VERSION" in line: - match = ver.match(line) - if match: - return match.group(1) + match = ver.match(line) + if match: + return match.group(1).strip() raise ValueError("Couldn't find matching version in {0}".format(file)) - @unittest.skip(reason="See #3424") def test_version(self): """version command outputs expected version and license""" code, out, err = self.t("version") @@ -79,7 +76,6 @@ class TestVersion(TestCase): self.assertIn("MIT license", out) self.assertIn("https://taskwarrior.org", out) - @unittest.skip(reason="See #3424") def test_under_version(self): """_version and diagnostics output expected version and syntax""" code, out, err = self.t("_version") diff --git a/test/view.test.cpp b/test/view.test.cpp index cdf4b4685..7c0e4f500 100644 --- a/test/view.test.cpp +++ b/test/view.test.cpp @@ -43,6 +43,8 @@ extern std::string configurationDefaults; int main (int, char**) { UnitTest t (1); + Context context; + Context::setContext(&context); // Ensure environment has no influence. unsetenv ("TASKDATA"); From 8aa4758993f1cc2bc5616d491c415cef790c4c14 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 3 May 2024 10:06:29 -0400 Subject: [PATCH 015/242] add targets for individual tests (#3431) --- doc/devel/contrib/development.md | 6 +++++- test/CMakeLists.txt | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/doc/devel/contrib/development.md b/doc/devel/contrib/development.md index 4a29f14ea..39241462d 100644 --- a/doc/devel/contrib/development.md +++ b/doc/devel/contrib/development.md @@ -56,7 +56,7 @@ First switch to the test directory: ``` Then you can run all tests, showing details, with ``` - $ make VERBOSE=1 + $ make VERBOSE=1 test ``` Alternately, run the tests with the details hidden in `all.log`: ``` @@ -66,6 +66,10 @@ Either way, you can get a summary of any test failures with: ``` $ ./problems ``` +You can run a single test suite, with source file `foo.test.cpp` or `foo.test.py`, with +``` + $ make foo.test +``` Note that any development should be performed using a git clone, and the current development branch. The source tarballs do not reflect HEAD, and do not contain the test suite. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dadca6bc8..17d9b781e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -64,6 +64,13 @@ foreach (src_FILE ${test_SRCS}) if (DARWIN) target_link_libraries (${src_FILE} "-framework CoreFoundation -framework Security -framework SystemConfiguration") endif (DARWIN) + + # Add a custom `foo.test` target. + string(REGEX REPLACE "\\.[^.]*$" "" test_target ${src_FILE}) + add_custom_target (${test_target} + ./${src_FILE} + DEPENDS ${src_FILE} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test) endforeach (src_FILE) configure_file(run_all run_all COPYONLY) @@ -197,6 +204,13 @@ set (pythonTests foreach (python_Test ${pythonTests}) configure_file(${python_Test} ${python_Test} COPYONLY) + + # Add a custom `foo.test` target. + string(REGEX REPLACE "\\.[^.]*$" "" test_target ${python_Test}) + add_custom_target (${test_target} + ./${python_Test} + DEPENDS ${python_Test} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test) endforeach(python_Test) #SET(CMAKE_BUILD_TYPE gcov) From 651ea363824db1c04314dd8064dcfe90408ae1d6 Mon Sep 17 00:00:00 2001 From: Steve Dondley Date: Sun, 5 May 2024 19:51:44 -0400 Subject: [PATCH 016/242] prevent task completion commands from triggering hooks #3131 (#3133) --- scripts/zsh/_task | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/zsh/_task b/scripts/zsh/_task index fd8d0d278..45fb7fd87 100644 --- a/scripts/zsh/_task +++ b/scripts/zsh/_task @@ -31,7 +31,7 @@ _task_filter() { local word=$'[^\0]#\0' # projects - local _task_projects=($(task _projects)) + local _task_projects=($(task rc.hooks=0 _projects)) local task_projects=( /"$word"/ ":values:task projects:compadd -a _task_projects" @@ -157,7 +157,7 @@ _task_filter() { local uda_name uda_label uda_values local -a udas_spec task _udas | while read uda_name; do - uda_label="$(task _get rc.uda."$uda_name".label)" + uda_label="$(task rc.hooks=0 _get rc.uda."$uda_name".label)" # TODO: we could have got the values of every uda and try to complete that # but that can become extremly slow with a lot of udas #uda_values=(${(@s:,:)"$(task _get rc.uda."$uda_name".values)"}) @@ -167,8 +167,8 @@ _task_filter() { _regex_words -t ':' default 'task attributes' "${_task_all_attributes[@]}" local task_attributes=("$reply[@]") - local _task_tags=($(task _tags)) - local _task_config=($(task _config)) + local _task_tags=($(task rc.hooks=0 _tags)) + local _task_config=($(task rc.hooks=0 _config)) local _task_modifiers=( 'before' 'after' @@ -213,12 +213,12 @@ _task_filter() { # id-only completion (( $+functions[_task_ids] )) || _task_ids() { - local _ids=( ${(f)"$(task _zshids)"} ) + local _ids=( ${(f)"$(task rc.hooks=0 _zshids)"} ) _describe 'task ids' _ids } (( $+functions[_task_aliases] )) || _task_aliases() { - local _aliases=( ${(f)"$(task _aliases)"} ) + local _aliases=( ${(f)"$(task rc.hooks=0 _aliases)"} ) _describe 'task aliases' _aliases } @@ -230,7 +230,7 @@ _task_subcommands() { local cmd category desc local lastcategory='' # The list is sorted by category, in the right order. - local _task_zshcmds=( ${(f)"$(task _zshcommands)"} sentinel:sentinel:sentinel ) + local _task_zshcmds=( ${(f)"$(task rc.hooks=0 _zshcommands)"} sentinel:sentinel:sentinel ) for _zshcmd in "$_task_zshcmds[@]"; do # Parse out the three fields cmd=${_zshcmd%%:*} @@ -254,7 +254,7 @@ _task_subcommands() { ## contexts (( $+functions[_task_context] )) || _task_context() { - local _contexts=(${(f)"$(task _context)"}) + local _contexts=(${(f)"$(task rc.hooks=0 _context)"}) _describe 'task contexts' _contexts } @@ -264,7 +264,7 @@ _task_default() { local cmd ret=1 integer i=1 - local _task_cmds=($(task _commands; task _aliases)) + local _task_cmds=($(task rc.hooks=0 _commands; task _aliases)) while (( i < $#words )) do cmd="${_task_cmds[(r)$words[$i]]}" @@ -289,4 +289,4 @@ _task_default() { } _arguments -s -S \ - "*::task default:_task_default" + "*::task default:_task_default" \ No newline at end of file From 9d9dde1065d68bce36575a571d4cc14b58f451d9 Mon Sep 17 00:00:00 2001 From: Philipp Oberdiek Date: Mon, 6 May 2024 02:02:12 +0200 Subject: [PATCH 017/242] Fix inherited urgency when parent and child have the same urgency (#2941) Update condition for inheritance value hack If the parent and child task have the same urgency the parent task also needs the 0.01 extra urgency to be sorted above the child. --- src/Task.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Task.cpp b/src/Task.cpp index 2ba0b6d18..0d0d080b4 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -2180,7 +2180,7 @@ float Task::urgency_c () const // This is a hackish way of making sure parent tasks are sorted above // child tasks. For reports that hide blocked tasks, this is not needed. - if (prev < value) + if (prev <= value) value += 0.01; } #endif From 0deeeb0a1d4014f9cf04517142859b0ecbda88cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Reh=C3=A1k?= <33783628+drehak@users.noreply.github.com> Date: Mon, 6 May 2024 01:04:18 +0100 Subject: [PATCH 018/242] CmdStart: Fix "make this task pending" note for completed tasks (#2769) --- src/commands/CmdStart.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/CmdStart.cpp b/src/commands/CmdStart.cpp index 6329c8129..e63edd1cb 100644 --- a/src/commands/CmdStart.cpp +++ b/src/commands/CmdStart.cpp @@ -83,7 +83,6 @@ int CmdStart::execute (std::string&) std::string question = format ("Start task {1} '{2}'?", task.identifier (true), task.get ("description")); - task.modify (Task::modAnnotate); task.setAsNow ("start"); Task::status status = task.getStatus (); @@ -93,6 +92,7 @@ int CmdStart::execute (std::string&) task.setStatus (Task::pending); } + task.modify (Task::modAnnotate); if (Context::getContext ().config.getBoolean ("journal.time")) task.addAnnotation (Context::getContext ().config.get ("journal.time.start.annotation")); From 60575a19675713fd5cc209c77733b8ffc3f442e5 Mon Sep 17 00:00:00 2001 From: Joseph Coffa Date: Sun, 5 May 2024 23:24:41 -0400 Subject: [PATCH 019/242] Update task-sync(5) to include delete perms for GCP sync (#3442) --- doc/man/task-sync.5.in | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/man/task-sync.5.in b/doc/man/task-sync.5.in index 70c45d216..8f9b626c7 100644 --- a/doc/man/task-sync.5.in +++ b/doc/man/task-sync.5.in @@ -108,6 +108,7 @@ Select the following permissions: - storage.objects.get - storage.objects.list - storage.objects.update + - storage.objects.delete Create your new role. From 2361521449bc02c9aa0be00dcc98a2d25aadb31b Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Thu, 9 May 2024 03:33:17 +0200 Subject: [PATCH 020/242] remove stress_test file (#3447) Close #3422 --- test/stress_test | 94 ------------------------------------------------ 1 file changed, 94 deletions(-) delete mode 100755 test/stress_test diff --git a/test/stress_test b/test/stress_test deleted file mode 100755 index 3073324fa..000000000 --- a/test/stress_test +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -from __future__ import print_function -from subprocess import call -import argparse -import glob -import logging -import os -import sys - - -def find_tests(): - tests = [] - - for test in glob.glob("*.test.sh") + glob.glob("*.test.py"): - if os.access(test, os.X_OK): - # Executables only - tests.append(test) - else: - log.debug("Ignored test %s as it is not executable", test) - - log.info("Tests found: %s", len(tests)) - - return tests - - -def run_test(test): - log.warn("Running test %s %s times", test, cmd_args.repeat) - - if not test.startswith(".{0}".format(os.path.sep)): - test = ".{0}{1}".format(os.path.sep, test) - - for i in range(cmd_args.repeat): - exit = call([test], stdout=sys.stdout, stderr=sys.stderr) - - if exit != 0: - log.error("Failed to run test %s on repetition %s", test, i) - break - - -def parse_args(): - parser = argparse.ArgumentParser(description="Run Taskwarrior tests repeatedly") - parser.add_argument('--logging', '-l', action="count", default=0, - help="Logging level. -lll is the highest level") - parser.add_argument('--repeat', metavar="N", type=int, default=100, - help="How many times to run each test (default: 100)") - parser.add_argument('--noprompt', action="store_true", - help="Do not prompt when running all tests") - parser.add_argument('test', nargs="*", - help="Test files to run repeatedly. Unspecified = all tests") - return parser.parse_args() - - -def main(): - if cmd_args.test: - for test in cmd_args.test: - run_test(test) - else: - if not cmd_args.noprompt: - r = raw_input("No test was specified, are you sure you want to run all tests? (y/N)\n") - if not (r and r[0] in ["y", "Y"]): - return - - log.info("Stress testing all tests") - for test in find_tests(): - run_test(test) - - -if __name__ == "__main__": - cmd_args = parse_args() - - if cmd_args.logging == 1: - level = logging.WARN - elif cmd_args.logging == 2: - level = logging.INFO - elif cmd_args.logging >= 3: - level = logging.DEBUG - else: - level = logging.ERROR - - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(message)s", - level=level, - ) - log = logging.getLogger(__name__) - - log.debug("Parsed commandline arguments: %s", cmd_args) - - try: - main() - except Exception as e: - log.exception(e) - sys.exit(1) From 82e0d53cdf2df4d505fda7b8341bb5e93c8bf4ce Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Fri, 10 May 2024 03:20:54 +0200 Subject: [PATCH 021/242] add ctest as test driver (#3446) --- .github/workflows/tests.yaml | 4 +- CMakeLists.txt | 1 + doc/devel/contrib/development.md | 51 +++++-- test/CMakeLists.txt | 40 ++--- test/{README => README.md} | 78 ++++------ test/docker/arch | 6 +- test/docker/debianstable | 6 +- test/docker/debiantesting | 6 +- test/docker/fedora39 | 6 +- test/docker/fedora40 | 6 +- test/docker/opensuse | 6 +- test/docker/ubuntu2004 | 6 +- test/docker/ubuntu2204 | 6 +- test/problems | 149 ------------------- test/run_all | 242 ------------------------------- 15 files changed, 90 insertions(+), 523 deletions(-) rename test/{README => README.md} (73%) delete mode 100755 test/problems delete mode 100755 test/run_all diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 36df18ce8..b61e9a709 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -99,10 +99,10 @@ jobs: GITHUB_USER: ${{ github.actor }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CONTAINER: ${{ matrix.dockerfile }} - run: docker-compose build test-$CONTAINER + run: docker-compose build test-${{ env.CONTAINER }} - name: Test ${{ matrix.name }} - run: docker-compose run test-$CONTAINER + run: docker-compose run test-${{ env.CONTAINER }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CONTAINER: ${{ matrix.dockerfile }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 79900b169..574f30ecd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required (VERSION 3.22) +enable_testing() set (CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/doc/devel/contrib/development.md b/doc/devel/contrib/development.md index 39241462d..a5c904c5e 100644 --- a/doc/devel/contrib/development.md +++ b/doc/devel/contrib/development.md @@ -2,7 +2,7 @@ ## Satisfy the Requirements: - * CMake 3.0 or later + * CMake 3.22 or later * gcc 7.0 or later, clang 6.0 or later, or a compiler with full C++17 support * libuuid (if not on macOS) * Rust 1.64.0 or higher (hint: use https://rustup.rs/ instead of using your system's package manager) @@ -49,28 +49,47 @@ cmake --build build-clang ``` ## Run the Test Suite: -First switch to the test directory: +For running the test suite [ctest](https://cmake.org/cmake/help/latest/manual/ctest.1.html) is used. +Before one can run the test suite the `task_executable` must be built. +After that also the `build_tests` target must be build, which can be done over: +```sh +cmake --build build --target build_tests +``` +Again you may also use the `-j ` option for parallel builds. +Now one can invoke `ctest` to run the tests. +```sh +ctest --test-dir build ``` - $ cd build/test +This would run all the test in serial and might take some time. + +### Running tests in parallel +```sh +ctest --test-dir build -j ``` -Then you can run all tests, showing details, with + +Further it is adviced to add the `--output-on-failure` option to `ctest`, to recieve a verbose output if a test is failing as well as the `--rerun-failed` flag, to invoke in subsequent runs only the failed ones. + +### Running specific tests +For this case one can use the `-R ` or `--tests-regex ` option to run only the tests matching the regular expression. +Running only the `cpp` tests can then be achieved over +```sh +ctest --test-dir build -R cpp ``` - $ make VERBOSE=1 test +or running the `variant_*` tests +```sh +ctest --test-dir build -R variant ``` -Alternately, run the tests with the details hidden in `all.log`: -``` - $ ./run_all -``` -Either way, you can get a summary of any test failures with: -``` - $ ./problems -``` -You can run a single test suite, with source file `foo.test.cpp` or `foo.test.py`, with -``` - $ make foo.test + +### Repeating a test case +In order to find sporadic test failures the `--repeat` flag can be used. +```sh +ctest --test-dir build -R cpp --repeat-until-fail 10 ``` +There are more options to `ctest` such as `--progress`, allowing to have a less verbose output. +They can be found in the [ctest](https://cmake.org/cmake/help/latest/manual/ctest.1.html) man page. + Note that any development should be performed using a git clone, and the current development branch. The source tarballs do not reflect HEAD, and do not contain the test suite. Follow the [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) for creating a pull request. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 17d9b781e..877bb6863 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,13 +1,5 @@ cmake_minimum_required (VERSION 3.22) -# This is a work-around for the following CMake issue: -# https://gitlab.kitware.com/cmake/cmake/issues/16062 -# The issue has been fixed in CMake 3.11.0; the policy is set -# to OLD for compatibility with older versions of CMake only. -if(POLICY CMP0037 AND ${CMAKE_VERSION} VERSION_LESS "3.11.0") - cmake_policy(SET CMP0037 OLD) -endif() - include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/tc/lib @@ -51,30 +43,23 @@ set (test_SRCS view.test.cpp ) -add_custom_target (test ./run_all --verbose - DEPENDS ${test_SRCS} task_executable - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test) - add_custom_target (build_tests DEPENDS ${test_SRCS} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/test) foreach (src_FILE ${test_SRCS}) add_executable (${src_FILE} ${src_FILE} test.cpp) target_link_libraries (${src_FILE} task tc commands columns libshared task tc commands columns libshared task commands columns libshared ${TASK_LIBRARIES}) + add_dependencies (${src_FILE} task_executable) if (DARWIN) target_link_libraries (${src_FILE} "-framework CoreFoundation -framework Security -framework SystemConfiguration") endif (DARWIN) - # Add a custom `foo.test` target. - string(REGEX REPLACE "\\.[^.]*$" "" test_target ${src_FILE}) - add_custom_target (${test_target} - ./${src_FILE} - DEPENDS ${src_FILE} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test) + add_test(NAME ${src_FILE} + COMMAND ${src_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) endforeach (src_FILE) -configure_file(run_all run_all COPYONLY) -configure_file(problems problems COPYONLY) configure_file(bash_tap.sh bash_tap.sh COPYONLY) configure_file(bash_tap_tw.sh bash_tap_tw.sh COPYONLY) @@ -205,15 +190,8 @@ set (pythonTests foreach (python_Test ${pythonTests}) configure_file(${python_Test} ${python_Test} COPYONLY) - # Add a custom `foo.test` target. - string(REGEX REPLACE "\\.[^.]*$" "" test_target ${python_Test}) - add_custom_target (${test_target} - ./${python_Test} - DEPENDS ${python_Test} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test) + add_test(NAME ${python_Test} + COMMAND ${Python_EXECUTABLE} ${python_Test} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) endforeach(python_Test) - -#SET(CMAKE_BUILD_TYPE gcov) -#SET(CMAKE_CXX_FLAGS_GCOV "--coverage") -#SET(CMAKE_C_FLAGS_GCOV "--coverage") -#SET(CMAKE_EXE_LINKER_FLAGS_GCOV "--coverage") diff --git a/test/README b/test/README.md similarity index 73% rename from test/README rename to test/README.md index 30ba98b8c..6b19ec215 100644 --- a/test/README +++ b/test/README.md @@ -1,36 +1,18 @@ -README -====== - -This is the task.git/test/README file, and contains notes about the Taskwarrior -test suite. - - -Running Tests -------------- - +## Running Tests Do this to run all tests: +```shell +cmake --build build --target build_tests +ctest --test-dir build +``` - $ cd test && make && ./run_all && ./problems +All tests produce TAP (Test Anything Protocol) output. +In order to run the tests in parallel add the `--parallel <# of threads>` or shortly `-j <# of threads>` option to `ctest`. +Depending on your IDE, all tests might also be available under the `All CTest` target. +Keep in mind that the tests are not automatically rebuild if a source file is changes, it requires a manual rebuild. -All unit tests produce TAP (Test Anything Protocol) output, and are run by the -'run_all' test harness. +Please also have a look at [development.md](../doc/devel/contrib/development.md) for more information on how to run tests as well as some information about `ctest`. -The 'run_all' script produces an 'all.log' file which is the accumulated output -of all tests. Before executing 'run_all' you need to compile the C++ unit -tests, by running 'make' in the 'test' directory. - -The script 'problems' will list all the tests that fail, with a count of the -failing tests, once you have run all the tests and produced an 'all.log' file. - -Any TAP harness may be used. - -Note that adding the '--serial' option to ./run_all, all tests are executed -serially. The default runs Python, C++ and Bash tests in parallel. Using -'--serial' will make for a slower test run. - - -Architecture ------------- +## Architecture There are three varieties of tests: @@ -45,17 +27,18 @@ There are three varieties of tests: tests are small, quick tests, not intended to be permanent. All tests are named with the pattern '*.test.py', '*.test.sh', or '*.test.cpp', -and any other forms are not run by the test harness. Additionally a test must -be set executable (chmod +x) for it to be run. In the case of Python tests one -can still run them manually by launching them with 'python testname.test.py' or -simply './testname.test.py'. It also allows us to keep tests submitted for bugs -that are not scheduled to be fixed in the upcoming release, and we don't want -the failing tests to prevent us from seeing 100% pass rate for the bugs we -*have* fixed. +and any other forms are not run by the test harness. +In the case of Python tests one can still run them manually by launching them with 'python testname.test.py' or simply './testname.test.py'. + +If a test is failing and can not be fixed, it can be marked as `WILL_FAIL` in the `CMakeLists.txt` file. +See the [WILL_FAIL](https://cmake.org/cmake/help/latest/prop_test/WILL_FAIL.html) documentation for more information. +However, please keep in mind that such tests should be fixed as soon as possible as well as proper documentation should be added to the issue tracker. + +It also allows us to keep tests submitted for bugs that are not scheduled to be fixed in the upcoming release, and we don't want +the failing tests to prevent us from seeing 100% pass rate for the bugs we *have* fixed. -Goals ------ +## Goals The test suite is evolving, and becoming a better tool for determining whether code is ready for release. There are goals that shape these changes, and they @@ -71,16 +54,14 @@ are: There is simply no point in testing a feature twice, in the same manner. -What Makes a Good Test ----------------------- +## What Makes a Good Test A good test ensures that a feature is functioning as expected, and contains both positive and negative aspects, or in other words looks for expected behavior as well as looking for the absence of unexpected behavior. -Conventions for writing a test ------------------------------- +## Conventions for writing a test If you wish to contribute tests, please consider the following guidelines: @@ -109,14 +90,12 @@ If you wish to contribute tests, please consider the following guidelines: a live test that is skipped, than no test. -How to Submit a Test Change/Addition ------------------------------------- +## How to Submit a Test Change/Addition Mail it to support@gothenburgbitfactory.org, or attach it to an open bug. -Wisdom ------- +## Wisdom Here are some guildelines that may help: @@ -146,8 +125,7 @@ Here are some guildelines that may help: are reported. -TODO ----- +## TODO For anyone looking for test-related tasks to take on, here are some suggestions: @@ -161,6 +139,4 @@ For anyone looking for test-related tasks to take on, here are some suggestions: * All the attribute modifiers need to be tested, only a few are. - * Aliases are not well tested, and fragile. - ---- + * Aliases are not well tested, and fragile. \ No newline at end of file diff --git a/test/docker/arch b/test/docker/arch index 643462630..e4ef88b81 100644 --- a/test/docker/arch +++ b/test/docker/arch @@ -26,7 +26,5 @@ RUN cmake --install build RUN task --version # Setup tests -WORKDIR /root/code/build/test/ -RUN make -j8 - -CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems"] +RUN cmake --build build --target build_tests -j 8 +CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/debianstable b/test/docker/debianstable index 878553350..94e75a604 100644 --- a/test/docker/debianstable +++ b/test/docker/debianstable @@ -27,7 +27,5 @@ RUN cmake --install build RUN task --version # Setup tests -WORKDIR /root/code/build/test -RUN make -j8 - -CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems"] +RUN cmake --build build --target build_tests -j 8 +CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/debiantesting b/test/docker/debiantesting index 78773eef8..9d11a619c 100644 --- a/test/docker/debiantesting +++ b/test/docker/debiantesting @@ -27,7 +27,5 @@ RUN cmake --install build RUN task --version # Setup tests -WORKDIR /root/code/build/test -RUN make -j8 - -CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems"] +RUN cmake --build build --target build_tests -j 8 +CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/fedora39 b/test/docker/fedora39 index c4b3bd670..02659a415 100644 --- a/test/docker/fedora39 +++ b/test/docker/fedora39 @@ -26,7 +26,5 @@ RUN cmake --install build RUN task --version # Setup tests -WORKDIR /root/code/build/test -RUN make -j8 - -CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems"] +RUN cmake --build build --target build_tests -j 8 +CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/fedora40 b/test/docker/fedora40 index 8625f5da0..0574356a3 100644 --- a/test/docker/fedora40 +++ b/test/docker/fedora40 @@ -26,7 +26,5 @@ RUN cmake --install build RUN task --version # Setup tests -WORKDIR /root/code/build/test -RUN make -j8 - -CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems"] +RUN cmake --build build --target build_tests -j 8 +CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/opensuse b/test/docker/opensuse index 94a2d5a3a..6ea38d4f0 100644 --- a/test/docker/opensuse +++ b/test/docker/opensuse @@ -25,7 +25,5 @@ RUN cmake --install build RUN task --version # Setup tests -WORKDIR /root/code/build/test -RUN make -j8 - -CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems"] +RUN cmake --build build --target build_tests -j 8 +CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/ubuntu2004 b/test/docker/ubuntu2004 index efef83221..ea11d1f59 100644 --- a/test/docker/ubuntu2004 +++ b/test/docker/ubuntu2004 @@ -34,7 +34,5 @@ RUN cmake --install build RUN task --version # Setup tests -WORKDIR /root/code/build/test -RUN make -j8 - -CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems"] +RUN cmake --build build --target build_tests -j 8 +CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/ubuntu2204 b/test/docker/ubuntu2204 index 29381323a..110547ade 100644 --- a/test/docker/ubuntu2204 +++ b/test/docker/ubuntu2204 @@ -27,7 +27,5 @@ RUN cmake --install build RUN task --version # Setup tests -WORKDIR /root/code/build/test -RUN make -j8 - -CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems"] +RUN cmake --build build --target build_tests -j 8 +CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/problems b/test/problems deleted file mode 100755 index 7f53a8661..000000000 --- a/test/problems +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python3 - -from __future__ import print_function -import sys -import re -import argparse -from collections import defaultdict - - -def color(text, c): - """ - Add color on the keyword that identifies the state of the test - """ - if sys.stdout.isatty(): - clear = "\033[0m" - - colors = { - "red": "\033[1m\033[91m", - "yellow": "\033[1m\033[93m", - "green": "\033[1m\033[92m", - } - return colors[c] + text + clear - else: - return text - - -def parse_args(): - parser = argparse.ArgumentParser(description="Report on test results") - parser.add_argument('--summary', action="store_true", - help="Display only the totals in each category") - parser.add_argument('tapfile', default="all.log", nargs="?", - help="File containing TAP output") - return parser.parse_args() - - -def print_category(tests): - if not cmd_args.summary: - for key in sorted(tests): - print("%-32s %4d" % (key, tests[key])) - - -def pad(i): - return " " * i - - -if __name__ == "__main__": - cmd_args = parse_args() - - errors = defaultdict(int) - skipped = defaultdict(int) - expected = defaultdict(int) - unexpected = defaultdict(int) - passed = defaultdict(int) - - file = re.compile(r"^# (?:./)?(\S+\.test)(?:\.py|\.cpp)?$") - timestamp = re.compile(r"^# (\d+(?:\.\d+)?) ==>.*$") - - expected_fail = re.compile(r"^not ok.*?#\s*TODO", re.I) - unexpected_pass = re.compile(r"^not ok .*?#\s*FIXED", re.I) - skip = re.compile(r"^ok .*?#\s*skip", re.I) - ok = re.compile(r"^ok ", re.I) - not_ok = re.compile(r"^not ok", re.I) - comment = re.compile(r"^#") - plan = re.compile(r"^1..\d+\s*(?:#.*)?$") - - start = None - stop = None - - with open(cmd_args.tapfile) as fh: - for line in fh: - if start is None: - # First line contains the starting timestamp - start = float(timestamp.match(line).group(1)) - continue - - match = file.match(line) - if match: - filename = match.group(1) - - elif expected_fail.match(line): - expected[filename] += 1 - - elif unexpected_pass.match(line): - unexpected[filename] += 1 - - elif skip.match(line): - skipped[filename] += 1 - - # It's important these come last, since they're subpatterns of the above - - elif ok.match(line): - passed[filename] += 1 - - elif not_ok.match(line): - errors[filename] += 1 - - elif comment.match(line): - pass - - elif plan.match(line): - pass - - else: - # Uncomment if you want to see malformed things we caught as well... - # print(color("Malformed TAP (" + filename + "): " + line, "red")) - pass - - # Last line contains the ending timestamp - stop = float(timestamp.match(line).group(1)) - - v = "{0:>5d}" - passed_str = "Passed:" + pad(24) - passed_int = v.format(sum(passed.values())) - error_str = "Failed:" + pad(24) - error_int = v.format(sum(errors.values())) - unexpected_str = "Unexpected successes:" + pad(10) - unexpected_int = v.format(sum(unexpected.values())) - skipped_str = "Skipped:" + pad(23) - skipped_int = v.format(sum(skipped.values())) - expected_str = "Expected failures:" + pad(13) - expected_int = v.format(sum(expected.values())) - runtime_str = "Runtime:" + pad(20) - runtime_int = "{0:>8.2f} seconds".format(stop - start) - details_str = "For details run 'make problems'" - - if cmd_args.summary: - print(color(passed_str, "green"), passed_int) - print(color(error_str, "red"), error_int) - print(color(unexpected_str, "red"), unexpected_int) - print(color(skipped_str, "yellow"), skipped_int) - print(color(expected_str, "yellow"), expected_int) - print(runtime_str, runtime_int) - print(details_str) - - else: - print(color(error_str, "red")) - print_category(errors) - print() - print(color(unexpected_str, "red")) - print_category(unexpected) - print() - print(color(skipped_str, "yellow")) - print_category(skipped) - print() - print(color(expected_str, "yellow")) - print_category(expected) - - # If we encoutered any failures, return non-zero code - sys.exit(1 if int(error_int) or int(unexpected_int) else 0) diff --git a/test/run_all b/test/run_all deleted file mode 100755 index 70f060eb0..000000000 --- a/test/run_all +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -from __future__ import print_function -import os -import sys -import glob -import argparse -import logging -import time -from multiprocessing import cpu_count -from threading import Thread -from subprocess import call, Popen, PIPE - -if sys.version_info > (3,): - import codecs - -try: - # python 2 - from Queue import Queue, Empty -except ImportError: - # python 3 - from queue import Queue, Empty - -TIMEOUT = .2 - - -def run_test(testqueue, outqueue, threadname): - start = time.time() - while True: - try: - test = testqueue.get(block=True, timeout=TIMEOUT) - except Empty: - break - - log.info("Running test %s", test) - - try: - p = Popen(os.path.abspath(test), stdout=PIPE, stderr=PIPE, - env=os.environ) - out, err = p.communicate() - except Exception as e: - log.exception(e) - # Premature end - break - - if sys.version_info > (3,): - out, err = out.decode('utf-8'), err.decode('utf-8') - - if p.returncode != 0: - out = out + "\nnot ok - test executable failed\n" - - output = ("# {0}\n".format(os.path.basename(test)), out, err) - log.debug("Collected output %s", output) - outqueue.put(output) - - testqueue.task_done() - - log.warning("Finished %s thread after %s seconds", - threadname, round(time.time() - start, 3)) - - -class TestRunner(object): - def __init__(self): - self.threads = [] - if sys.version_info > (3,): - self.tap = open(cmd_args.tapfile, 'w', errors='ignore') - else: - self.tap = open(cmd_args.tapfile, 'w') - - self._parallelq = Queue() - self._serialq = Queue() - self._outputq = Queue() - - def _find_tests(self): - for test in glob.glob("*.test.sh") + glob.glob("*.test.py") + glob.glob("*.test.cpp"): - if os.access(test, os.X_OK): - # Executables only - if self._is_parallelizable(test): - log.debug("Treating as parallel: %s", test) - self._parallelq.put(test) - else: - log.debug("Treating as serial: %s", test) - self._serialq.put(test) - else: - log.debug("Ignored test %s as it is not executable", test) - - log.info("Parallel tests: %s", self._parallelq.qsize()) - log.info("Serial tests: %s", self._serialq.qsize()) - - def _prepare_threads(self): - # Serial thread - self.threads.append( - Thread(target=run_test, args=(self._serialq, self._outputq, "Serial")) - ) - # Parallel threads - self.threads.extend([ - Thread(target=run_test, args=(self._parallelq, self._outputq, "Parallel")) - for i in range(cpu_count()) - ]) - log.info("Spawned %s threads to run tests", len(self.threads)) - - def _start_threads(self): - for thread in self.threads: - # Threads die when main thread dies - log.debug("Starting thread %s", thread) - thread.daemon = True - thread.start() - - def _print_timestamp_to_tap(self): - now = time.time() - timestamp = "# {0} ==> {1}\n".format( - now, - time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now)), - ) - - log.debug("Adding timestamp %s to TAP file", timestamp) - self.tap.write(timestamp) - - def _is_parallelizable(self, test): - if cmd_args.serial: - return False - - # This is a pretty weird way to do it, and not realiable. - # We are dealing with some binary tests though. - with open(test, 'rb') as fh: - header = fh.read(100).split(b"\n") - if len(header) >= 2 and \ - ((b"!#/usr/bin/env python3" in header[0]) or \ - (header[1][-14:] == b"bash_tap_tw.sh")): - return True - else: - return False - - def _get_remaining_tests(self): - return self._parallelq.qsize() + self._serialq.qsize() - - def is_running(self): - for thread in self.threads: - if thread.is_alive(): - return True - - return False - - def start(self): - self._find_tests() - self._prepare_threads() - - self._print_timestamp_to_tap() - - finished = 0 - total = self._get_remaining_tests() - - self._start_threads() - - while self.is_running() or not self._outputq.empty(): - try: - outputs = self._outputq.get(block=True, timeout=TIMEOUT) - except Empty: - continue - - log.debug("Outputting to TAP: %s", outputs) - - for output in outputs: - self.tap.write(output) - - if cmd_args.verbose: - sys.stdout.write(output) - - self._outputq.task_done() - finished += 1 - - log.warning("Finished %s out of %s tests", finished, total) - - self._print_timestamp_to_tap() - - if not self._parallelq.empty() or not self._serialq.empty(): - raise RuntimeError( - "Something went wrong, not all tests were ran. {0} " - "remaining.".format(self._get_remaining_tests())) - - def show_report(self): - self.tap.flush() - sys.stdout.flush() - sys.stderr.flush() - - log.debug("Calling 'problems --summary' for report") - return call([os.path.abspath("problems"), "--summary", cmd_args.tapfile]) - - -def parse_args(): - parser = argparse.ArgumentParser(description="Run Taskwarrior tests") - parser.add_argument('--verbose', '-v', action="store_true", - help="Also send TAP output to stdout") - parser.add_argument('--logging', '-l', action="count", - default=0, - help="Logging level. -lll is the highest level") - parser.add_argument('--serial', action="store_true", - help="Do not run tests in parallel") - parser.add_argument('--tapfile', default="all.log", - help="File to use for TAP output") - return parser.parse_args() - - -def main(): - if sys.version_info > (3,): - sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) - - runner = TestRunner() - runner.start() - - # Propagate the return code - return runner.show_report() - - -if __name__ == "__main__": - cmd_args = parse_args() - - if cmd_args.logging == 1: - level = logging.WARN - elif cmd_args.logging == 2: - level = logging.INFO - elif cmd_args.logging >= 3: - level = logging.DEBUG - else: - level = logging.ERROR - - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(message)s", - level=level, - ) - log = logging.getLogger(__name__) - - log.debug("Parsed commandline arguments: %s", cmd_args) - - try: - sys.exit(main()) - except Exception as e: - log.exception(e) - sys.exit(1) - -# vim: ai sts=4 et sw=4 From 20583ddb7d0bd8856e6f8de1a02da912cb5c64d3 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 13 May 2024 21:50:43 -0400 Subject: [PATCH 022/242] more specifics about docs updates (#3453) --- doc/devel/contrib/releasing.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/devel/contrib/releasing.md b/doc/devel/contrib/releasing.md index da002ca9f..d8b4fa125 100644 --- a/doc/devel/contrib/releasing.md +++ b/doc/devel/contrib/releasing.md @@ -20,4 +20,7 @@ To release Taskwarrior, follow this process: - Find the tag under https://github.com/GothenburgBitFactory/taskwarrior/tags and create a release from it - Give it a clever title if you can think of one; refer to previous releases - Include the tarball from earlier -- Add a new item in `content/news` on https://github.com/GothenburgBitFactory/tw.org +- Update https://github.com/GothenburgBitFactory/tw.org + - Add a new item in `content/news` + - Update `data/projects.json` with the latest version and a fake next version for "devel" + - Update `data/releases.json` with the new version, and copy the tarball into `content/download`. From e4c33d1e1d4c27abb7cbea506dbbcc2619ae10dd Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 13 May 2024 21:51:23 -0400 Subject: [PATCH 023/242] move .cargo/config to config.toml (#3454) --- .cargo/config => config.toml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .cargo/config => config.toml (100%) diff --git a/.cargo/config b/config.toml similarity index 100% rename from .cargo/config rename to config.toml From fb16dbf7cf601a5c501b95cefcb0f74ee366f740 Mon Sep 17 00:00:00 2001 From: Maarten Aertsen Date: Wed, 15 May 2024 03:47:43 +0200 Subject: [PATCH 024/242] Test modification of a task by an on-add hook (test case for #3416) (#3443) * Add test case to cover https://github.com/GothenburgBitFactory/taskwarrior/issues/3416 * Add (builtin) on-add-modify test hook and use it * TDB2::add() move hook invocation before save (#3416) --- src/TDB2.cpp | 6 +++--- test/hooks.on-add.test.py | 17 +++++++++++++++++ test/test_hooks/on-add-modify | 21 +++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 test/test_hooks/on-add-modify diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 561511d98..a8c7157dc 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -81,6 +81,9 @@ void TDB2::add (Task& task) std::string uuid = task.get ("uuid"); changes[uuid] = task; + // run hooks for this new task + Context::getContext ().hooks.onAdd (task); + auto innertask = replica.import_task_with_uuid (uuid); { @@ -121,9 +124,6 @@ void TDB2::add (Task& task) // update the cached working set with the new information _working_set = std::make_optional (std::move (ws)); - // run hooks for this new task - Context::getContext ().hooks.onAdd (task); - if (id.has_value ()) { task.id = id.value(); } diff --git a/test/hooks.on-add.test.py b/test/hooks.on-add.test.py index d62c0d109..d3d057f56 100755 --- a/test/hooks.on-add.test.py +++ b/test/hooks.on-add.test.py @@ -58,6 +58,23 @@ class TestHooksOnAdd(TestCase): code, out, err = self.t("1 info") self.assertIn("Description foo", out) + def test_onadd_builtin_accept_modify(self): + """on-add-accept-modify - a well-behaved, successful, on-add hook, that modifies the added task.""" + hookname = 'on-add-modify' + self.t.hooks.add_default(hookname, log=True) + + code, out, err = self.t("add teh foo") + + hook = self.t.hooks[hookname] + hook.assertTriggeredCount(1) + hook.assertExitcode(0) + + logs = hook.get_logs() + self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK") + + code, out, err = self.t("1 info") + self.assertIn("Description the foo", out) + def test_onadd_builtin_reject(self): """on-add-reject - a well-behaved, failing, on-add hook.""" hookname = 'on-add-reject' diff --git a/test/test_hooks/on-add-modify b/test/test_hooks/on-add-modify new file mode 100644 index 000000000..175332985 --- /dev/null +++ b/test/test_hooks/on-add-modify @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# Input: +# - Line of JSON for proposed new task. +read new_task + +if (echo $new_task | grep -qE '[tT]eh'); +then + new_task=$(echo $new_task | sed -r 's/([tT])eh/\1he/g') +fi + +# Output: +# - JSON, modified +# - Optional feedback/error. +echo $new_task +echo 'FEEDBACK' + +# Status: +# - 0: JSON accepted, non-JSON is feedback. +# - non-0: JSON ignored, non-JSON is error. +exit 0 From aebbfaff985db41a24fa3bd4ef9b0ec03c0eb27b Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 14 May 2024 22:03:16 -0400 Subject: [PATCH 025/242] Be resilient to different numbers of local changes (#3449) With an explanation in the comments --- test/tdb2.test.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/tdb2.test.cpp b/test/tdb2.test.cpp index f469dacfb..059663349 100644 --- a/test/tdb2.test.cpp +++ b/test/tdb2.test.cpp @@ -97,7 +97,13 @@ int main (int, char**) t.is ((int) pending.size (), 1, "TDB2 after set, 1 pending task"); t.is ((int) completed.size (), 0, "TDB2 after set, 0 completed tasks"); t.is ((int) num_reverts_possible, 1, "TDB2 after set, 1 revert possible"); - t.is ((int) num_local_changes, 7, "TDB2 after set, 7 local changes"); + + // At this point, there may be 7 or 8 local changes, depending on whether + // the `modified` property changed between the `add` and `modify` + // invocation. That only happens if the clock ticks over to the next second + // between those invocations. + t.ok (num_local_changes == 7 || num_local_changes == 8, + "TDB2 after set, 7 or 8 local changes"); // Reset for reuse. cleardb (); From 8a807af2ef1cc99529beda4697fac2b39c727cec Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 15 May 2024 08:11:45 -0400 Subject: [PATCH 026/242] Add test that on-add returning 1 does not add task (#3457) This test existed, but didn't notice that the task was actually added. The bug itself was fixed in #3443. --- test/hooks.on-add.test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/hooks.on-add.test.py b/test/hooks.on-add.test.py index d3d057f56..66fdf2b67 100755 --- a/test/hooks.on-add.test.py +++ b/test/hooks.on-add.test.py @@ -89,6 +89,10 @@ class TestHooksOnAdd(TestCase): logs = hook.get_logs() self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK") + # task was not added + code, out, err = self.t.runError("1 info") + self.assertIn("No matches", err) + def test_onadd_builtin_misbehave1(self): """on-add-misbehave1 - does not consume input.""" hookname = 'on-add-misbehave1' From d775923070966ecf18f71d90dc253688b93ea181 Mon Sep 17 00:00:00 2001 From: Andonome <52453904+Andonome@users.noreply.github.com> Date: Mon, 27 May 2024 19:50:46 +0200 Subject: [PATCH 027/242] Let task show recognize limit in taskrc (#3466) --- scripts/vim/syntax/taskrc.vim | 1 + src/commands/CmdShow.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts/vim/syntax/taskrc.vim b/scripts/vim/syntax/taskrc.vim index 62f9d993a..8efddad9b 100644 --- a/scripts/vim/syntax/taskrc.vim +++ b/scripts/vim/syntax/taskrc.vim @@ -142,6 +142,7 @@ syn match taskrcGoodKey '^\s*\Vjournal.time='he=e-1 syn match taskrcGoodKey '^\s*\Vjournal.time.start.annotation='he=e-1 syn match taskrcGoodKey '^\s*\Vjournal.time.stop.annotation='he=e-1 syn match taskrcGoodKey '^\s*\Vjson.array='he=e-1 +syn match taskrcGoodKey '^\s*\Vlimit='he=e-1 syn match taskrcGoodKey '^\s*\Vlist.all.projects='he=e-1 syn match taskrcGoodKey '^\s*\Vlist.all.tags='he=e-1 syn match taskrcGoodKey '^\s*\Vlocale='he=e-1 diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index 27e0af744..04456840c 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -173,6 +173,7 @@ int CmdShow::execute (std::string& output) " journal.time.start.annotation" " journal.time.stop.annotation" " json.array" + " limit" " list.all.projects" " list.all.tags" " locking" From 50052f5115815a82cad36819ce23dbd534e7a0ba Mon Sep 17 00:00:00 2001 From: Andonome <52453904+Andonome@users.noreply.github.com> Date: Mon, 27 May 2024 21:50:59 +0200 Subject: [PATCH 028/242] Placing limit in man 5 taskrc (#3467) --- doc/man/taskrc.5.in | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 90fc4790b..e1d4fbb3d 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -241,6 +241,13 @@ rc.data.location or TASKDATA override) is missing. Default value is '0'. Determines whether to use ioctl to establish the size of the window you are using, for text wrapping. +.TP +.B limit:25 +Specifies the desired number of tasks a report should show, if a positive +integer is given. The value 'page' may also be used, and will limit the +report output to as many lines of text as will fit on screen. Default value +is '25'. + .TP .B defaultwidth=80 The width of output used when auto-detection support is not available. Defaults From 236a5f0bf19d1d1df504301543073d3782e02fb7 Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Wed, 29 May 2024 00:27:09 +0200 Subject: [PATCH 029/242] remove custom commands and custom scripts (#3468) Fixes #3462. --- CMakeLists.txt | 17 ------------ scripts/reproduce-dockerfile | 34 ----------------------- scripts/review-dockerfile | 54 ------------------------------------ 3 files changed, 105 deletions(-) delete mode 100644 scripts/reproduce-dockerfile delete mode 100644 scripts/review-dockerfile diff --git a/CMakeLists.txt b/CMakeLists.txt index 574f30ecd..a96ccaf81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,23 +158,6 @@ foreach (doc_FILE ${doc_FILES}) install (FILES ${doc_FILE} DESTINATION ${TASK_DOCDIR}) endforeach (doc_FILE) -add_custom_command(OUTPUT run-review - COMMAND docker build -q --build-arg PR=$(PR) --build-arg LIBPR=$(LIBPR) -t taskwarrior-review:$(PR)s$(LIBPR) - < scripts/review-dockerfile - COMMAND docker run --rm --memory 1g --hostname pr-$(PR)s$(LIBPR) -it taskwarrior-review:$(PR)s$(LIBPR) bash || : -) -add_custom_target(review DEPENDS run-review) - -add_custom_command(OUTPUT run-reproduce - COMMAND docker build -q --build-arg RELEASE=$(RELEASE) -t taskwarrior-reproduce:$(RELEASE) - < scripts/reproduce-dockerfile - COMMAND docker run --rm --memory 1g --hostname tw-$(RELEASE) -it taskwarrior-reproduce:$(RELEASE) bash || : -) -add_custom_target(reproduce DEPENDS run-reproduce) - -add_custom_command(OUTPUT show-problems - COMMAND cd test && ./problems -) -add_custom_target(problems DEPENDS show-problems) - # --- set (CPACK_SOURCE_GENERATOR "TGZ") diff --git a/scripts/reproduce-dockerfile b/scripts/reproduce-dockerfile deleted file mode 100644 index 167ea2f3d..000000000 --- a/scripts/reproduce-dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -# Dockerfile for containers to perform PR review in -# Use with make as follows: make RELEASE=v2.5.1 reproduce - -FROM centos:8 - -RUN dnf update -y -RUN yum install epel-release -y -RUN dnf install python38 vim git gcc gcc-c++ cmake make libuuid-devel libfaketime sudo man gdb -y - -RUN useradd warrior -RUN echo warrior ALL=NOPASSWD:ALL > /etc/sudoers.d/warrior - -USER warrior -WORKDIR /home/warrior/ - -# Setup taskwarrior -# The purpose is to speed up subsequent re-installs due to Docker layer caching -RUN git clone https://github.com/GothenburgBitFactory/taskwarrior.git -WORKDIR /home/warrior/taskwarrior/ -RUN git submodule init - -# Install the given release -ARG RELEASE -RUN git checkout $RELEASE -RUN git submodule update --init -RUN cmake -DCMAKE_BUILD_TYPE=debug . -RUN make -j8 -RUN sudo make install - -# Set the PS1 variable -ENV PS1="[\u@\H \W]\$ " - -WORKDIR /home/warrior -RUN task rc.confirmation=0 _ids || : # Generate default taskrc diff --git a/scripts/review-dockerfile b/scripts/review-dockerfile deleted file mode 100644 index 862b389a5..000000000 --- a/scripts/review-dockerfile +++ /dev/null @@ -1,54 +0,0 @@ -# Dockerfile for containers to perform PR review in -# Use with make as follows: make PR=1234 review - -FROM centos:8 - -# Workaround to the location of the repos -RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* -RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* - -RUN dnf update -y -RUN yum install epel-release -y -RUN dnf install python38 git gcc gcc-c++ cmake make libuuid-devel libfaketime sudo man -y - -RUN useradd warrior -RUN echo warrior ALL=NOPASSWD:ALL > /etc/sudoers.d/warrior - -USER warrior -WORKDIR /home/warrior/ - -# Setup Rust -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh && \ - sh rustup.sh -y --profile minimal --default-toolchain stable --component rust-docs - -# Setup taskwarrior -# The purpose is to speed up subsequent re-installs due to Docker layer caching -RUN git clone https://github.com/GothenburgBitFactory/taskwarrior.git -WORKDIR /home/warrior/taskwarrior/ -RUN git submodule init -RUN git submodule update -RUN cmake -DCMAKE_BUILD_TYPE=debug . -RUN make -j8 -RUN sudo make install - -# Use specified PR's branch, if provided -ARG PR -RUN if [[ ! -z $PR ]]; then \ - git fetch origin refs/pull/${PR}/head:pr-${PR}; \ - git checkout pr-${PR}; fi - -# Use specified libshared PR's branch, if provided -ARG LIBPR -WORKDIR /home/warrior/taskwarrior/src/libshared/ -RUN if [[ ! -z $LIBPR ]]; then \ - git fetch origin refs/pull/${LIBPR}/head:libpr-${LIBPR}; \ - git checkout libpr-${LIBPR}; fi - -# Install taskwarrior -WORKDIR /home/warrior/taskwarrior/ -RUN cmake -DCMAKE_BUILD_TYPE=debug . -RUN make -j8 -RUN sudo make install - -WORKDIR /home/warrior -RUN task rc.confirmation=0 _ids || : # Generate default taskrc From 977a8f3853136e25c320b7d8142c1041d5e517b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 08:00:05 -0400 Subject: [PATCH 030/242] Bump tokio from 1.37.0 to 1.38.0 (#3470) Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.37.0 to 1.38.0. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.37.0...tokio-1.38.0) --- updated-dependencies: - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 11c8e2d7b..56dfd8c69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1447,9 +1447,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -1465,9 +1465,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", From 99827de3dd62b90a6aa6332a7ae1092ae6086206 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 2 Jun 2024 17:44:04 -0400 Subject: [PATCH 031/242] Add a release-check action to verify release tarballs have all the necessary parts (#3472) --- .github/workflows/release-check.yaml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/release-check.yaml diff --git a/.github/workflows/release-check.yaml b/.github/workflows/release-check.yaml new file mode 100644 index 000000000..e9cf424ba --- /dev/null +++ b/.github/workflows/release-check.yaml @@ -0,0 +1,28 @@ +name: release-tests +on: [push, pull_request] +jobs: + check-tarball: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + + - uses: actions-rs/toolchain@v1 + with: + toolchain: "stable" + override: true + + - name: make a release tarball and build from it + run: | + cmake -S. -Bbuild && + make -Cbuild package_source && + tar -xf build/task-*.tar.gz && + cd task-*.*.* && + cmake -S. -Bbuild && + cmake --build build --target task_executable + From 5821eda98e42b16539554661b486917dfa151bc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:07:15 -0400 Subject: [PATCH 032/242] Bump docker/login-action from 3.1.0 to 3.2.0 (#3474) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v3.1.0...v3.2.0) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 375f8ea62..add92d0a2 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -32,7 +32,7 @@ jobs: uses: sigstore/cosign-installer@v3.5.0 - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3.1.0 + uses: docker/login-action@v3.2.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} From e9c6c6c846c932eea9fb46894779e10ef1f30a52 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 13 Jun 2024 14:17:23 -0400 Subject: [PATCH 033/242] Be resilient to a missing 'entry' in urgency (#3479) Any combination of properties is possible - Taskwarrior should make the best of the tasks it finds, rather than crashing. --- src/Task.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Task.cpp b/src/Task.cpp index 0d0d080b4..cb9626b41 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -2319,7 +2319,8 @@ float Task::urgency_due () const //////////////////////////////////////////////////////////////////////////////// float Task::urgency_age () const { - assert (has ("entry")); + if (!has ("entry")) + return 1.0; Datetime now; Datetime entry (get_date ("entry")); From bba010c307a86c1a542836ab619008d07751c44d Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 14 Jun 2024 19:37:47 -0400 Subject: [PATCH 034/242] Update the comms channels in README (#3484) We don't update the bird-site anymore, libera.chaat is gone (or un-monitored), and we prefer discussions over discord over reddit. --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index f1ae0998f..b09157121 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,9 @@ The [online documentation](https://taskwarrior.org/docs), downloads, news and more are available on our website, [taskwarrior.org](https://taskwarrior.org). ## Community -[![Twitter](https://img.shields.io/twitter/follow/taskwarrior?style=social)](https://twitter.com/taskwarrior) -[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/taskwarrior?style=social)](https://reddit.com/r/taskwarrior/) -[![Libera.chat](https://img.shields.io/badge/IRC%20libera.chat-online-green)](https://web.libera.chat/#taskwarrior) [![Discord](https://img.shields.io/discord/796949983734661191?label=discord)](https://discord.gg/eRXEHk8w62) [![Github discussions](https://img.shields.io/github/discussions/GothenburgBitFactory/taskwarrior?label=GitHub%20discussions)](https://github.com/GothenburgBitFactory/taskwarrior/discussions) +[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/taskwarrior?style=social)](https://reddit.com/r/taskwarrior/) Taskwarrior has a lively community on many places on the internet. From 161463deece23235e50574d0cdd28bf114bc8141 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 14 Jun 2024 22:15:21 -0400 Subject: [PATCH 035/242] Fix warning about unused method (#3488) fix warning about unused method --- src/tc/lib/src/traits.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/tc/lib/src/traits.rs b/src/tc/lib/src/traits.rs index c9040071d..0b2eac970 100644 --- a/src/tc/lib/src/traits.rs +++ b/src/tc/lib/src/traits.rs @@ -83,7 +83,7 @@ pub(crate) trait PassByPointer: Sized { /// # Safety /// /// - arg must not be NULL - /// - arg must be a value returned from Box::into_raw (via return_ptr or ptr_to_arg_out) + /// - arg must be a value returned from Box::into_raw (via return_ptr) /// - arg becomes invalid and must not be used after this call unsafe fn take_from_ptr_arg(arg: *mut Self) -> Self { debug_assert!(!arg.is_null()); @@ -127,19 +127,6 @@ pub(crate) trait PassByPointer: Sized { unsafe fn return_ptr(self) -> *mut Self { Box::into_raw(Box::new(self)) } - - /// Return a value to C, transferring ownership, via an "output parameter". - /// - /// # Safety - /// - /// - the caller must ensure that the value is eventually freed - /// - arg_out must not be NULL - /// - arg_out must point to valid, properly aligned memory for a pointer value - unsafe fn ptr_to_arg_out(self, arg_out: *mut *mut Self) { - debug_assert!(!arg_out.is_null()); - // SAFETY: see docstring - unsafe { *arg_out = self.return_ptr() }; - } } /// Support for C lists of objects referenced by value. From 82e645b9293caa09c9bc85809b4a78296077846b Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 17 Jun 2024 04:15:20 -0400 Subject: [PATCH 036/242] Fix builds with Rust 1.79 (#3487) * use underscore in taskchampion-lib name * update to corrosion 0.5.0 --- src/tc/CMakeLists.txt | 2 +- src/tc/corrosion | 2 +- src/tc/lib/Cargo.toml | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tc/CMakeLists.txt b/src/tc/CMakeLists.txt index 883367e63..ff19a08cd 100644 --- a/src/tc/CMakeLists.txt +++ b/src/tc/CMakeLists.txt @@ -27,4 +27,4 @@ set (tc_SRCS Task.cpp Task.h) add_library (tc STATIC ${tc_SRCS}) -target_link_libraries(tc taskchampion-lib) +target_link_libraries(tc taskchampion_lib) diff --git a/src/tc/corrosion b/src/tc/corrosion index 8ddd6d56c..64289b1d7 160000 --- a/src/tc/corrosion +++ b/src/tc/corrosion @@ -1 +1 @@ -Subproject commit 8ddd6d56ca597cb855f532e9ba4c7bc1cbe0803b +Subproject commit 64289b1d79d6d19cd2e241db515381a086bb8407 diff --git a/src/tc/lib/Cargo.toml b/src/tc/lib/Cargo.toml index ecfb1e931..2a48d5293 100644 --- a/src/tc/lib/Cargo.toml +++ b/src/tc/lib/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" publish = false [lib] +name = "taskchampion_lib" crate-type = ["staticlib", "rlib"] [dependencies] From dfab237830192c9ffe9517576069210456ba3b5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 12:02:51 +0000 Subject: [PATCH 037/242] Bump docker/build-push-action from 5.3.0 to 6.0.0 (#3492) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.3.0 to 6.0.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v5.3.0...v6.0.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index add92d0a2..7119397a4 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -40,7 +40,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v5.3.0 + uses: docker/build-push-action@v6.0.0 with: context: . file: "./docker/task.dockerfile" From 978879818967bb86e3af778e2ed971496e86a621 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 18 Jun 2024 22:54:46 -0400 Subject: [PATCH 038/242] Remove unnecessary cbindgen-related Makefile (#3486) --- src/tc/lib/Makefile | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 src/tc/lib/Makefile diff --git a/src/tc/lib/Makefile b/src/tc/lib/Makefile deleted file mode 100644 index d2dbe101b..000000000 --- a/src/tc/lib/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -taskchampion.h: cbindgen.toml ../target/debug/libtaskchampion.so - cbindgen --config cbindgen.toml --crate taskchampion-lib --output $@ From 24f56b65a94e86b821c1e30c88834d224e3655e4 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 19 Jun 2024 05:17:14 -0400 Subject: [PATCH 039/242] Only warn about .data files when showing reports (#3473) * Only warn about .data files when showing reports This avoids the warning appearing in shell completion, for example. * Update src/commands/CmdCustom.cpp Co-authored-by: ryneeverett --------- Co-authored-by: ryneeverett --- src/TDB2.cpp | 8 -------- src/commands/CmdCustom.cpp | 10 ++++++++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/TDB2.cpp b/src/TDB2.cpp index a8c7157dc..e871d4854 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -58,14 +58,6 @@ TDB2::TDB2 () //////////////////////////////////////////////////////////////////////////////// void TDB2::open_replica (const std::string& location, bool create_if_missing) { - File pending_data = File (location + "/pending.data"); - if (pending_data.exists()) { - Color warning = Color (Context::getContext ().config.get ("color.warning")); - std::cerr << warning.colorize ( - format ("Found existing '*.data' files in {1}", location)) << "\n"; - std::cerr << " Taskwarrior's storage format changed in 3.0, requiring a manual migration.\n"; - std::cerr << " See https://github.com/GothenburgBitFactory/taskwarrior/releases.\n"; - } replica = tc::Replica(location, create_if_missing); } diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index d6e1b7503..bb240bf7d 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -272,6 +273,15 @@ int CmdCustom::execute (std::string& output) } } + std::string location = (Context::getContext ().data_dir); + File pending_data = File (location + "/pending.data"); + if (pending_data.exists()) { + Color warning = Color (Context::getContext ().config.get ("color.warning")); + std::cerr << warning.colorize ( + format ("Found existing '*.data' files in {1}", location)) << "\n"; + std::cerr << " Taskwarrior's storage format changed in 3.0, requiring a manual migration.\n"; + std::cerr << " See https://github.com/GothenburgBitFactory/taskwarrior/releases.\n"; + } feedback_backlog (); output = out.str (); From 210ec1013219c6123eb927f754a1c20b9df8a3ae Mon Sep 17 00:00:00 2001 From: Hector Dearman Date: Thu, 20 Jun 2024 13:28:02 +0100 Subject: [PATCH 040/242] Update cargo dependencies (#3496) Run cargo update The build was failing on nightly-aarch64-apple-darwin (rustc 1.81.0-nightly (d8a38b000 2024-06-19)) due issues like: error[E0635]: unknown feature `stdsimd` --> /Users/chromy/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ahash-0.7.6/src/lib.rs:33:42 | 33 | #![cfg_attr(feature = "stdsimd", feature(stdsimd))] | ^^^^^^^ This was due to: https://users.rust-lang.org/t/error-e0635-unknown-feature-stdsimd/106445 And resolved by moving to a newer version of ahash. I confirmed the build still works on: stable-aarch64-apple-darwin (rustc 1.78.0 (9b00956e5 2024-04-29)) 1.70.0-aarch64-apple-darwin (rustc 1.70.0 (90c541806 2023-05-31)) after this change. I had to manually downgrade google-cloud-auth to v0.13.0 v0.13.2 depends on jsonwebtoken v9.3.0 which drops support for rustc 1.70.0. --- Cargo.lock | 957 ++++++++++++++++++++++++++++------------------------- 1 file changed, 504 insertions(+), 453 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56dfd8c69..c717d2849 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -19,24 +19,37 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ - "getrandom", + "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -48,9 +61,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "async-stream" @@ -71,37 +84,37 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.66", ] [[package]] name = "async-trait" -version = "0.1.69" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.66", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", "cfg-if", "libc", - "miniz_oxide 0.7.1", + "miniz_oxide", "object", "rustc-demangle", ] @@ -114,9 +127,15 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -132,9 +151,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.0.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -147,9 +166,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" @@ -159,15 +178,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -177,18 +196,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "serde", - "time 0.1.43", "wasm-bindgen", - "winapi", + "windows-targets 0.52.5", ] [[package]] @@ -198,25 +216,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] -name = "core-foundation-sys" -version = "0.8.3" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -233,9 +261,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468", @@ -243,16 +271,26 @@ dependencies = [ ] [[package]] -name = "diff" -version = "0.1.12" +name = "deranged" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -260,19 +298,25 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -305,17 +349,17 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 1.0.104", + "syn 1.0.109", ] [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", - "miniz_oxide 0.5.1", + "miniz_oxide", ] [[package]] @@ -335,53 +379,53 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 1.0.104", + "syn 2.0.66", ] [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", @@ -396,9 +440,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -406,9 +450,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -417,9 +461,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "google-cloud-auth" @@ -428,7 +472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1087f1fbd2dd3f58c17c7574ddd99cd61cbbbc2c4dc81114b8687209b196cb" dependencies = [ "async-trait", - "base64 0.21.0", + "base64 0.21.7", "google-cloud-metadata", "google-cloud-token", "home", @@ -437,7 +481,7 @@ dependencies = [ "serde", "serde_json", "thiserror", - "time 0.3.20", + "time", "tokio", "tracing", "urlencoding", @@ -462,7 +506,7 @@ checksum = "ac04b29849ebdeb9fb008988cc1c4d1f0c9d121b4c7f1ddeb8061df124580e93" dependencies = [ "async-stream", "async-trait", - "base64 0.21.0", + "base64 0.21.7", "bytes", "futures-util", "google-cloud-auth", @@ -474,12 +518,12 @@ dependencies = [ "pkcs8", "regex", "reqwest", - "ring 0.17.3", + "ring 0.17.8", "serde", "serde_json", "sha2", "thiserror", - "time 0.3.20", + "time", "tokio", "tracing", "url", @@ -496,9 +540,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.17" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -515,26 +559,21 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - -[[package]] -name = "hashbrown" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", + "allocator-api2", ] [[package]] name = "hashlink" -version = "0.8.0" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452c155cb93fecdfb02a73dd57b5d8e442c2063bd7aac72f1bc5e4263a43086" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.12.2", + "hashbrown", ] [[package]] @@ -545,12 +584,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -569,9 +605,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -591,21 +627,21 @@ dependencies = [ [[package]] name = "httparse" -version = "1.7.1" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -618,7 +654,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2", "tokio", "tower-service", "tracing", @@ -634,23 +670,32 @@ dependencies = [ "futures-util", "http", "hyper", - "rustls", + "rustls 0.21.12", "tokio", "tokio-rustls", ] [[package]] name = "iana-time-zone" -version = "0.1.47" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", + "iana-time-zone-haiku", "js-sys", - "once_cell", "wasm-bindgen", - "winapi", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", ] [[package]] @@ -665,12 +710,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.1" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ - "autocfg", - "hashbrown 0.11.2", + "equivalent", + "hashbrown", ] [[package]] @@ -690,15 +735,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.59" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -709,7 +754,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.0", + "base64 0.21.7", "pem", "ring 0.16.20", "serde", @@ -717,17 +762,11 @@ dependencies = [ "simple_asn1", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" -version = "0.2.151" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libsqlite3-sys" @@ -742,29 +781,29 @@ dependencies = [ [[package]] name = "linkme" -version = "0.3.8" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc2b30967da1bcca8f15aa741f2b949a315ef0eabd0ef630a5a0643d7a45260" +checksum = "ccb76662d78edc9f9bf56360d6919bdacc8b7761227727e5082f128eeb90bbf5" dependencies = [ "linkme-impl", ] [[package]] name = "linkme-impl" -version = "0.3.8" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a440f823b734f5a90d7cc2850a2254611092e88fa13fb1948556858ce2d35d2a" +checksum = "f8dccda732e04fa3baf2e17cf835bfe2601c7c2edafd64417c627dabae3a8cda" dependencies = [ "proc-macro2", "quote", - "syn 1.0.104", + "syn 2.0.66", ] [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -772,24 +811,21 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -803,18 +839,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -832,39 +859,43 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", @@ -872,9 +903,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -887,9 +918,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -897,15 +928,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.45.0", + "windows-targets 0.52.5", ] [[package]] @@ -934,9 +965,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -956,9 +987,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "pretty_assertions" @@ -972,36 +1009,36 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", ] [[package]] name = "regex" -version = "1.10.2" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1011,9 +1048,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1022,17 +1059,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.0", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -1050,11 +1087,13 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", + "rustls 0.21.12", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", + "system-configuration", "tokio", "tokio-rustls", "tokio-util", @@ -1064,7 +1103,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.22.6", + "webpki-roots 0.25.4", "winreg", ] @@ -1085,16 +1124,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.3" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babe80d5c16becf6594aa32ad2be8fe08498e7ae60b77de8df700e67f191d7e" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1103,7 +1143,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" dependencies = [ - "bitflags 2.0.2", + "bitflags 2.5.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1113,94 +1153,125 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustls" -version = "0.21.11" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.3", - "rustls-webpki", + "ring 0.17.8", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.0", + "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.3", + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.6" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] name = "serde" -version = "1.0.147" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 1.0.104", + "syn 2.0.66", ] [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -1221,9 +1292,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -1239,39 +1310,32 @@ dependencies = [ "num-bigint", "num-traits", "thiserror", - "time 0.3.20", + "time", ] [[package]] name = "slab" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" - -[[package]] -name = "smallvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" - -[[package]] -name = "socket2" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "libc", - "winapi", + "autocfg", ] [[package]] -name = "socket2" -version = "0.5.5" +name = "smallvec" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1312,14 +1376,20 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.18", + "syn 2.0.66", ] [[package]] -name = "syn" -version = "1.0.104" +name = "subtle" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce" +checksum = "0d0208408ba0c3df17ed26eb06992cb1a1268d41b2c0e12e65203fbe3972cee5" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -1328,15 +1398,42 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "taskchampion" version = "0.5.0" @@ -1349,7 +1446,7 @@ dependencies = [ "flate2", "google-cloud-storage", "log", - "ring 0.17.3", + "ring 0.17.8", "rusqlite", "serde", "serde_json", @@ -1375,41 +1472,34 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 1.0.104", + "syn 2.0.66", ] [[package]] name = "time" -version = "0.1.43" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ + "deranged", "itoa", + "num-conv", + "powerfmt", "serde", "time-core", "time-macros", @@ -1417,16 +1507,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -1441,9 +1532,9 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" @@ -1458,7 +1549,7 @@ dependencies = [ "num_cpus", "parking_lot", "pin-project-lite", - "socket2 0.5.5", + "socket2", "tokio-macros", "windows-sys 0.48.0", ] @@ -1471,7 +1562,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.66", ] [[package]] @@ -1480,22 +1571,21 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -1506,11 +1596,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.34" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1524,16 +1613,16 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.66", ] [[package]] name = "tracing-core" -version = "0.1.26" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] @@ -1544,9 +1633,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicase" @@ -1559,21 +1648,21 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -1592,25 +1681,26 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.0" +version = "2.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7830e33f6e25723d41a63f77e434159dad02919f18f55a512b5f16f3b1d77138" +checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" dependencies = [ - "base64 0.21.0", + "base64 0.22.1", "flate2", "log", "once_cell", - "rustls", - "rustls-webpki", + "rustls 0.22.4", + "rustls-pki-types", + "rustls-webpki 0.102.4", "url", - "webpki-roots 0.25.2", + "webpki-roots 0.26.3", ] [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -1662,9 +1752,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.82" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1672,24 +1762,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.82" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.104", + "syn 2.0.66", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.32" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -1699,9 +1789,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.82" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1709,28 +1799,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.82" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.104", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.82" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-streams" -version = "0.2.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", @@ -1741,39 +1831,29 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "webpki" -version = "0.22.4" +name = "webpki-roots" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" -dependencies = [ - "ring 0.17.3", - "untrusted 0.9.0", -] +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" dependencies = [ - "webpki", + "rustls-pki-types", ] -[[package]] -name = "webpki-roots" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" - [[package]] name = "winapi" version = "0.3.9" @@ -1797,12 +1877,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.45.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.52.5", ] [[package]] @@ -1811,7 +1891,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.5", ] [[package]] @@ -1820,187 +1900,138 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "windows_i686_gnu" -version = "0.52.0" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] @@ -2019,7 +2050,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] -name = "zeroize" -version = "1.7.0" +name = "zerocopy" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" From 0119867223cce668a69b24607487612db6ff209b Mon Sep 17 00:00:00 2001 From: Hector Dearman Date: Thu, 20 Jun 2024 13:29:39 +0100 Subject: [PATCH 041/242] Resolve a number of minor warnings (#3495) --- src/commands/CmdCalendar.cpp | 2 -- src/commands/CmdCustom.h | 2 +- src/commands/CmdTimesheet.h | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/commands/CmdCalendar.cpp b/src/commands/CmdCalendar.cpp index caf211ffc..1a956a605 100644 --- a/src/commands/CmdCalendar.cpp +++ b/src/commands/CmdCalendar.cpp @@ -177,7 +177,6 @@ int CmdCalendar::execute (std::string& output) yFrom = argYear; // Now begin the data subset and rendering. - auto countDueDates = 0; if (getPendingDate == true) { // Find the oldest pending due date. @@ -190,7 +189,6 @@ int CmdCalendar::execute (std::string& output) if (task.has ("due") && !task.hasTag ("nocal")) { - ++countDueDates; Datetime d (task.get ("due")); if (d < oldest) oldest = d; } diff --git a/src/commands/CmdCustom.h b/src/commands/CmdCustom.h index 1c805a45c..521876d65 100644 --- a/src/commands/CmdCustom.h +++ b/src/commands/CmdCustom.h @@ -35,7 +35,7 @@ class CmdCustom : public Command public: CmdCustom (const std::string&, const std::string&, const std::string&); bool uses_context () const override; - int execute (std::string&); + int execute (std::string&) override; private: void validateReportColumns (std::vector &); diff --git a/src/commands/CmdTimesheet.h b/src/commands/CmdTimesheet.h index 666e7c830..3401abec6 100644 --- a/src/commands/CmdTimesheet.h +++ b/src/commands/CmdTimesheet.h @@ -34,7 +34,7 @@ class CmdTimesheet : public Command { public: CmdTimesheet (); - int execute (std::string&); + int execute (std::string&) override; bool uses_context () const override; }; From c44229dd32260b6c3f5fd068e7df1d0937d98618 Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:31:45 +0200 Subject: [PATCH 042/242] create coverage actions (#3483) * add coveralls coverage actions to tests * using Ninja for Build * exclude build directory in coverage report * let distro tests run after successful coverage run * add coveralls badge to readme Closes #3413. --- .github/workflows/tests.yaml | 36 +++++++++++++++++++++++++++++++++++- README.md | 1 + 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index b61e9a709..72a871e2d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -1,10 +1,42 @@ ## Run the Taskwarrior tests, using stable rust to build TaskChampion. - name: tests + on: [push, pull_request] + jobs: + coverage: + runs-on: ubuntu-22.04 + steps: + - name: Install apt packages + run: sudo apt-get install -y build-essential cmake git uuid-dev faketime locales python3 curl gcovr ninja-build + + - name: Check out this repository + uses: actions/checkout@v4.1.6 + + - name: Configure project + run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS=--coverage + + - name: Build project + run: cmake --build build --target build_tests + + - name: Test project + run: ctest --test-dir build -j 8 --output-on-failure + + - name: Generate a code coverage report + uses: threeal/gcovr-action@v1.0.0 + with: + coveralls-out: coverage.coveralls.json + excludes: | + build + + - name: Sent to Coveralls + uses: coverallsapp/github-action@v2 + with: + file: coverage.coveralls.json + # MacOS tests do not run in Docker, and use the actions-rs Rust installaction tests-macos-12: + needs: coverage name: tests (Mac OS 12.latest) if: false # see #3242 runs-on: macos-latest @@ -33,6 +65,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} tests-macos-13: + needs: coverage name: tests (Mac OS 13.latest) if: false # see #3242 runs-on: macos-13 @@ -62,6 +95,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} tests: + needs: coverage strategy: fail-fast: false matrix: diff --git a/README.md b/README.md index b09157121..09565ac07 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@
    [![GitHub Actions build status](https://github.com/GothenburgBitFactory/taskwarrior/workflows/tests/badge.svg?branch=develop)](https://github.com/GothenburgBitFactory/taskwarrior/actions) +[![Coverage Status](https://coveralls.io/repos/github/GothenburgBitFactory/taskwarrior/badge.svg?branch=add-coverage-actions)](https://coveralls.io/github/GothenburgBitFactory/taskwarrior?branch=add-coverage-actions) [![Release](https://img.shields.io/github/v/release/GothenburgBitFactory/taskwarrior)](https://github.com/GothenburgBitFactory/taskwarrior/releases/latest) [![Release date](https://img.shields.io/github/release-date/GothenburgBitFactory/taskwarrior)](https://github.com/GothenburgBitFactory/taskwarrior/releases/latest) [![GitHub Sponsors](https://img.shields.io/github/sponsors/GothenburgBitFactory?color=green)](https://github.com/sponsors/GothenburgBitFactory/) From 5f983a66af8b22c4f6652df6e22194b0bc538b53 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 21 Jun 2024 08:32:09 -0400 Subject: [PATCH 043/242] Include version in default-generated .taskrc (#3500) This will avoid new users being prompted with all the news since 2.5.0. --- src/Context.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Context.cpp b/src/Context.cpp index 9d2c16686..c11e63632 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -1216,10 +1217,6 @@ void Context::createDefaultConfig () ! confirm ( format ("A configuration file could not be found in {1}\n\nWould you like a sample {2} created, so Taskwarrior can proceed?", home_dir, rc_file._data))) throw std::string ("Cannot proceed without rc file."); - // Override data.location in the defaults. - auto loc = configurationDefaults.find ("data.location=~/.task"); - // loc+0^ +14^ +21^ - Datetime now; std::stringstream contents; contents << "# [Created by " @@ -1227,10 +1224,10 @@ void Context::createDefaultConfig () << ' ' << now.toString ("m/d/Y H:N:S") << "]\n" - << configurationDefaults.substr (0, loc + 14) - << data_dir._original - << "\n\n# To use the default location of the XDG directories,\n" - << "# move this configuration file from ~/.taskrc to ~/.config/task/taskrc and uncomment below\n" + << "data.location=" << data_dir._original << "\n" + << "news.version=" << Version::Current() << "\n" + << "\n# To use the default location of the XDG directories,\n" + << "# move this configuration file from ~/.taskrc to ~/.config/task/taskrc and update location config as follows:\n" << "\n#data.location=~/.local/share/task\n" << "#hooks.location=~/.config/task/hooks\n" << "\n# Color theme (uncomment one to use)\n" @@ -1253,6 +1250,9 @@ void Context::createDefaultConfig () // Write out the new file. if (! File::write (rc_file._data, contents.str ())) throw format ("Could not write to '{1}'.", rc_file._data); + + // Load it so that it takes effect for this run. + config.load(rc_file); } } From 261e07dc0dbb54c38eb2aa54b4917417d6270a67 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 21 Jun 2024 18:18:16 -0400 Subject: [PATCH 044/242] Add a `config.toml` symlink (#3503) This also uses `cargo xtask` in the action, to test this functionality --- .cargo/config.toml | 1 + .github/workflows/checks.yml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 120000 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 120000 index 000000000..e1fd6d8b1 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1 @@ +../config.toml \ No newline at end of file diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index c95ba45fa..15183b975 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -87,8 +87,8 @@ jobs: - uses: actions-rs/cargo@v1.0.3 with: - command: run - args: --package xtask -- codegen + command: xtask + args: codegen - name: check for changes run: | From bb8a105754625639f86b59de88bf15e72e8b20b4 Mon Sep 17 00:00:00 2001 From: Adrian Galilea <90320947+adriangalilea@users.noreply.github.com> Date: Sun, 23 Jun 2024 01:46:56 +0200 Subject: [PATCH 045/242] Add bubblegum-256.theme for improved legibility and contrast (#3376) (#3505) --- doc/rc/bubblegum-256.theme | 99 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 doc/rc/bubblegum-256.theme diff --git a/doc/rc/bubblegum-256.theme b/doc/rc/bubblegum-256.theme new file mode 100644 index 000000000..2bb005611 --- /dev/null +++ b/doc/rc/bubblegum-256.theme @@ -0,0 +1,99 @@ +############################################################################### +# +# 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 +# +############################################################################### + +# Theme author: Adrian Galilea @adriangalilea + +rule.precedence.color=deleted,completed,active,keyword.,tag.,project.,overdue,scheduled,due.today,due,blocked,blocking,recurring,tagged,uda. + +# General decoration +color.label= +color.label.sort= +color.alternate=on gray2 +color.header=rgb013 +color.footnote=rgb013 +color.warning=rgb520 +color.error=red +color.debug=blue + +# Task state +color.completed= +color.deleted= +color.active=rgb553 +color.recurring=bright rgb535 +color.scheduled= +color.until= +color.blocked=gray10 +color.blocking=on rgb002 + +# Project +color.project.none= + +# Priority +color.uda.priority.H=rgb435 +color.uda.priority.L=gray15 + +# Tags +color.tag.next=rgb253 +color.tag.none= +color.tagged= + +# Due +color.due= +color.due.today=rgb125 +color.overdue=bold inverse + +# Report: burndown +color.burndown.pending=on rgb103 +color.burndown.started=on rgb214 +color.burndown.done=on gray4 + +# Report: history +color.history.add=color0 on rgb105 +color.history.done=color0 on rgb205 +color.history.delete=color0 on rgb305 + +# Report: summary +color.summary.bar=white on rgb104 +color.summary.background=white on rgb001 + +# Command: calendar +color.calendar.due=color0 on rgb325 +color.calendar.due.today=color0 on rgb404 +color.calendar.holiday=color15 on rgb102 +color.calendar.overdue=color0 on color5 +color.calendar.today=color15 on rgb103 +color.calendar.weekend=gray12 on gray3 +color.calendar.weeknumber=rgb104 + +# Command: sync +color.sync.added=gray4 +color.sync.changed=rgb214 +color.sync.rejected=rgb103 + +# Command: undo +color.undo.before=rgb103 +color.undo.after=rgb305 + From e0e6ea71708ded10d3c62fb04b3eb5f26402a98c Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Sun, 23 Jun 2024 01:52:43 +0200 Subject: [PATCH 046/242] remove test certificates and link in python code (#3506) With taskwarrior > 3.0 these are no longer required and thus can be removed. --- test/basetest/utils.py | 5 - test/test_certs/README | 5 - test/test_certs/api.cert.pem | 27 --- test/test_certs/api.key.pem | 182 -------------------- test/test_certs/ca.cert.pem | 33 ---- test/test_certs/ca.key.pem | 240 --------------------------- test/test_certs/client.cert.pem | 33 ---- test/test_certs/client.key.pem | 240 --------------------------- test/test_certs/server.cert.pem | 33 ---- test/test_certs/server.crl.pem | 18 -- test/test_certs/server.key.pem | 240 --------------------------- test/test_certs/test_client.cert.pem | 33 ---- test/test_certs/test_client.key.pem | 240 --------------------------- 13 files changed, 1329 deletions(-) delete mode 100644 test/test_certs/README delete mode 100644 test/test_certs/api.cert.pem delete mode 100644 test/test_certs/api.key.pem delete mode 100644 test/test_certs/ca.cert.pem delete mode 100644 test/test_certs/ca.key.pem delete mode 100644 test/test_certs/client.cert.pem delete mode 100644 test/test_certs/client.key.pem delete mode 100644 test/test_certs/server.cert.pem delete mode 100644 test/test_certs/server.crl.pem delete mode 100644 test/test_certs/server.key.pem delete mode 100644 test/test_certs/test_client.cert.pem delete mode 100644 test/test_certs/test_client.key.pem diff --git a/test/basetest/utils.py b/test/basetest/utils.py index ad68b8701..65859a464 100644 --- a/test/basetest/utils.py +++ b/test/basetest/utils.py @@ -32,11 +32,6 @@ BIN_PREFIX = os.path.abspath( os.path.join("${CMAKE_BINARY_DIR}","src") ) -# Default location of test certificates -DEFAULT_CERT_PATH = os.path.abspath( - os.path.join("${CMAKE_SOURCE_DIR}", "test", "test_certs") -) - # Default location of test hooks DEFAULT_HOOK_PATH = os.path.abspath( os.path.join("${CMAKE_SOURCE_DIR}", "test", "test_hooks") diff --git a/test/test_certs/README b/test/test_certs/README deleted file mode 100644 index 8623d31c6..000000000 --- a/test/test_certs/README +++ /dev/null @@ -1,5 +0,0 @@ -WARNING - -These certificates are part of the testing framework, DO NOT USE FOR OTHER PURPOSES - -WARNING diff --git a/test/test_certs/api.cert.pem b/test/test_certs/api.cert.pem deleted file mode 100644 index 85331f205..000000000 --- a/test/test_certs/api.cert.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIErDCCAxSgAwIBAgIUImT6dvD09I4yGowOKIyYCFwCU/IwDQYJKoZIhvcNAQEL -BQAwdDEVMBMGA1UEAxMMbG9jYWxob3N0IENBMR4wHAYDVQQKDBVHw7Z0ZWJvcmcg -Qml0IEZhY3RvcnkxEjAQBgNVBAcMCUfDtnRlYm9yZzEaMBgGA1UECAwRVsOkc3Ry -YSBHw7Z0YWxhbmQxCzAJBgNVBAYTAlNFMB4XDTE5MDMwMzE1MDAzOVoXDTIwMDMw -MjE1MDAzOVowNDESMBAGA1UEAxMJbG9jYWxob3N0MR4wHAYDVQQKDBVHw7Z0ZWJv -cmcgQml0IEZhY3RvcnkwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDm -SHBv3zILItaJrG+3y7dWgvsX6X3FzLVXOrhFOBNbsrHSsktt20yHZhofoelkgtlc -kBsft5GUYGEBZuJIIFcOvpbXXTz/cYge8WhpcFo64APNc7zxykaTeIHMmEqjxqtO -efyOmuEyWya2XbCA4aZ6emyirkem952wBJNYLIWoy53YQ79dFvH2SOWKPlXCa1B/ -ih7b9qYZE2XHoLQSbLCJqDWoQTxmOe13/1aKnJIuIy/igGsM7ygmphG4kzvyfbiA -o+Nd50USVeeeF8P+X2YZfXFKmbzJTg4q6UUB4vqn4XegW065eIyN1Xw3Z/l8CiP0 -G27iuk/ZfRuz00+d9oTG5OtvyCMcc/kDI4Db1Xu9C5z9UpF5IIM85Ky91YxMMqPu -ChQQoDXJ7mdtC7zRvn6t0erMVbpaqyXxKll/ybPFAZYIgV7jtSPPJDAcTsCZQLRc -VcggScYSiWkRgW+DKsECczo++6577q++cyRBtENPOXUZ6J74eKBHizYda0chVzcC -AwEAAaN2MHQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAjAPBgNV -HQ8BAf8EBQMDB6AAMB0GA1UdDgQWBBQuQdj0wAQOzML5AQ13xsb4E8Vh9DAfBgNV -HSMEGDAWgBSmHbkkwFmCruE7I7TSI0SlD1hLiDANBgkqhkiG9w0BAQsFAAOCAYEA -CD77JzLXB0YbgJrrkotw0I8rmrb8mw0tHkDGH70UyG6VhEt3vaYcYioxs3TIo23U -dhOTApGdHzjrZePa0y2Kh7YtnPLAUml6hHTATE1D7Hufdtamd56URgBrEylKkpKl -6UnwHWzlkdRWzPRo+RD9RMBzTUMiooZaP9zzzYQNEfCmMTlHqztOLPNfFR6kFLr5 -Xnc04EV/hvkByoBmo9/i4qAa3AW4y6cUHYpxVS9nBF2h364aDIIA0mn9hEewRJBq -3odng4d+e9v9b+G1ExQXM3en39REo/b73P2VATZyMxmq5gxL1OmbOa13A+S2D0Og -OkqNq6vzRhTxLbkqc0fjKv2TUFrnYCsCROvrMF+TAN0mG4NcfgeeziBTmHJq0D3I -LNTRMU/BoMYG3wGu0BUak83a9JhwViR3o0iFnctuMbD2Mz6QOawgWz8BuTcwIur6 -USeERs3+G2XX499wWswGgWK5JGZ1skD1PiRsZfuaCMJHBiWolv4/G+My5IYg6B8a ------END CERTIFICATE----- diff --git a/test/test_certs/api.key.pem b/test/test_certs/api.key.pem deleted file mode 100644 index b294f3e3f..000000000 --- a/test/test_certs/api.key.pem +++ /dev/null @@ -1,182 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA - Key Security Level: High (3072 bits) - -modulus: - 00:e6:48:70:6f:df:32:0b:22:d6:89:ac:6f:b7:cb:b7 - 56:82:fb:17:e9:7d:c5:cc:b5:57:3a:b8:45:38:13:5b - b2:b1:d2:b2:4b:6d:db:4c:87:66:1a:1f:a1:e9:64:82 - d9:5c:90:1b:1f:b7:91:94:60:61:01:66:e2:48:20:57 - 0e:be:96:d7:5d:3c:ff:71:88:1e:f1:68:69:70:5a:3a - e0:03:cd:73:bc:f1:ca:46:93:78:81:cc:98:4a:a3:c6 - ab:4e:79:fc:8e:9a:e1:32:5b:26:b6:5d:b0:80:e1:a6 - 7a:7a:6c:a2:ae:47:a6:f7:9d:b0:04:93:58:2c:85:a8 - cb:9d:d8:43:bf:5d:16:f1:f6:48:e5:8a:3e:55:c2:6b - 50:7f:8a:1e:db:f6:a6:19:13:65:c7:a0:b4:12:6c:b0 - 89:a8:35:a8:41:3c:66:39:ed:77:ff:56:8a:9c:92:2e - 23:2f:e2:80:6b:0c:ef:28:26:a6:11:b8:93:3b:f2:7d - b8:80:a3:e3:5d:e7:45:12:55:e7:9e:17:c3:fe:5f:66 - 19:7d:71:4a:99:bc:c9:4e:0e:2a:e9:45:01:e2:fa:a7 - e1:77:a0:5b:4e:b9:78:8c:8d:d5:7c:37:67:f9:7c:0a - 23:f4:1b:6e:e2:ba:4f:d9:7d:1b:b3:d3:4f:9d:f6:84 - c6:e4:eb:6f:c8:23:1c:73:f9:03:23:80:db:d5:7b:bd - 0b:9c:fd:52:91:79:20:83:3c:e4:ac:bd:d5:8c:4c:32 - a3:ee:0a:14:10:a0:35:c9:ee:67:6d:0b:bc:d1:be:7e - ad:d1:ea:cc:55:ba:5a:ab:25:f1:2a:59:7f:c9:b3:c5 - 01:96:08:81:5e:e3:b5:23:cf:24:30:1c:4e:c0:99:40 - b4:5c:55:c8:20:49:c6:12:89:69:11:81:6f:83:2a:c1 - 02:73:3a:3e:fb:ae:7b:ee:af:be:73:24:41:b4:43:4f - 39:75:19:e8:9e:f8:78:a0:47:8b:36:1d:6b:47:21:57 - 37: - -public exponent: - 01:00:01: - -private exponent: - 00:8b:e2:40:fa:93:f8:10:2f:af:66:9d:ea:97:19:16 - 5b:64:e1:26:1b:5d:9d:43:c6:7c:20:5d:43:1e:d7:13 - 82:ae:e6:30:0c:05:c5:8a:ed:4c:a6:5d:c4:ba:c3:a5 - 80:67:eb:d9:ae:20:92:3c:31:77:7b:a4:85:9c:0e:99 - 13:89:ce:93:30:3e:17:65:5d:ac:7e:34:50:a8:41:07 - 36:80:d8:d2:8f:59:c8:e7:aa:39:2f:8f:9a:8a:ec:85 - 88:15:f9:9f:e2:f8:4e:07:8a:bb:2f:58:26:19:83:f8 - de:b9:73:38:36:e9:ab:91:0a:a6:9b:80:ed:b4:cd:d4 - 45:2b:b2:ed:24:57:65:d2:c1:2a:72:d4:d1:1c:c3:26 - f1:15:28:4f:aa:8a:5f:47:28:33:51:5a:5b:48:3d:e1 - d7:1c:e8:cb:36:25:7c:6b:7f:c6:be:c2:51:1c:de:e7 - 4b:d4:90:a0:35:66:fb:f7:c5:d2:67:3d:59:a2:b6:a0 - 8a:c6:03:8b:db:3a:39:32:28:21:ba:11:3b:a3:77:90 - 59:0d:72:81:01:ad:5f:66:92:b5:6c:61:d2:8c:60:eb - e5:62:8d:5c:71:72:99:7c:12:b8:1c:c6:ba:ee:64:17 - 41:04:b6:d2:59:f0:1a:0f:74:ca:d5:19:1e:98:f4:61 - 75:c3:e1:50:27:af:fa:48:87:3d:c9:11:aa:70:3e:56 - 64:a1:dc:42:d9:16:a6:d2:14:98:48:cd:ad:d2:09:33 - 00:10:5f:3c:69:36:81:96:75:44:f8:56:95:d2:e5:76 - 85:41:85:8d:f1:64:14:05:6b:f7:2a:26:6a:d9:f0:b9 - d0:de:d6:3d:7e:7c:6c:2c:26:49:22:5e:2a:04:21:4f - c9:d7:3c:a1:1b:7b:d1:27:1d:80:38:25:57:7f:a2:99 - 46:d7:60:56:e6:5d:50:71:36:b6:af:a6:5b:d0:92:d8 - 19:0e:86:4b:3b:34:cf:cd:ea:e5:35:e4:4e:65:e3:20 - 91: - -prime1: - 00:f7:07:aa:be:24:f1:2f:26:ea:0e:45:86:b0:45:05 - df:b8:59:d6:3b:40:5c:dd:c8:bf:18:3e:76:74:e8:31 - 35:ca:b0:d4:63:df:4d:4c:d5:0c:46:6e:31:71:3c:17 - 5e:45:4e:5c:a1:96:4f:69:5e:92:bd:e8:09:27:85:50 - e3:29:1d:bb:8e:f9:2c:41:af:0f:c8:e0:d3:70:6a:d4 - b9:67:43:08:e4:4a:c9:12:f6:d2:7e:7d:bc:69:52:ba - 48:96:0a:7e:42:e7:6e:82:e8:0c:6c:1b:a5:01:f0:36 - 2c:ae:a4:2f:d8:62:ef:ab:1c:47:3a:98:79:40:68:dd - de:6a:b4:8c:53:03:09:78:11:27:36:6f:e4:2b:fb:f4 - 4d:bf:13:30:37:1f:51:fd:2f:84:d9:b9:62:ea:91:a8 - e8:72:9d:78:14:bd:5a:9e:1f:06:12:70:19:bd:3b:80 - b2:5c:33:8b:d6:5b:9c:2f:f9:12:46:55:4a:5a:10:bc - f5: - -prime2: - 00:ee:a5:18:9e:e5:9a:59:d4:d9:0c:42:4c:cf:a1:d7 - ca:8f:b5:45:24:59:f9:83:1f:f4:f2:82:01:50:3c:e0 - ae:bf:17:39:09:cd:5a:71:dd:ab:9e:b6:d6:1e:47:dc - 34:eb:28:c0:4c:77:e5:05:5d:e3:0a:57:bf:65:1f:f8 - 29:68:08:45:ee:da:01:bc:57:c6:35:a7:2c:82:62:64 - 2a:cd:46:eb:54:eb:27:e2:eb:d5:d3:d3:04:ef:05:a1 - 4c:63:ec:d4:23:6f:60:02:71:a8:c3:ab:a5:2f:26:4e - ea:e1:a3:f5:e4:d2:59:19:c9:26:18:c5:4d:45:1e:61 - 1d:53:7c:6a:83:d2:18:40:dd:10:af:e8:24:09:1a:06 - 9b:f1:51:33:8e:13:e0:ce:18:b3:f0:8b:f3:9d:12:b3 - e7:88:01:a1:c1:38:0d:c9:c6:0e:49:99:37:2f:be:60 - e3:37:9d:c6:a8:cb:4b:c5:c0:49:2a:5d:fb:f2:fb:e7 - fb: - -coefficient: - 22:52:c4:c2:c3:dd:30:13:54:c7:95:54:b2:cb:24:b5 - 0c:88:f4:22:a7:6a:13:b3:17:5f:0b:b2:8c:4f:2c:e6 - d3:b6:ca:cd:e6:70:e4:37:5d:65:16:50:ae:b8:cc:bf - 80:81:3d:e5:5c:0e:6d:ec:f9:4e:f5:49:00:fd:63:28 - 2a:ab:03:92:ae:b6:a6:97:ed:f0:97:25:55:06:3f:15 - be:cf:70:22:bd:af:f8:2c:3f:ce:d5:e5:e6:5f:94:d7 - d2:0a:fb:99:d7:ad:ab:d1:3a:b8:fb:6c:b0:47:1c:60 - 81:b1:8c:5e:df:25:8b:93:82:8c:28:52:99:5e:e7:e7 - f8:fc:30:48:bd:43:82:9e:39:2b:7b:14:6b:fb:1b:8a - 7d:f1:1e:06:01:ca:7d:59:54:79:00:fa:76:bd:a9:a1 - 02:ad:c4:7e:0b:56:c2:37:6b:7d:20:5f:53:ef:46:88 - cf:24:e2:4c:25:fc:98:49:40:38:10:19:6f:3c:18:4c - - -exp1: - 00:8b:1e:3a:3e:13:37:f0:c2:0d:96:33:f9:82:53:9c - d7:3d:4e:fa:a3:2b:c0:20:f6:e9:07:92:45:cb:d8:e7 - bd:cf:84:7e:58:30:6d:ac:13:5f:72:5a:a4:65:8c:dd - ec:2d:43:d0:4f:00:03:80:e7:cd:e4:3d:44:ca:88:fd - e0:b0:4b:1a:51:8e:6a:2a:23:98:d4:1c:29:77:69:f2 - 9a:e7:58:8d:2d:64:20:91:19:87:b9:cc:bd:ca:e2:d8 - 1e:00:c1:b0:11:a5:9c:4b:04:bb:da:36:47:5b:2c:18 - 96:59:54:05:cd:eb:09:e6:67:6a:85:c9:50:9f:c1:6f - 11:cf:2e:16:c8:b9:31:1f:f9:29:08:33:43:60:b1:e8 - 07:d0:cf:d1:9b:79:7c:07:06:37:df:15:d4:6b:1d:d4 - ed:f3:7e:53:1d:fa:f5:89:8f:17:30:53:09:6b:d4:92 - c9:df:ba:f7:c9:a4:95:f5:3e:63:d8:50:38:2b:38:b9 - f1: - -exp2: - 00:ed:06:45:41:ec:c2:35:5e:d6:84:fa:84:d7:e4:e3 - 33:69:30:9d:8f:d1:5d:a5:02:e4:82:c8:e5:0d:10:aa - 08:65:fb:66:c7:79:92:cf:6d:5f:bb:af:d5:53:16:04 - 7c:fa:e3:ea:bb:08:8a:0b:9e:88:96:09:39:2b:f3:68 - c3:97:74:40:21:4f:9e:51:b6:cc:43:15:db:7b:54:c6 - 30:4c:da:97:7a:2c:65:dd:58:67:74:90:2e:62:48:b1 - 3f:f2:2f:93:33:ee:b6:e9:36:82:6c:75:db:06:cd:81 - ac:80:98:1c:ee:3c:8e:0a:b2:62:88:4f:ce:c3:4b:bd - 21:27:7e:77:3c:9e:3b:40:91:50:b5:a6:57:c4:42:79 - 36:01:a4:a9:14:00:62:53:d0:ed:47:89:79:59:14:ee - 62:94:0f:2a:dd:82:13:0f:c9:0a:ff:c6:91:ad:75:e5 - 3d:48:4c:08:b8:35:d2:f8:82:57:29:21:57:d0:aa:aa - 69: - - -Public Key PIN: - pin-sha256:SSdvX3918szw/veSWFuQJD3lGynf9mFLiTxpZPj/Jz4= -Public Key ID: - sha256:49276f5f7f75f2ccf0fef792585b90243de51b29dff6614b893c6964f8ff273e - sha1:2e41d8f4c0040eccc2f9010d77c6c6f813c561f4 - ------BEGIN RSA PRIVATE KEY----- -MIIG5QIBAAKCAYEA5khwb98yCyLWiaxvt8u3VoL7F+l9xcy1Vzq4RTgTW7Kx0rJL -bdtMh2YaH6HpZILZXJAbH7eRlGBhAWbiSCBXDr6W1108/3GIHvFoaXBaOuADzXO8 -8cpGk3iBzJhKo8arTnn8jprhMlsmtl2wgOGmenpsoq5HpvedsASTWCyFqMud2EO/ -XRbx9kjlij5VwmtQf4oe2/amGRNlx6C0Emywiag1qEE8Zjntd/9WipySLiMv4oBr -DO8oJqYRuJM78n24gKPjXedFElXnnhfD/l9mGX1xSpm8yU4OKulFAeL6p+F3oFtO -uXiMjdV8N2f5fAoj9Btu4rpP2X0bs9NPnfaExuTrb8gjHHP5AyOA29V7vQuc/VKR -eSCDPOSsvdWMTDKj7goUEKA1ye5nbQu80b5+rdHqzFW6Wqsl8SpZf8mzxQGWCIFe -47UjzyQwHE7AmUC0XFXIIEnGEolpEYFvgyrBAnM6Pvuue+6vvnMkQbRDTzl1Geie -+HigR4s2HWtHIVc3AgMBAAECggGBAIviQPqT+BAvr2ad6pcZFltk4SYbXZ1Dxnwg -XUMe1xOCruYwDAXFiu1Mpl3EusOlgGfr2a4gkjwxd3ukhZwOmROJzpMwPhdlXax+ -NFCoQQc2gNjSj1nI56o5L4+aiuyFiBX5n+L4TgeKuy9YJhmD+N65czg26auRCqab -gO20zdRFK7LtJFdl0sEqctTRHMMm8RUoT6qKX0coM1FaW0g94dcc6Ms2JXxrf8a+ -wlEc3udL1JCgNWb798XSZz1ZoragisYDi9s6OTIoIboRO6N3kFkNcoEBrV9mkrVs -YdKMYOvlYo1ccXKZfBK4HMa67mQXQQS20lnwGg90ytUZHpj0YXXD4VAnr/pIhz3J -EapwPlZkodxC2Ram0hSYSM2t0gkzABBfPGk2gZZ1RPhWldLldoVBhY3xZBQFa/cq -JmrZ8LnQ3tY9fnxsLCZJIl4qBCFPydc8oRt70ScdgDglV3+imUbXYFbmXVBxNrav -plvQktgZDoZLOzTPzerlNeROZeMgkQKBwQD3B6q+JPEvJuoORYawRQXfuFnWO0Bc -3ci/GD52dOgxNcqw1GPfTUzVDEZuMXE8F15FTlyhlk9pXpK96AknhVDjKR27jvks -Qa8PyODTcGrUuWdDCORKyRL20n59vGlSukiWCn5C526C6AxsG6UB8DYsrqQv2GLv -qxxHOph5QGjd3mq0jFMDCXgRJzZv5Cv79E2/EzA3H1H9L4TZuWLqkajocp14FL1a -nh8GEnAZvTuAslwzi9ZbnC/5EkZVSloQvPUCgcEA7qUYnuWaWdTZDEJMz6HXyo+1 -RSRZ+YMf9PKCAVA84K6/FzkJzVpx3auettYeR9w06yjATHflBV3jCle/ZR/4KWgI -Re7aAbxXxjWnLIJiZCrNRutU6yfi69XT0wTvBaFMY+zUI29gAnGow6ulLyZO6uGj -9eTSWRnJJhjFTUUeYR1TfGqD0hhA3RCv6CQJGgab8VEzjhPgzhiz8IvznRKz54gB -ocE4DcnGDkmZNy++YOM3ncaoy0vFwEkqXfvy++f7AoHBAIseOj4TN/DCDZYz+YJT -nNc9TvqjK8Ag9ukHkkXL2Oe9z4R+WDBtrBNfclqkZYzd7C1D0E8AA4DnzeQ9RMqI -/eCwSxpRjmoqI5jUHCl3afKa51iNLWQgkRmHucy9yuLYHgDBsBGlnEsEu9o2R1ss -GJZZVAXN6wnmZ2qFyVCfwW8Rzy4WyLkxH/kpCDNDYLHoB9DP0Zt5fAcGN98V1Gsd -1O3zflMd+vWJjxcwUwlr1JLJ37r3yaSV9T5j2FA4Kzi58QKBwQDtBkVB7MI1XtaE -+oTX5OMzaTCdj9FdpQLkgsjlDRCqCGX7Zsd5ks9tX7uv1VMWBHz64+q7CIoLnoiW -CTkr82jDl3RAIU+eUbbMQxXbe1TGMEzal3osZd1YZ3SQLmJIsT/yL5Mz7rbpNoJs -ddsGzYGsgJgc7jyOCrJiiE/Ow0u9ISd+dzyeO0CRULWmV8RCeTYBpKkUAGJT0O1H -iXlZFO5ilA8q3YITD8kK/8aRrXXlPUhMCLg10viCVykhV9CqqmkCgcAiUsTCw90w -E1THlVSyyyS1DIj0IqdqE7MXXwuyjE8s5tO2ys3mcOQ3XWUWUK64zL+AgT3lXA5t -7PlO9UkA/WMoKqsDkq62ppft8JclVQY/Fb7PcCK9r/gsP87V5eZflNfSCvuZ162r -0Tq4+2ywRxxggbGMXt8li5OCjChSmV7n5/j8MEi9Q4KeOSt7FGv7G4p98R4GAcp9 -WVR5APp2vamhAq3EfgtWwjdrfSBfU+9GiM8k4kwl/JhJQDgQGW88GEw= ------END RSA PRIVATE KEY----- diff --git a/test/test_certs/ca.cert.pem b/test/test_certs/ca.cert.pem deleted file mode 100644 index 9832d1231..000000000 --- a/test/test_certs/ca.cert.pem +++ /dev/null @@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFoDCCA4igAwIBAgIMWId0/Tdzb0Anm7xOMA0GCSqGSIb3DQEBCwUAMHQxFTAT -BgNVBAMTDGxvY2FsaG9zdCBDQTEeMBwGA1UECgwVR8O2dGVib3JnIEJpdCBGYWN0 -b3J5MRIwEAYDVQQHDAlHw7Z0ZWJvcmcxGjAYBgNVBAgMEVbDpHN0cmEgR8O2dGFs -YW5kMQswCQYDVQQGEwJTRTAeFw0xNzAxMjQxNTM4MzdaFw0xODAxMjQxNTM4Mzda -MHQxFTATBgNVBAMTDGxvY2FsaG9zdCBDQTEeMBwGA1UECgwVR8O2dGVib3JnIEJp -dCBGYWN0b3J5MRIwEAYDVQQHDAlHw7Z0ZWJvcmcxGjAYBgNVBAgMEVbDpHN0cmEg -R8O2dGFsYW5kMQswCQYDVQQGEwJTRTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC -AgoCggIBAKc95KldnRqe+UVbHdcQLOfCpCevV8vJjwlgiEZX6iPaekPyVjGnxAQ3 -Zq2j1mepTbRh37FBb1xl8vPu2UHRe7d79LF7M6SlP/Wz/wpi74hiDaOgnSVS/7c+ -wg/nJqtuCUPAJUW+z5m3PtlyGj/4Xt6yjBQ4wN1uLY45JfIE6OdW9KRTAy5s+Kpf -qGSyb9HLAjsNgB65BCL8/ZQYc9v5P/2woyqR0Ee09R1JL0gvJEqBvdvl7DcdRhqt -XFl1f7wnEW+Qwg0d+YkFnGX8gL3f9CYWaKm58Ys0A6kq/EzlbPCTn8PqgEP8pVZ1 -/+WGVirULfqjzLOwhUEHzkCggKSpLIkrm5OzDtx1lQ9KSF9M9q+hxiNzg9djMhvI -fjOOkL8M7e2+gmnfuJ8oJSkf6md9lNEar/r+K1B9p6vOF3i6elCu79L4xSnMtM2O -88G9TTjwRX9CtT2qqCLbMPm8eKc/yVmMNhLDWyUyfXZ9BLrXhFAwclwCRwMHfnkC -ORW1EZvFGtmimxJSTDlCWYb1/ciB2MSIJktNpNPUdV/IhdEVDZjBGtBxEfWaW3CE -PU1QbakBMNdfLfGHGQhFOklrZuQFngwEEHaSgojxXkz/Yloh+rLHZnSltfchBaVm -DwzdqdD4dztMNI03VB5isZtl10cPMjfiEQ9u9ch98wZj3yLidtJfAgMBAAGjMjAw -MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIs7BvGArBW1hgbUU7xdTWfwJAvg -MA0GCSqGSIb3DQEBCwUAA4ICAQB9D0662JXbMwqQL5pvPEUZiBN2DSmuijNEIFEY -Q83zy0PcNg9Gce3tBdy4GI5gk5HpfDWwd78ahnqotxZUVt2RbdsaXvZ5VYRpIiyZ -dY9fBG1g23iE6cmlW8FTRkcke/cjOOtHfohys9tFdRrS+pIMzP+BB9tOoxfpQ+8w -LgHEzZ4I0EGKsqoSPOPHDug9ZGN3IPZTRKdCxahV4cc0nxVbYFebJqpocv7xkXLb -HQA5HkhImKDPzY6cX+n+zL9f8F0KozXqt/f9AP3v2F/QNWRSjiiPCFw5qhKiyeRv -bbrW01XJsUDmPxL08U0fenPYC9RuzDGWeJJxFpG6OSfn/dOry3As7bvrh6C9Q5ox -QLrEFRIbrq37DICBTTs4LW0wG1A0kKLxTVsTK7e99FCZwfKO2K6ra3UPNwlQYxlv -C7NMEtbCGx43gWoQngpzYNvoXyb8j5GhzEeniELdCzISXMBx3tPhGc9ZWvYtMe5c -ZdN67pcx6hNPs3PpVuHS5otDUe2rw7DlqeMJokkoPgFD7CDioRZbq/kMvbkHTXAs -0rqUkH9SP14q6SbuA7t/j43JGrzKiayKYdXmn/EGd31GUdI6hOWCLzsLKg/8lDA1 -TposJN5apxRy3FVuuFcFbG7TjEFu5LGQUJrKQYbrnED74btoALBenrNHEznQhXfO -HbNnhg== ------END CERTIFICATE----- diff --git a/test/test_certs/ca.key.pem b/test/test_certs/ca.key.pem deleted file mode 100644 index fcb891c37..000000000 --- a/test/test_certs/ca.key.pem +++ /dev/null @@ -1,240 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA - Key Security Level: High (4096 bits) - -modulus: - 00:a7:3d:e4:a9:5d:9d:1a:9e:f9:45:5b:1d:d7:10:2c - e7:c2:a4:27:af:57:cb:c9:8f:09:60:88:46:57:ea:23 - da:7a:43:f2:56:31:a7:c4:04:37:66:ad:a3:d6:67:a9 - 4d:b4:61:df:b1:41:6f:5c:65:f2:f3:ee:d9:41:d1:7b - b7:7b:f4:b1:7b:33:a4:a5:3f:f5:b3:ff:0a:62:ef:88 - 62:0d:a3:a0:9d:25:52:ff:b7:3e:c2:0f:e7:26:ab:6e - 09:43:c0:25:45:be:cf:99:b7:3e:d9:72:1a:3f:f8:5e - de:b2:8c:14:38:c0:dd:6e:2d:8e:39:25:f2:04:e8:e7 - 56:f4:a4:53:03:2e:6c:f8:aa:5f:a8:64:b2:6f:d1:cb - 02:3b:0d:80:1e:b9:04:22:fc:fd:94:18:73:db:f9:3f - fd:b0:a3:2a:91:d0:47:b4:f5:1d:49:2f:48:2f:24:4a - 81:bd:db:e5:ec:37:1d:46:1a:ad:5c:59:75:7f:bc:27 - 11:6f:90:c2:0d:1d:f9:89:05:9c:65:fc:80:bd:df:f4 - 26:16:68:a9:b9:f1:8b:34:03:a9:2a:fc:4c:e5:6c:f0 - 93:9f:c3:ea:80:43:fc:a5:56:75:ff:e5:86:56:2a:d4 - 2d:fa:a3:cc:b3:b0:85:41:07:ce:40:a0:80:a4:a9:2c - 89:2b:9b:93:b3:0e:dc:75:95:0f:4a:48:5f:4c:f6:af - a1:c6:23:73:83:d7:63:32:1b:c8:7e:33:8e:90:bf:0c - ed:ed:be:82:69:df:b8:9f:28:25:29:1f:ea:67:7d:94 - d1:1a:af:fa:fe:2b:50:7d:a7:ab:ce:17:78:ba:7a:50 - ae:ef:d2:f8:c5:29:cc:b4:cd:8e:f3:c1:bd:4d:38:f0 - 45:7f:42:b5:3d:aa:a8:22:db:30:f9:bc:78:a7:3f:c9 - 59:8c:36:12:c3:5b:25:32:7d:76:7d:04:ba:d7:84:50 - 30:72:5c:02:47:03:07:7e:79:02:39:15:b5:11:9b:c5 - 1a:d9:a2:9b:12:52:4c:39:42:59:86:f5:fd:c8:81:d8 - c4:88:26:4b:4d:a4:d3:d4:75:5f:c8:85:d1:15:0d:98 - c1:1a:d0:71:11:f5:9a:5b:70:84:3d:4d:50:6d:a9:01 - 30:d7:5f:2d:f1:87:19:08:45:3a:49:6b:66:e4:05:9e - 0c:04:10:76:92:82:88:f1:5e:4c:ff:62:5a:21:fa:b2 - c7:66:74:a5:b5:f7:21:05:a5:66:0f:0c:dd:a9:d0:f8 - 77:3b:4c:34:8d:37:54:1e:62:b1:9b:65:d7:47:0f:32 - 37:e2:11:0f:6e:f5:c8:7d:f3:06:63:df:22:e2:76:d2 - 5f: - -public exponent: - 01:00:01: - -private exponent: - 1c:34:af:14:f5:69:e2:ac:7f:23:f8:5b:0f:03:76:5f - 5c:0f:6d:76:00:1b:a9:91:cb:26:11:b5:b3:6c:14:c1 - eb:2e:fc:77:17:06:d1:63:58:a8:a3:8e:67:41:b2:67 - 1d:8e:08:39:0f:ed:25:2b:38:8f:75:70:04:ce:bc:cb - d4:47:0f:8b:d4:c8:e6:e9:e3:99:88:e1:0a:90:95:72 - dc:14:05:a9:9b:3a:e5:4f:d5:70:cb:57:d3:c8:c3:d5 - 22:2d:0c:dc:37:73:31:dc:9a:e7:f7:7b:7f:e0:76:b7 - 9e:6b:4b:99:ca:c5:4d:a8:b7:3c:e2:2e:70:2f:8b:9c - b9:c3:e1:10:3f:4b:37:a1:1c:62:fd:20:af:05:35:f4 - d9:5a:cc:89:e1:f0:55:aa:4f:66:23:bd:9e:92:e9:fc - f2:46:82:8e:77:0f:30:f0:ca:10:a6:bd:c8:dd:99:07 - 4a:ab:04:9b:13:2c:87:bf:46:0f:b6:32:bf:e9:4e:b9 - 7d:14:6f:f5:e0:61:4e:7f:b8:c1:e6:95:1b:f0:e2:6f - 6e:06:16:e1:fa:5f:dc:d4:e5:3a:0f:b5:34:58:6c:36 - a9:92:54:41:6f:b5:f3:78:a2:8c:16:fc:d2:19:85:11 - 80:1d:12:10:18:2c:51:2c:6c:d7:af:7a:22:cb:39:6c - 5f:18:2b:75:9d:1c:45:70:31:22:da:a5:c8:72:bb:e8 - 85:00:56:3d:18:f0:0e:3a:a7:73:49:a4:b1:d2:42:9e - 9e:4d:21:9d:69:d5:b7:e0:1e:b8:91:e4:94:1e:b5:f1 - 31:3e:1e:f3:20:2c:ab:e7:bf:3f:f1:88:15:4c:bf:a6 - e8:03:78:35:8a:94:d3:2d:e9:c7:07:3a:f8:d8:c8:9d - 7d:87:3a:c2:a7:f7:4d:62:d3:2c:09:e1:7b:a6:e5:99 - ba:7c:24:08:16:3a:2a:29:21:e5:78:47:fb:13:f8:e4 - de:05:2b:ac:73:f0:08:9d:d5:4d:fd:ca:6a:50:1e:b7 - 09:f0:31:47:5c:b7:09:9b:3a:af:0d:df:fb:89:88:4b - 98:1e:2e:dc:f7:4a:2b:74:a6:4b:1d:ef:76:5f:fb:36 - 6f:ce:f4:34:8b:40:38:58:8d:3b:7e:42:97:74:0a:0c - d8:41:98:81:7b:18:bb:e3:b9:2e:f9:3a:ef:91:ca:41 - 46:e0:a0:99:70:e0:32:ce:09:5a:78:ed:2e:3a:4e:de - b3:a4:85:1a:05:8a:fe:bf:25:a1:cf:d7:de:6f:33:81 - 33:6e:c3:82:5d:de:f9:93:ba:06:46:ec:d1:d3:3a:80 - 39:94:d9:35:5f:58:df:55:2d:b6:4d:0b:66:e2:f8:39 - - -prime1: - 00:db:a2:a3:71:db:9f:13:8a:1e:4a:a6:b9:8a:4e:bd - eb:89:c3:27:1d:29:42:c4:71:0e:cc:5e:59:fd:63:72 - d6:5d:d4:1b:16:57:e6:9e:4e:e7:b6:dc:2b:30:a2:55 - be:36:94:0c:e1:06:ea:e0:c3:9f:fa:89:d0:eb:05:f0 - a2:ed:9c:45:42:46:4b:85:45:f2:ab:63:b9:4f:6e:1b - c4:fd:15:a4:09:26:f0:b1:77:e3:ee:2a:30:3b:4d:e2 - 0e:53:35:94:9e:26:aa:b6:65:d9:1d:de:94:79:5f:47 - 80:7f:34:cf:d9:2a:63:0f:1a:9f:4a:ea:10:95:7c:2f - 13:4b:a4:37:5a:aa:cd:55:3c:6f:38:a2:10:06:2a:4e - 26:2b:47:22:ae:c6:73:38:8a:cc:0c:05:92:86:ae:9a - 37:aa:c5:65:92:72:dc:d0:45:3f:a3:b6:b2:c0:1f:b6 - 1a:66:16:a8:45:3a:8e:14:d3:a0:1e:85:a5:59:64:6e - a5:49:43:76:62:52:79:4b:85:88:af:ec:07:5e:32:39 - 6e:71:65:2f:90:cc:32:7d:1d:db:8c:40:b7:ab:e9:46 - 22:42:29:0f:32:af:7d:05:6a:67:ab:9b:80:57:5e:12 - 4f:26:54:a6:3b:9c:88:8c:58:5f:20:02:f3:4c:a6:32 - 5d: - -prime2: - 00:c2:ee:86:bc:d2:32:81:f8:a1:7c:3e:4b:6e:03:66 - 34:dc:1f:2f:db:44:ec:de:2a:d0:58:fc:0c:08:6a:4c - 8d:27:2f:d1:c9:10:18:ed:ee:00:72:75:78:6e:fe:08 - 70:5d:77:57:b9:0f:5f:0e:70:ad:50:1e:75:52:b0:80 - aa:70:44:44:bb:38:ae:cb:a4:4e:e1:29:3b:3a:98:8f - 4a:48:f3:8d:2a:89:d5:02:f3:e2:0b:3e:f5:d1:14:af - 87:cf:69:d1:4f:ca:d5:af:2d:c7:53:61:09:c7:52:09 - 62:c7:66:9e:87:e3:6c:94:a9:44:a9:ae:48:f9:0a:c9 - 65:b0:85:87:9a:b3:f8:62:f6:8d:7e:24:84:5d:cf:8b - 0e:44:8e:c4:4e:67:41:ab:f2:b0:76:27:c1:62:dd:3b - 65:07:23:27:c4:2c:43:1f:8b:65:7c:db:6c:fa:05:dd - 89:c7:21:a3:df:96:42:7a:e7:12:e3:f2:02:a6:f0:5a - 9e:50:37:29:0a:fa:de:4c:82:41:9f:9d:cc:43:b6:c0 - e4:5a:2d:40:ad:3f:61:f6:31:b2:76:ff:57:86:ec:b9 - 57:26:db:b1:f6:3b:ee:eb:82:f0:ca:15:d1:c9:ea:3b - 81:b5:f1:a9:ca:b7:88:77:fb:dc:0f:b4:f5:32:95:83 - eb: - -coefficient: - 63:04:df:d0:5f:bd:e4:1f:94:21:97:c6:60:6e:1c:e8 - fb:1a:d8:96:82:a4:4d:2d:2b:97:0c:dc:58:20:63:a7 - 9d:dc:bf:4c:0a:49:59:12:2f:0b:b2:2d:e6:f5:3a:1e - 88:d9:31:db:49:38:22:0b:ac:36:68:35:d5:49:10:6e - 2f:45:7e:55:32:4b:7f:cc:b2:23:2f:c3:34:6e:61:ba - 9f:20:66:52:e1:89:80:d2:3f:9e:84:74:bb:46:dc:55 - 93:6a:11:78:8c:4c:d7:84:8c:7f:ee:8c:cd:7a:13:66 - d4:6c:15:43:6f:8d:c3:ed:86:ba:41:7d:d0:6e:24:b1 - a7:58:7a:b4:f5:47:7d:b4:8c:38:6f:18:39:4a:e4:04 - 04:f9:5f:e1:1a:b1:f8:10:e1:05:e2:7f:d6:64:94:1f - f0:7f:52:bf:01:79:49:06:5f:92:21:24:0e:d8:e3:53 - 65:09:12:ff:33:b5:99:83:49:30:2a:8b:44:03:18:6b - 30:84:ff:61:91:63:2e:6a:72:16:e6:40:d8:10:f9:60 - cc:09:1c:4a:15:3c:49:9a:86:b3:66:f6:83:55:8d:86 - 33:c3:13:e1:61:d2:5e:7d:d8:02:67:c5:5d:3e:82:d1 - 8c:f5:93:4a:83:35:1c:7b:d7:a0:e5:f6:76:20:dc:e3 - - -exp1: - 00:8c:4e:67:6f:32:c7:7a:69:ff:53:dd:12:26:02:8b - 5c:ce:52:b6:c7:f0:35:d0:dc:10:82:09:bc:1f:1c:ca - 9d:9a:af:06:e6:cd:1c:6c:01:28:11:2e:b1:00:8e:93 - 8b:c1:bd:ee:44:4a:70:fd:48:bb:08:07:c6:48:7d:30 - ee:44:30:52:26:36:55:c4:3b:9c:fc:53:e6:6b:91:81 - 77:e3:dd:51:3e:ef:7b:4f:43:b1:7d:d1:c1:a2:00:ec - ea:0d:c9:ea:b8:e5:26:a5:02:87:22:87:af:fd:a1:1a - 42:42:00:f0:ce:60:3f:8b:c4:8c:02:05:68:b6:b5:f0 - 34:b9:5b:9c:68:f5:37:da:bd:92:a7:71:10:1f:80:23 - 10:4b:11:54:5c:a6:35:b0:e7:29:d6:7c:30:d0:8c:f5 - c3:9d:fa:20:1a:df:f9:a3:aa:b9:2e:d2:65:7a:4a:47 - c0:b1:7a:c3:e0:05:ba:02:1f:f6:e7:c8:a4:8a:68:98 - 2e:4a:91:28:2d:43:56:6d:22:4e:e1:63:3c:89:b6:37 - ee:0f:ce:7e:06:58:0b:e0:d5:94:0d:12:c2:c2:b5:70 - b2:2d:e5:a0:e9:d6:25:5d:b2:4d:ab:d8:d8:b1:f7:4c - 66:f7:ab:97:cd:f8:ff:59:f8:92:3c:16:e6:6a:0e:b9 - 99: - -exp2: - 6c:20:a5:8a:9d:ca:38:80:33:0b:58:fe:bc:52:94:62 - 23:ec:04:be:cb:57:dc:d0:c0:eb:6b:3c:3d:7a:c1:65 - d6:e3:d6:a9:82:0d:62:05:90:27:ed:73:55:96:02:16 - 5a:32:35:ed:c8:52:01:0f:8d:83:90:b1:a1:a1:98:6d - 72:82:db:e1:42:4a:eb:59:04:0c:2f:b3:d4:08:06:0b - bd:b5:a3:8b:82:13:2f:65:1a:f1:04:06:94:e8:b5:89 - 4f:0b:32:78:e1:59:70:a7:a3:16:64:10:69:88:de:fd - f4:ff:be:56:40:57:dd:8a:9b:34:c8:21:60:4b:94:75 - 9c:da:24:e3:32:8b:48:51:dd:20:a8:9e:e7:ee:95:02 - e0:12:95:5f:ac:35:28:6d:21:67:19:1c:53:7b:c6:53 - 7f:07:08:2a:f0:26:77:9b:fa:3a:ac:f8:a1:23:99:c4 - b0:39:bf:cd:e9:6a:8d:04:1a:5d:68:c4:01:d7:f7:5c - 33:c0:b5:3a:8a:f7:be:56:ce:91:fc:94:ee:c6:b3:ee - b6:fa:bd:12:9c:c2:f9:2e:8e:01:26:18:66:16:6f:a4 - a1:40:62:51:e4:e2:dd:ae:af:64:59:80:1a:51:9c:9a - 91:d3:30:4c:eb:4b:7e:ca:ad:41:e5:2d:d1:d6:4f:eb - - - -Public Key ID: - sha256:1C:1B:09:C3:1E:D5:B3:D7:16:60:45:8E:DB:B4:64:4D:67:F3:81:11:49:3B:D3:A1:F7:EE:4E:75:27:F7:66:03 - sha1:8B:3B:06:F1:80:AC:15:B5:86:06:D4:53:BC:5D:4D:67:F0:24:0B:E0 -Public key's random art: -+--[ RSA 4096]----+ -|o...+. ...o+.+ | -| ..+ o. ...B | -| .oo+ oE. . . | -| .+.o. . | -| o + S | -|. . .. . | -| .. . | -| o. | -| ... | -+-----------------+ - ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/test/test_certs/client.cert.pem b/test/test_certs/client.cert.pem deleted file mode 100644 index 27838d74f..000000000 --- a/test/test_certs/client.cert.pem +++ /dev/null @@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFpDCCA4ygAwIBAgIMWId1Agdxxug/oIa6MA0GCSqGSIb3DQEBCwUAMHQxFTAT -BgNVBAMTDGxvY2FsaG9zdCBDQTEeMBwGA1UECgwVR8O2dGVib3JnIEJpdCBGYWN0 -b3J5MRIwEAYDVQQHDAlHw7Z0ZWJvcmcxGjAYBgNVBAgMEVbDpHN0cmEgR8O2dGFs -YW5kMQswCQYDVQQGEwJTRTAeFw0xNzAxMjQxNTM4NDJaFw0xODAxMjQxNTM4NDJa -MDQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGA1UECgwVR8O2dGVib3JnIEJpdCBG -YWN0b3J5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApOz4wcdTiEdj -O192j39Q1EhzLq9qqZghpWUxQHSV0/Fpm7kUj/wtK1xky130+H2ab+k/Sp8jjda0 -TVV7es7yXYQM2iYEDIc1i3oCtygFkISFpzNG5SGLV2GANovwYfgPhvpnGo9inAVJ -lXv2DJouLBxsL3c86dA3auMjcJ8FNgfvSJ7So7vEdoZQo1QGKpToUUVPD9e+K/y+ -j4WI8swP8lJaZ/EIDK74ESAyyk7R+jYjItF4GrwR7RM7CxWHOENwO/3pxbiJF+oW -XOt+2jsTTKyL1FZJHI2beBcPvQql9baBY/q0koT7cS8PJiGI/D6TqXQ25XkbTG/l -dmzJ7K5toLJ+7iUcdt+q8sF0owS4hR/3VUXM24DgBduzvmihD9G0k0wDNmjHu3te -8tGQogvMJRTUtiw1AEO2DiUUZqd6p/pFGrJfRyxU6nHBvr1ADeClBwcQB0hH2mVD -59affexzBsUwhouk3v0fTgNQpX2/qfeKwVbnV4bEN1fd+xSwzdmSelLa6rE+pYER -JOl+zdLSq0AtZE2GTp2A60jk+AurALcQqhdNU9YV8tCVhFTidO49GXqTmuAJAzvu -SAoIqCFuU1TWeiNQCHI3clzxUAZcqDBxTZUvbkP3QK7v0Ib5c7HiRp5kmO4CQuUg -y8mT1RZhaMp9qIy3HKUNEBdYwhj45wECAwEAAaN2MHQwDAYDVR0TAQH/BAIwADAT -BgNVHSUEDDAKBggrBgEFBQcDAjAPBgNVHQ8BAf8EBQMDB6AAMB0GA1UdDgQWBBQ3 -DVC7k5DA5XjW4dD+mWRthF48NjAfBgNVHSMEGDAWgBSLOwbxgKwVtYYG1FO8XU1n -8CQL4DANBgkqhkiG9w0BAQsFAAOCAgEASw6rojEtxVNLmqhuCGRXpBkeJcEC3hgd -usOmw3QFwMRwG0JkQuDTZobs20bWCSJzCnGcou6mvdOhjtaErZ/KUA2uAr9YvlmO -+F7mpXlGjfETm3Ta8JzvTtqgCPjQmz+Wn7nnFmwl9MLOOh//NXQ1KJd/ixWtpaLG -GhY3zduNczLT7O+qganFneostaZFpa/BL3PVXoI669zp7GmzJYSjuxQIyAMP/hKJ -aFfTNIv9WhPzXrzEviLspJ7OwfWW/S46WZWcXzauXiYS5y21rfFSBSAo1J3oRV95 -qi2IMM4PVpwQISMPwdJe3f2+Gc6qn3TaHFrnSv8OuRNOdvuqV9MLn9uxovgTTyw4 -j1sb7kO6OoLCqrbdZ+lTHy48jahpV3q+Vcep8WA+fC42sV/lfMuuS3EaQTJIfu89 -+R+pFQPco60risM8Gy/k0QkwxVwoxsq5xM+SaGyHpEazSasX/qJBj+VH97a9rlje -fzPpmApWRtZNMNx4qSx7ZEFVtSynM7vDWsWsB9x+OYdObzl4bbZUZ3MSCY0/smGO -PIbNPmqhlr8bRuJGGlXuftOGLPnssWrZSbQhMzBJQWGiKsHQF+AsdQzaDrtgwuWM -6REkiXd/cs0QXZh9P2ZzzIqKuIwwOazppJ5mdenWx/0X3Ex7PfdiGZPL8f8DEE7/ -lHXLHvTvWag= ------END CERTIFICATE----- diff --git a/test/test_certs/client.key.pem b/test/test_certs/client.key.pem deleted file mode 100644 index 6bcd83661..000000000 --- a/test/test_certs/client.key.pem +++ /dev/null @@ -1,240 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA - Key Security Level: High (4096 bits) - -modulus: - 00:a4:ec:f8:c1:c7:53:88:47:63:3b:5f:76:8f:7f:50 - d4:48:73:2e:af:6a:a9:98:21:a5:65:31:40:74:95:d3 - f1:69:9b:b9:14:8f:fc:2d:2b:5c:64:cb:5d:f4:f8:7d - 9a:6f:e9:3f:4a:9f:23:8d:d6:b4:4d:55:7b:7a:ce:f2 - 5d:84:0c:da:26:04:0c:87:35:8b:7a:02:b7:28:05:90 - 84:85:a7:33:46:e5:21:8b:57:61:80:36:8b:f0:61:f8 - 0f:86:fa:67:1a:8f:62:9c:05:49:95:7b:f6:0c:9a:2e - 2c:1c:6c:2f:77:3c:e9:d0:37:6a:e3:23:70:9f:05:36 - 07:ef:48:9e:d2:a3:bb:c4:76:86:50:a3:54:06:2a:94 - e8:51:45:4f:0f:d7:be:2b:fc:be:8f:85:88:f2:cc:0f - f2:52:5a:67:f1:08:0c:ae:f8:11:20:32:ca:4e:d1:fa - 36:23:22:d1:78:1a:bc:11:ed:13:3b:0b:15:87:38:43 - 70:3b:fd:e9:c5:b8:89:17:ea:16:5c:eb:7e:da:3b:13 - 4c:ac:8b:d4:56:49:1c:8d:9b:78:17:0f:bd:0a:a5:f5 - b6:81:63:fa:b4:92:84:fb:71:2f:0f:26:21:88:fc:3e - 93:a9:74:36:e5:79:1b:4c:6f:e5:76:6c:c9:ec:ae:6d - a0:b2:7e:ee:25:1c:76:df:aa:f2:c1:74:a3:04:b8:85 - 1f:f7:55:45:cc:db:80:e0:05:db:b3:be:68:a1:0f:d1 - b4:93:4c:03:36:68:c7:bb:7b:5e:f2:d1:90:a2:0b:cc - 25:14:d4:b6:2c:35:00:43:b6:0e:25:14:66:a7:7a:a7 - fa:45:1a:b2:5f:47:2c:54:ea:71:c1:be:bd:40:0d:e0 - a5:07:07:10:07:48:47:da:65:43:e7:d6:9f:7d:ec:73 - 06:c5:30:86:8b:a4:de:fd:1f:4e:03:50:a5:7d:bf:a9 - f7:8a:c1:56:e7:57:86:c4:37:57:dd:fb:14:b0:cd:d9 - 92:7a:52:da:ea:b1:3e:a5:81:11:24:e9:7e:cd:d2:d2 - ab:40:2d:64:4d:86:4e:9d:80:eb:48:e4:f8:0b:ab:00 - b7:10:aa:17:4d:53:d6:15:f2:d0:95:84:54:e2:74:ee - 3d:19:7a:93:9a:e0:09:03:3b:ee:48:0a:08:a8:21:6e - 53:54:d6:7a:23:50:08:72:37:72:5c:f1:50:06:5c:a8 - 30:71:4d:95:2f:6e:43:f7:40:ae:ef:d0:86:f9:73:b1 - e2:46:9e:64:98:ee:02:42:e5:20:cb:c9:93:d5:16:61 - 68:ca:7d:a8:8c:b7:1c:a5:0d:10:17:58:c2:18:f8:e7 - 01: - -public exponent: - 01:00:01: - -private exponent: - 44:b8:a9:7e:b5:3b:cd:51:51:bb:ef:af:4b:63:d5:9e - 5f:01:ff:b6:00:4f:e2:a0:42:76:c3:eb:03:a9:5a:c3 - 01:2a:6e:18:6f:56:b8:cb:94:98:3b:55:4f:3a:2b:bc - 2a:5d:9a:8d:d1:79:d3:24:5f:c4:c9:95:c6:3a:6d:2b - 22:56:e8:9f:66:98:81:ce:81:eb:b9:2d:f0:73:41:20 - b7:40:50:51:7e:30:58:0b:75:09:23:b1:73:dc:9e:ac - 79:a5:e5:48:5f:ee:ca:ec:39:19:1c:aa:0d:de:40:d7 - 08:90:db:c6:67:8f:55:bf:81:be:5b:8a:15:f8:e9:e6 - ac:82:2a:0b:c3:45:fe:3b:15:04:8c:c9:fa:37:cc:0c - 71:b0:db:9c:d2:5c:df:9f:55:18:20:a0:4b:eb:53:c9 - b9:1f:0a:a8:98:9e:10:5a:35:68:a1:41:43:4e:a3:5f - e3:8c:22:94:55:2f:80:98:b4:a6:a9:9b:b2:d8:72:e1 - 55:5e:1c:06:d3:39:ec:c9:11:c0:6e:30:51:66:c4:47 - f2:ad:e1:30:83:0e:6e:c3:15:6b:26:97:b2:d4:2c:6a - 7b:c7:d9:33:5c:ca:24:ab:a8:dc:3b:1b:46:25:35:3d - fa:21:fe:ad:e7:a4:c4:58:eb:d8:48:c4:6a:e6:d3:ae - b2:f1:c6:b1:ed:7b:ca:23:fc:51:bc:9f:3a:3e:56:9e - 6d:23:b0:e5:9a:46:ae:f9:ee:8f:1a:5e:cc:42:37:22 - 6e:4b:52:37:0d:36:59:72:1a:19:c1:17:f9:45:55:bd - 33:ce:15:5b:b5:ee:a3:88:a2:b8:46:fe:b8:e2:93:54 - 0a:68:81:a3:b8:63:95:c6:b2:96:d0:f8:ac:d6:5d:df - 97:58:85:6f:eb:2d:66:3b:27:7a:db:2c:7d:8d:04:01 - 44:af:4a:91:2e:e8:f7:6c:e8:52:7a:70:a4:31:9a:f0 - b7:d3:bc:dd:f4:46:d0:61:f9:5f:57:98:c0:79:b9:e1 - 0f:6e:76:33:66:46:82:7a:31:61:a4:88:19:48:15:86 - 0e:b1:40:6a:af:1a:3f:75:db:12:bc:79:c7:f2:fe:41 - 49:02:88:3b:66:33:44:12:9d:43:f4:e6:cf:0d:4d:c5 - 44:d4:a9:4e:0d:fe:dd:00:87:bf:5e:f2:aa:5f:71:cf - f8:4a:24:4c:2c:bf:ee:88:3d:f5:78:e1:5c:95:a7:4c - 82:c8:65:7d:e7:b3:d7:98:b2:f2:4b:66:5c:1c:f7:d5 - 6a:6e:af:a7:e9:98:31:e8:92:99:1d:64:85:fb:8a:f6 - 6c:3b:a1:dc:22:be:fc:f8:8c:3b:51:c4:c0:dc:44:71 - - -prime1: - 00:cb:37:46:9c:11:91:72:90:0e:18:7a:ad:d9:c4:cb - 3d:64:2c:47:9a:2f:04:69:05:d5:22:b4:f0:af:3e:30 - 26:2e:32:35:83:ba:e1:80:3b:5a:5d:1c:c9:ac:c5:85 - f2:87:93:8e:f3:dd:7c:f5:cd:a5:77:8b:c2:bf:47:67 - 4d:c7:df:49:cd:41:bf:6c:14:f8:95:0e:db:00:70:d7 - 3a:43:b8:ae:43:be:a3:ce:48:5d:1b:c5:30:14:34:91 - 2c:6e:c5:00:81:ab:86:4b:9f:f0:ab:45:27:62:e0:ea - 2d:89:61:a2:bd:06:4d:2e:c7:2c:9d:bd:fd:f5:39:b8 - 29:2a:f4:2e:bf:1f:13:9d:c8:d0:bb:56:67:d3:de:2d - 31:63:22:61:5c:30:14:ea:b7:72:a8:f4:ea:d6:db:0b - 35:86:a6:e4:8d:c7:c2:9f:2b:33:85:d7:05:45:f7:05 - d4:5a:74:8f:26:4a:91:b3:7a:cc:8d:57:1b:c2:a8:b8 - e8:b5:ef:b0:24:4d:1d:8d:03:a1:79:0b:d4:0b:06:1a - 62:ca:5c:b8:aa:f9:ff:f4:cb:54:e1:6e:a5:d7:41:46 - 7c:2d:36:4b:15:01:c9:17:45:3c:33:44:79:4c:99:b0 - 43:0d:f7:c0:05:13:e5:82:3d:53:a3:2b:88:d9:ee:3b - 83: - -prime2: - 00:cf:c3:9b:a6:b3:b2:65:0b:88:8c:64:db:1e:00:21 - 42:a5:bd:47:75:0d:a4:29:e5:e9:9b:b7:1a:cf:df:9f - a3:44:9c:67:a4:a9:0c:87:97:94:94:40:54:44:3b:bf - 1a:f3:71:97:05:d6:a5:ad:28:f5:a9:94:92:f5:7f:f8 - cd:82:40:6e:ca:ff:d8:3f:18:84:42:83:dd:9e:87:bf - fa:2e:96:5e:fc:1d:db:d4:d5:dd:2d:c3:71:f4:bf:54 - 6d:06:21:95:8f:0a:89:b8:14:62:7c:a7:9b:10:fc:ae - 4b:0c:51:ac:46:5a:ee:04:cc:e2:ad:fc:97:50:32:b9 - c7:1e:40:6f:07:de:45:7f:2e:ac:12:39:30:b8:7b:6d - 0f:07:75:bd:58:c2:e2:e7:52:2a:6f:7e:8a:2f:07:c5 - eb:10:db:8d:10:2e:d4:a2:1c:28:ca:f5:c4:de:56:6a - 20:f3:47:21:b7:4c:b6:04:93:86:e6:78:b5:89:64:cb - 9e:f2:cd:78:e7:43:e6:a0:94:41:b4:c5:a8:c7:43:2b - 20:17:50:7a:a9:c0:ee:db:9a:f0:48:85:1f:1f:64:11 - 4e:4d:dd:99:6f:49:7a:f2:65:2a:83:98:96:5b:a3:7e - 64:da:77:00:7e:4b:7a:55:d9:cf:d7:73:fb:63:1a:f8 - 2b: - -coefficient: - 69:74:f7:c6:34:ec:d8:f9:08:c2:d8:5b:6a:61:4e:33 - b3:9b:dd:f1:30:8d:2e:bf:12:2d:74:de:53:6c:d2:88 - e5:9f:5d:5f:f1:e3:59:00:c4:1d:e3:5a:1d:07:6a:53 - ea:c8:46:0e:b1:61:5d:03:27:d9:1a:67:54:8d:bb:85 - 53:07:39:19:56:8a:dd:9f:f4:30:13:fa:2e:ce:1c:8e - 1c:cb:d6:54:b7:a9:92:26:87:f6:60:c1:04:41:fc:7c - f7:fd:fb:a7:fc:f2:d0:2c:4a:65:ef:62:5b:f9:b4:d6 - e8:f6:68:57:1f:b2:d1:d6:c4:78:0e:d4:66:39:cd:4c - 42:d7:5b:d1:ce:ae:5a:b6:25:12:7f:d1:2c:ae:95:4c - 14:cb:0a:31:7c:a8:0a:71:b8:98:c8:2b:03:19:92:d0 - e1:4c:a8:c4:0b:8c:b9:8b:d0:9a:b3:8f:27:b3:a4:09 - 8b:6d:95:e3:12:a0:60:e6:e7:b9:8f:53:fa:b4:56:f1 - cd:fe:11:31:20:01:fd:82:73:f1:51:ba:13:71:f1:0f - 8b:78:3f:36:71:7e:15:93:35:01:06:8b:40:a3:b5:23 - 22:9d:c7:c7:88:c8:25:07:dd:0d:1d:70:43:37:3e:ea - 25:b0:54:f0:5d:83:29:ca:5a:ec:4e:f5:a7:66:f0:fa - - -exp1: - 4a:04:cd:3c:45:8d:e3:db:a2:b6:b9:e0:9f:04:76:3e - db:40:e1:a7:c0:5e:6b:de:8a:fe:84:47:72:9d:45:2e - 72:ff:28:cc:dd:82:0b:92:12:dc:fd:82:5b:e2:ea:62 - 27:8d:d0:b0:f4:c8:f2:43:40:74:e5:bc:3e:ad:c4:6b - e9:54:64:6e:55:f7:62:67:d5:0f:7e:04:b9:09:60:eb - c1:05:00:bc:7e:30:ee:0f:1f:92:e0:e5:1d:46:f4:65 - e9:c6:e9:e3:51:55:ae:30:08:9a:69:aa:e9:f2:20:7a - 0b:a3:3b:82:7c:4c:1a:b0:c3:88:85:4e:7e:46:d2:d4 - 73:e7:d3:2b:1c:27:a9:fe:1e:41:4e:3c:ad:48:2c:cf - e3:5a:ff:79:73:ad:fa:bc:6d:10:2b:7d:6a:5b:08:9f - 2b:77:98:a2:27:d3:b4:e4:28:75:24:97:b0:1f:44:c9 - 4f:55:4b:5a:d8:28:6f:e6:57:a1:57:cc:2d:c0:04:f2 - 06:6a:d2:8e:b6:64:00:1c:05:71:b0:a4:40:8b:ad:8a - b4:48:c7:9e:c7:46:ba:a4:61:3b:67:71:12:91:9d:19 - d7:e2:01:c1:1a:10:63:e0:7d:07:f3:75:f7:37:b7:a3 - 04:f0:6b:c9:ad:b0:98:1a:bc:5f:1f:99:4e:3f:de:ff - - -exp2: - 0d:a2:8b:bb:83:fd:88:2e:1a:97:04:23:71:33:96:fb - 35:bf:57:4a:32:4b:fc:c7:ee:ed:de:35:6f:41:00:cc - 09:3b:ae:7d:9a:ee:8c:93:81:17:bd:a5:0a:19:55:b0 - 62:1b:a9:4a:a3:cc:99:b1:9f:75:b2:9f:76:67:20:9f - f4:15:60:70:08:1c:5b:ff:b2:e6:5e:9b:13:c5:5a:ef - 03:51:b1:08:20:b9:85:9d:47:77:b2:64:ef:28:03:55 - 68:5a:99:e3:1a:50:f1:78:bd:01:eb:49:fc:f2:68:49 - da:94:1d:97:3c:6e:74:78:31:c4:33:58:86:d5:dd:65 - 58:f1:e7:97:7f:99:d5:ff:ed:21:01:09:d6:81:9b:25 - aa:5a:aa:c3:81:7e:bc:a9:a2:c9:50:67:a7:30:7e:67 - af:e2:88:be:70:24:5a:43:38:d6:21:0c:fb:7e:76:56 - 95:40:ac:d0:c7:c3:06:47:dc:49:91:d0:70:24:e2:4c - 1b:29:2a:ef:1a:80:af:37:2b:9c:be:80:16:1b:ad:5f - dc:c7:d6:54:ff:a9:6d:56:1c:c0:d5:a3:b6:3e:ad:f8 - 12:9a:21:70:b1:44:d5:55:98:55:ac:94:e9:8c:b0:45 - d4:24:8d:2e:bc:ab:59:a9:02:bf:e4:07:b2:78:59:a3 - - - -Public Key ID: - sha256:99:0C:C5:EF:0C:3C:DC:71:32:9D:02:23:F8:28:E8:3B:55:D8:00:3D:D0:12:F3:56:33:03:B1:FD:30:3B:71:F1 - sha1:37:0D:50:BB:93:90:C0:E5:78:D6:E1:D0:FE:99:64:6D:84:5E:3C:36 -Public key's random art: -+--[ RSA 4096]----+ -| ...+oo o | -| .+ *.o. E | -| . *.=. = o | -| o ..=+ o | -| S *+.+ | -| . o+ | -| | -| | -| | -+-----------------+ - ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/test/test_certs/server.cert.pem b/test/test_certs/server.cert.pem deleted file mode 100644 index 32f9d38dd..000000000 --- a/test/test_certs/server.cert.pem +++ /dev/null @@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFpDCCA4ygAwIBAgIMWId0/ykDlohr/6M4MA0GCSqGSIb3DQEBCwUAMHQxFTAT -BgNVBAMTDGxvY2FsaG9zdCBDQTEeMBwGA1UECgwVR8O2dGVib3JnIEJpdCBGYWN0 -b3J5MRIwEAYDVQQHDAlHw7Z0ZWJvcmcxGjAYBgNVBAgMEVbDpHN0cmEgR8O2dGFs -YW5kMQswCQYDVQQGEwJTRTAeFw0xNzAxMjQxNTM4MzlaFw0xODAxMjQxNTM4Mzla -MDQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGA1UECgwVR8O2dGVib3JnIEJpdCBG -YWN0b3J5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzhorPKY3cEsy -vbKPvLn6aA3RArbRAgDMR+a4knyMWpQ8Nhb+xgLG/CUAO/fHhuyltw6vg7dx3+X+ -5jXMc7dfZf2YNGZTAn7jAlpTYPJMMO5m/VCOaiDdymU41YHYuV64pmKCrDv5EeHE -Fi7A2LuCXfsn+jIV6iPs6ASbgCavVs8yVp2Sb4qrrXVlgBuOv5ety369mNi91Yot -wZ8jbI1qAAe/nujrJwshg1qSTGTTseaZ0aAgimuoE+BsMB59VL9fVUEBH+dn7Qp/ -ff/AQpwjJK7hrCL9177bLMp23t6uyGkZlqEqlJ1+1ZTP4fl9ZOPmGhZE6laohcLx -kI0VThH8jmoOp7nQWWkjzAqj28qSYfbXd975pw3ZdztqTt3vgVmvWLWJfOfXcJ4+ -+OUu9Boocsirxx53p4ONjz+a5BQ7RI0dDEx0yKmrzDTmuBiLqeZPxQBwtYRBIw0g -sGCIp8jYGdlzBg39cETQ48BA0jwKdDO819+ZCh4ZlE707clz/GMR2v3DiGQTOac8 -vlagpmsWsmzNtGjLEZKKAlppx/OMoOKEi0LGA7Nos+Du6RzKYKEqkGwLW2gxj43A -FPjr9Pvr+sbUectDLZz3DvwXkrsBvz7ySObS+1LZWMgt5mvT+NSx5oHSaok4g/fZ -fBf2jHABuIHTkGiTlKaO3NjAwj3nEZkCAwEAAaN2MHQwDAYDVR0TAQH/BAIwADAT -BgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHQ8BAf8EBQMDB6AAMB0GA1UdDgQWBBQS -NmTiVjC6QIA8chtyQhUb4Se1wzAfBgNVHSMEGDAWgBSLOwbxgKwVtYYG1FO8XU1n -8CQL4DANBgkqhkiG9w0BAQsFAAOCAgEAj5V6MSHkRWAWUaEzMxVuyYMHhVqZenM2 -KBhaoM3Y9zbd5RHpYaypAKqr2joLGnNgIi9jsnCOOc+fdwh0lFd+faW5DvbPpIH/ -n1DlmnFv5mJ4uKuaRKhSY5NjPsL304riZKGjoi7fIuZgOv2lqXkvwB7e1b0/T4e4 -e9BjXuXLBkWSpPVo6TwdzJsQTRdr82uha+HxWs464Bo/Z/kgLaWTOQE3zpDXd5Yj -oC4sMlbIHb86hDQJrJwLmSpy5J2hIKyoQDhDRagxzULkDgrHUEhC4Ri9e2hgeZbX -/Na3AJw6EBnHP58yI6JpWX40B3HBGSJghzEUX+fijXbKPKLBjSD4rAj4kYNDkQim -SaRzU1MfFYp8MXMHTQw9cVH9wiLt1/HHWSfVZK5oTxjXFBnmV5INvMoPbHFrydy0 -a9aiOb9usJwOjwgBwnQFruvsEOl5xltBWHSGRC4iA28o8jlOPzR0XTijsUczrO08 -s10rTzuFNFymumGcMCoHWVFtQdK3Z3TSC1mvPeh5o/pHFXAO25YZjbMcx/cWcZq0 -Fg5CdQpkOZB+UJE7QBdN+xg++AxPsHihSp3RJTgZaTgaPL7eQXNCDHp3l+5QTWTX -39wPkU672ANEjG2+P2VfACVZQFmDXA3MNkLdPmGkJ78sohfkAP+qZEm6wAX7id9C -3glO5S3cYm4= ------END CERTIFICATE----- diff --git a/test/test_certs/server.crl.pem b/test/test_certs/server.crl.pem deleted file mode 100644 index c77e221b2..000000000 --- a/test/test_certs/server.crl.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN X509 CRL----- -MIIC+zCB5AIBATANBgkqhkiG9w0BAQsFADB0MRUwEwYDVQQDEwxsb2NhbGhvc3Qg -Q0ExHjAcBgNVBAoMFUfDtnRlYm9yZyBCaXQgRmFjdG9yeTESMBAGA1UEBwwJR8O2 -dGVib3JnMRowGAYDVQQIDBFWw6RzdHJhIEfDtnRhbGFuZDELMAkGA1UEBhMCU0UX -DTE3MDEyNDE1MzgzOVoXDTE4MDEyNDE1MzgzOVowAKA6MDgwHwYDVR0jBBgwFoAU -izsG8YCsFbWGBtRTvF1NZ/AkC+AwFQYDVR0UBA4CDFiHdP8sH+YAO+X4pTANBgkq -hkiG9w0BAQsFAAOCAgEAhbN7sB2e4R/K0E1drwX1O5Uz9B2XiEt8aHO+LKcuGbsp -u6m+M0QwkxBPVdcG2bdpoN0Q5YZr/azRiiZz/FtmoxkFiLziP1nLm2uJ68QTrzk7 -61vqZcM+oOKhjPMkf2YORHBFbwVOm/JDobDFrHjbE7gCMQD93L1TW1mlYdoujSV9 -qpUHZkfwJ7tlaNtMMSaGrtRh8PCl+TpzMvTpG30LPZY/VHlHxrb3RQL05ReCoHsn -r2OHvzEh3E4/YjKjXKIVVFvZh4K6ljdPWgOeizGcp+LxHySe/HKgVr86ia2xKEqw -tbSGD1okRrDrRu0rmRhAYgLh8BoKeJXROOKcyogDc3ViMNGJwVBE62ZajL/HcjPs -NSSeaNoYt+njRUpOWF2RFj4pLDZQc2VNWTDiJR+ljvlBlqdZ/Raia9J76tU1MuAs -wDTTTZpOA7URMqgR0gUigOGB2kSsWthscTfc3viL7WXBuh7a+3lURwQf9t3TnZLH -HTNQNBgQS84Gyt8y8hOx5dlnxBoRr1+BZT90VlqE9Ccllrins74uK/pGVNyIvKNg -Fk6Wfo9iCanP4Usdg1qj8mVAye8Zga/q/6C9/mpQ9rSc6z/vcoQ0Fehd03msTHpz -++7djv8LKLsCd5TyCFsSCgAFxZL0qR6cp7S8K6kH7XH216TAag5Q+Zdl2j26law= ------END X509 CRL----- diff --git a/test/test_certs/server.key.pem b/test/test_certs/server.key.pem deleted file mode 100644 index e7688538f..000000000 --- a/test/test_certs/server.key.pem +++ /dev/null @@ -1,240 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA - Key Security Level: High (4096 bits) - -modulus: - 00:ce:1a:2b:3c:a6:37:70:4b:32:bd:b2:8f:bc:b9:fa - 68:0d:d1:02:b6:d1:02:00:cc:47:e6:b8:92:7c:8c:5a - 94:3c:36:16:fe:c6:02:c6:fc:25:00:3b:f7:c7:86:ec - a5:b7:0e:af:83:b7:71:df:e5:fe:e6:35:cc:73:b7:5f - 65:fd:98:34:66:53:02:7e:e3:02:5a:53:60:f2:4c:30 - ee:66:fd:50:8e:6a:20:dd:ca:65:38:d5:81:d8:b9:5e - b8:a6:62:82:ac:3b:f9:11:e1:c4:16:2e:c0:d8:bb:82 - 5d:fb:27:fa:32:15:ea:23:ec:e8:04:9b:80:26:af:56 - cf:32:56:9d:92:6f:8a:ab:ad:75:65:80:1b:8e:bf:97 - ad:cb:7e:bd:98:d8:bd:d5:8a:2d:c1:9f:23:6c:8d:6a - 00:07:bf:9e:e8:eb:27:0b:21:83:5a:92:4c:64:d3:b1 - e6:99:d1:a0:20:8a:6b:a8:13:e0:6c:30:1e:7d:54:bf - 5f:55:41:01:1f:e7:67:ed:0a:7f:7d:ff:c0:42:9c:23 - 24:ae:e1:ac:22:fd:d7:be:db:2c:ca:76:de:de:ae:c8 - 69:19:96:a1:2a:94:9d:7e:d5:94:cf:e1:f9:7d:64:e3 - e6:1a:16:44:ea:56:a8:85:c2:f1:90:8d:15:4e:11:fc - 8e:6a:0e:a7:b9:d0:59:69:23:cc:0a:a3:db:ca:92:61 - f6:d7:77:de:f9:a7:0d:d9:77:3b:6a:4e:dd:ef:81:59 - af:58:b5:89:7c:e7:d7:70:9e:3e:f8:e5:2e:f4:1a:28 - 72:c8:ab:c7:1e:77:a7:83:8d:8f:3f:9a:e4:14:3b:44 - 8d:1d:0c:4c:74:c8:a9:ab:cc:34:e6:b8:18:8b:a9:e6 - 4f:c5:00:70:b5:84:41:23:0d:20:b0:60:88:a7:c8:d8 - 19:d9:73:06:0d:fd:70:44:d0:e3:c0:40:d2:3c:0a:74 - 33:bc:d7:df:99:0a:1e:19:94:4e:f4:ed:c9:73:fc:63 - 11:da:fd:c3:88:64:13:39:a7:3c:be:56:a0:a6:6b:16 - b2:6c:cd:b4:68:cb:11:92:8a:02:5a:69:c7:f3:8c:a0 - e2:84:8b:42:c6:03:b3:68:b3:e0:ee:e9:1c:ca:60:a1 - 2a:90:6c:0b:5b:68:31:8f:8d:c0:14:f8:eb:f4:fb:eb - fa:c6:d4:79:cb:43:2d:9c:f7:0e:fc:17:92:bb:01:bf - 3e:f2:48:e6:d2:fb:52:d9:58:c8:2d:e6:6b:d3:f8:d4 - b1:e6:81:d2:6a:89:38:83:f7:d9:7c:17:f6:8c:70:01 - b8:81:d3:90:68:93:94:a6:8e:dc:d8:c0:c2:3d:e7:11 - 99: - -public exponent: - 01:00:01: - -private exponent: - 35:c7:94:52:ae:18:b7:3f:98:0c:f1:e4:19:dd:f9:9e - 5f:44:93:cd:ba:7a:28:55:25:c9:e9:8d:a7:dc:43:e2 - 7c:57:ac:e6:6c:2a:db:5b:7e:18:32:8d:b4:4d:db:c0 - 5e:25:01:dd:7f:e7:c2:01:d4:e2:a3:55:27:38:c9:36 - e6:d6:69:8d:ed:c9:2f:ec:f2:77:39:5c:89:bd:21:e3 - 83:92:44:0b:f5:5b:80:24:49:07:6c:87:06:53:50:89 - 24:39:7e:59:8d:08:b0:06:d6:50:e7:80:40:cd:a4:f5 - fb:0c:72:77:d1:4a:18:77:c0:0f:3b:b8:53:df:da:ea - 13:fb:90:00:5f:04:b3:49:7e:e1:ae:6d:02:71:b2:15 - 92:8d:0e:d2:2c:74:54:9d:4a:d5:44:3e:4d:1c:15:75 - ce:8f:cc:da:80:49:de:d8:ae:da:da:63:fe:c7:52:9b - 96:b5:a1:6f:fc:4e:9d:3c:1a:8a:80:7f:e4:3a:51:f9 - 81:58:85:ff:9a:35:7f:20:07:26:8c:ae:d1:72:c6:a4 - d2:d1:66:46:77:30:6d:c7:e9:47:27:7f:7e:61:e3:3f - b1:82:a1:a9:67:c0:35:11:6c:54:56:65:f0:b7:7b:d4 - 56:28:4b:71:4e:8d:88:e1:d6:80:de:9f:99:90:8d:f2 - ef:07:ae:db:65:fb:70:90:89:22:c6:de:6a:06:83:01 - 16:51:a0:bb:3b:d2:ed:98:cc:62:24:df:37:e0:98:06 - b3:88:1d:b9:c6:1c:d5:a7:18:e5:b7:3b:34:0f:63:90 - f1:ab:0b:3e:e6:b4:9a:54:69:41:f6:c5:ad:03:44:b2 - 48:48:66:55:a8:4b:0e:bb:43:48:53:a7:07:23:a6:06 - 64:46:b5:e3:9d:9b:b2:de:35:67:cd:e6:e1:39:0f:0d - 8b:c5:8b:68:bb:38:86:b7:a0:81:f4:b0:89:fa:6d:2d - 56:80:77:66:6d:32:02:1f:93:82:47:01:37:35:95:6d - 55:87:9c:75:cd:9a:f9:16:f4:09:3b:a8:60:47:5e:34 - 7f:6b:58:99:c8:db:fb:60:f5:dd:54:9c:27:42:5b:a7 - 4e:27:28:92:af:c6:76:56:c5:0f:db:73:1c:e0:06:bd - 4a:4f:75:37:19:39:9a:a7:7f:f2:8c:18:a4:25:77:c4 - e9:4a:3a:9d:3a:83:67:2b:bf:bd:c5:4d:bb:37:68:8e - 6f:a9:0d:67:d4:87:e9:59:02:61:60:e1:3f:b0:81:81 - 89:ba:29:4a:f0:37:4b:42:95:90:99:fd:7d:a9:30:31 - 33:ba:dc:86:6b:ce:29:98:8b:37:f2:72:b1:90:79:a5 - - -prime1: - 00:fc:59:48:15:f7:ed:1a:7d:0b:ba:2d:4e:b2:bd:75 - 10:a4:f3:68:2b:db:18:6a:b2:ff:93:c8:0d:c4:4c:55 - 99:58:02:ba:60:43:81:ee:09:14:e5:5a:38:91:37:6d - f6:b4:4b:b3:5e:be:9e:3a:43:27:a7:06:06:a1:ae:dd - 36:2f:8c:b1:17:1d:b5:55:bc:1a:05:cc:35:d2:03:25 - 18:97:f7:84:a4:2b:18:f9:50:67:ee:3f:c9:8c:d1:35 - 95:0e:9d:28:0b:f5:9d:00:cd:e5:e9:43:3f:8a:90:59 - c1:ae:a2:74:60:2b:c8:9c:50:9e:f8:5f:f2:87:13:36 - dd:8e:b3:d9:52:4c:26:40:7f:b6:52:81:9a:29:90:af - 85:ba:4a:9c:e6:38:4e:7d:9e:05:2e:e4:af:b1:a4:51 - 5d:fe:1f:ce:aa:b2:e2:b8:74:9f:ac:86:c7:ab:a9:5e - 5b:8a:33:49:c6:14:f1:10:0d:d6:02:55:6a:6a:57:92 - 5c:21:b3:c8:cf:b2:49:78:cb:23:47:d3:f5:5a:c9:ad - 2c:4d:18:9d:c4:2b:84:e0:91:d9:8b:99:ce:cd:02:cb - b0:c2:db:02:f5:23:a2:e7:d4:93:ed:40:72:eb:29:4b - 71:85:46:03:d3:f2:da:d3:f1:3f:89:b0:cf:7a:29:2b - 5f: - -prime2: - 00:d1:15:96:34:3b:7b:31:af:5d:07:83:c8:1b:20:50 - 33:51:3d:6a:8f:83:80:86:cc:1b:b2:d1:d2:63:58:d3 - 4b:6f:d6:6e:fa:18:06:f3:9f:0d:1c:c6:fe:2e:64:72 - b6:cd:8d:8e:93:e0:71:c6:8b:20:2b:49:32:99:70:e8 - 89:51:11:63:ad:53:ff:a7:2e:0f:3c:f8:e3:a0:63:16 - a2:12:44:65:1f:d4:e8:81:03:54:9f:e3:1e:c6:a8:68 - fa:fb:70:f7:ad:59:f9:e3:8e:f4:e4:e1:2e:c6:35:f5 - 43:8c:83:34:2d:f2:4f:ee:43:68:95:16:e9:51:f9:bc - 1c:6b:f3:6b:46:83:b2:7d:90:6a:cc:8a:cb:a8:31:dd - 64:cf:60:58:af:dd:82:9a:55:95:36:fa:cf:89:24:38 - ad:bd:75:02:28:83:8f:95:2d:95:9e:c5:44:d7:c5:80 - cb:53:74:82:12:9d:d6:1b:df:9f:6e:a8:6d:a4:99:1f - b9:b9:83:bb:c8:b9:f1:b1:9a:d1:a6:16:43:71:a8:27 - 4d:6d:c3:46:10:cd:be:1b:03:af:1c:67:9c:13:58:47 - 93:f2:07:eb:73:65:f0:b1:cd:10:11:5b:20:c6:2a:b4 - 9f:f0:52:53:0f:e2:8a:2a:e5:c1:d3:f8:4d:1f:66:5e - 07: - -coefficient: - 0b:81:01:5e:44:50:b1:e2:3f:99:45:3a:6f:97:ae:f9 - b3:e9:fd:bd:d1:3e:f0:82:30:69:d7:99:89:fa:75:e8 - e9:64:f2:76:6e:72:3d:81:c1:bf:5a:21:63:06:71:e3 - 28:92:bd:58:58:35:b5:d3:65:5a:79:89:01:bd:09:70 - 2d:b8:89:c3:76:25:f0:41:d7:72:08:0b:59:e8:8f:4e - 22:fd:e9:97:a3:4a:13:40:de:1c:0f:fc:3f:87:a0:4f - ba:6e:41:cb:e7:df:04:67:90:14:3f:dd:18:33:01:6d - 3b:a8:ae:7c:5a:e2:23:e1:9c:2b:a0:d6:8f:d1:8b:8c - c8:ca:39:17:11:82:65:d3:a2:c0:05:d3:bd:c5:b0:17 - ea:62:a5:1a:fe:3e:c8:f0:fb:c6:5e:6b:60:50:a7:34 - f1:ff:a9:70:79:16:ce:9e:c0:15:34:b3:cc:e6:22:1b - 02:bb:b5:59:2a:b1:b8:82:f5:4d:b1:6b:a1:27:a9:1d - 4a:22:44:98:08:c3:7e:34:7a:01:56:27:29:20:80:10 - 27:10:b8:d1:ab:d1:d6:5f:69:80:68:27:74:96:41:7f - c1:91:3a:b1:25:c0:39:e9:58:f6:4a:36:3f:73:c9:ab - 39:7b:02:72:76:7a:2f:74:5a:63:93:b0:b2:52:d8:2c - - -exp1: - 00:af:90:01:f9:de:ba:23:b2:99:a5:16:7e:69:16:2c - 4a:bf:27:e4:f2:96:04:7f:bf:36:d6:7e:d9:2d:17:9a - 7a:0b:e4:21:fc:75:1b:01:1b:6a:61:42:8a:96:65:44 - e8:dd:78:c9:3b:02:4d:1c:e5:b9:c1:97:0c:a8:11:fb - 2f:06:97:d0:60:ef:b7:48:05:8c:e9:39:b0:bc:02:9a - 1d:69:24:b8:30:6b:17:7d:e4:b5:d9:e8:a0:f5:8a:5b - c7:ef:19:e1:51:a8:b8:69:65:d6:2d:9a:2e:ab:dd:4f - c4:d3:15:8c:f7:97:9e:83:3a:07:cf:6f:19:51:66:49 - c6:8c:d0:8d:42:97:5e:09:83:90:ba:08:16:d4:12:28 - 3b:56:67:30:8f:6e:df:14:c0:0a:85:1c:6f:2b:9c:d1 - 4b:1e:50:cc:bc:af:a5:d2:84:b8:ce:14:1a:f8:4f:e6 - 28:b3:96:89:1d:f9:55:d9:40:77:02:ba:a0:45:89:d5 - 76:a9:af:e5:e6:b1:f8:31:c4:ca:2f:df:c4:14:3c:b9 - 71:57:d5:e7:75:22:7d:d8:ab:3f:f9:c2:b1:40:aa:50 - 42:12:de:c2:49:00:59:07:07:3b:3f:55:96:5e:0b:25 - c4:bd:de:ff:2c:c7:09:fd:68:1b:37:55:3e:93:93:ee - eb: - -exp2: - 1d:39:2f:2d:4d:c3:02:46:c8:71:ec:71:63:99:38:9c - 73:96:69:f6:75:22:d9:b4:5d:8a:b8:d8:f7:19:cb:2e - 98:0b:18:a7:cc:03:ec:b9:26:54:07:5c:2f:8f:ca:98 - d2:52:1e:c3:7c:73:6a:94:b7:82:55:50:c5:31:8a:ce - e6:8d:cf:a8:c2:3c:d1:59:16:b8:26:f1:69:d1:6f:b0 - 67:6d:37:d5:23:24:fc:23:43:08:b5:ed:ef:46:4a:a6 - 09:ce:d8:4f:5a:6c:1a:ea:38:40:65:58:ec:4b:4e:64 - 8a:97:2c:0b:df:fa:5c:0a:ff:eb:16:b0:b5:cc:7a:06 - 5e:f0:3e:e6:34:32:16:6a:c1:02:a5:c3:a1:56:96:e3 - 28:39:07:2c:4d:81:9b:a0:aa:a2:6c:9b:e5:47:32:c4 - d5:de:2c:d7:0c:cc:5c:c5:6a:4c:2e:b6:58:92:3a:56 - 30:eb:ef:e2:3a:3d:5a:d7:fe:a8:b0:d1:e2:57:a4:9f - 88:d6:68:c0:bd:7c:92:9e:58:ff:25:cd:77:6d:51:50 - f9:2e:79:f4:8f:4d:2a:e9:70:e8:3f:73:09:21:25:a0 - 00:0c:2b:54:8d:c2:fe:96:f2:cf:fa:34:b2:7d:f5:91 - 05:3d:fc:6e:13:f3:cb:db:21:24:47:68:40:23:46:b1 - - - -Public Key ID: - sha256:01:06:99:B3:BD:7B:A9:83:6F:D4:D0:AF:49:52:0D:19:23:66:EC:EB:7E:8C:A7:E0:4A:C7:DC:A1:C0:98:9B:76 - sha1:12:36:64:E2:56:30:BA:40:80:3C:72:1B:72:42:15:1B:E1:27:B5:C3 -Public key's random art: -+--[ RSA 4096]----+ -|*o.=*o+ | -|*o=o=*. | -|o=o*oE+ | -| ..oo..o | -| . . S | -| . | -| | -| | -| | -+-----------------+ - ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/test/test_certs/test_client.cert.pem b/test/test_certs/test_client.cert.pem deleted file mode 100644 index 27838d74f..000000000 --- a/test/test_certs/test_client.cert.pem +++ /dev/null @@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFpDCCA4ygAwIBAgIMWId1Agdxxug/oIa6MA0GCSqGSIb3DQEBCwUAMHQxFTAT -BgNVBAMTDGxvY2FsaG9zdCBDQTEeMBwGA1UECgwVR8O2dGVib3JnIEJpdCBGYWN0 -b3J5MRIwEAYDVQQHDAlHw7Z0ZWJvcmcxGjAYBgNVBAgMEVbDpHN0cmEgR8O2dGFs -YW5kMQswCQYDVQQGEwJTRTAeFw0xNzAxMjQxNTM4NDJaFw0xODAxMjQxNTM4NDJa -MDQxEjAQBgNVBAMTCWxvY2FsaG9zdDEeMBwGA1UECgwVR8O2dGVib3JnIEJpdCBG -YWN0b3J5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApOz4wcdTiEdj -O192j39Q1EhzLq9qqZghpWUxQHSV0/Fpm7kUj/wtK1xky130+H2ab+k/Sp8jjda0 -TVV7es7yXYQM2iYEDIc1i3oCtygFkISFpzNG5SGLV2GANovwYfgPhvpnGo9inAVJ -lXv2DJouLBxsL3c86dA3auMjcJ8FNgfvSJ7So7vEdoZQo1QGKpToUUVPD9e+K/y+ -j4WI8swP8lJaZ/EIDK74ESAyyk7R+jYjItF4GrwR7RM7CxWHOENwO/3pxbiJF+oW -XOt+2jsTTKyL1FZJHI2beBcPvQql9baBY/q0koT7cS8PJiGI/D6TqXQ25XkbTG/l -dmzJ7K5toLJ+7iUcdt+q8sF0owS4hR/3VUXM24DgBduzvmihD9G0k0wDNmjHu3te -8tGQogvMJRTUtiw1AEO2DiUUZqd6p/pFGrJfRyxU6nHBvr1ADeClBwcQB0hH2mVD -59affexzBsUwhouk3v0fTgNQpX2/qfeKwVbnV4bEN1fd+xSwzdmSelLa6rE+pYER -JOl+zdLSq0AtZE2GTp2A60jk+AurALcQqhdNU9YV8tCVhFTidO49GXqTmuAJAzvu -SAoIqCFuU1TWeiNQCHI3clzxUAZcqDBxTZUvbkP3QK7v0Ib5c7HiRp5kmO4CQuUg -y8mT1RZhaMp9qIy3HKUNEBdYwhj45wECAwEAAaN2MHQwDAYDVR0TAQH/BAIwADAT -BgNVHSUEDDAKBggrBgEFBQcDAjAPBgNVHQ8BAf8EBQMDB6AAMB0GA1UdDgQWBBQ3 -DVC7k5DA5XjW4dD+mWRthF48NjAfBgNVHSMEGDAWgBSLOwbxgKwVtYYG1FO8XU1n -8CQL4DANBgkqhkiG9w0BAQsFAAOCAgEASw6rojEtxVNLmqhuCGRXpBkeJcEC3hgd -usOmw3QFwMRwG0JkQuDTZobs20bWCSJzCnGcou6mvdOhjtaErZ/KUA2uAr9YvlmO -+F7mpXlGjfETm3Ta8JzvTtqgCPjQmz+Wn7nnFmwl9MLOOh//NXQ1KJd/ixWtpaLG -GhY3zduNczLT7O+qganFneostaZFpa/BL3PVXoI669zp7GmzJYSjuxQIyAMP/hKJ -aFfTNIv9WhPzXrzEviLspJ7OwfWW/S46WZWcXzauXiYS5y21rfFSBSAo1J3oRV95 -qi2IMM4PVpwQISMPwdJe3f2+Gc6qn3TaHFrnSv8OuRNOdvuqV9MLn9uxovgTTyw4 -j1sb7kO6OoLCqrbdZ+lTHy48jahpV3q+Vcep8WA+fC42sV/lfMuuS3EaQTJIfu89 -+R+pFQPco60risM8Gy/k0QkwxVwoxsq5xM+SaGyHpEazSasX/qJBj+VH97a9rlje -fzPpmApWRtZNMNx4qSx7ZEFVtSynM7vDWsWsB9x+OYdObzl4bbZUZ3MSCY0/smGO -PIbNPmqhlr8bRuJGGlXuftOGLPnssWrZSbQhMzBJQWGiKsHQF+AsdQzaDrtgwuWM -6REkiXd/cs0QXZh9P2ZzzIqKuIwwOazppJ5mdenWx/0X3Ex7PfdiGZPL8f8DEE7/ -lHXLHvTvWag= ------END CERTIFICATE----- diff --git a/test/test_certs/test_client.key.pem b/test/test_certs/test_client.key.pem deleted file mode 100644 index 6bcd83661..000000000 --- a/test/test_certs/test_client.key.pem +++ /dev/null @@ -1,240 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA - Key Security Level: High (4096 bits) - -modulus: - 00:a4:ec:f8:c1:c7:53:88:47:63:3b:5f:76:8f:7f:50 - d4:48:73:2e:af:6a:a9:98:21:a5:65:31:40:74:95:d3 - f1:69:9b:b9:14:8f:fc:2d:2b:5c:64:cb:5d:f4:f8:7d - 9a:6f:e9:3f:4a:9f:23:8d:d6:b4:4d:55:7b:7a:ce:f2 - 5d:84:0c:da:26:04:0c:87:35:8b:7a:02:b7:28:05:90 - 84:85:a7:33:46:e5:21:8b:57:61:80:36:8b:f0:61:f8 - 0f:86:fa:67:1a:8f:62:9c:05:49:95:7b:f6:0c:9a:2e - 2c:1c:6c:2f:77:3c:e9:d0:37:6a:e3:23:70:9f:05:36 - 07:ef:48:9e:d2:a3:bb:c4:76:86:50:a3:54:06:2a:94 - e8:51:45:4f:0f:d7:be:2b:fc:be:8f:85:88:f2:cc:0f - f2:52:5a:67:f1:08:0c:ae:f8:11:20:32:ca:4e:d1:fa - 36:23:22:d1:78:1a:bc:11:ed:13:3b:0b:15:87:38:43 - 70:3b:fd:e9:c5:b8:89:17:ea:16:5c:eb:7e:da:3b:13 - 4c:ac:8b:d4:56:49:1c:8d:9b:78:17:0f:bd:0a:a5:f5 - b6:81:63:fa:b4:92:84:fb:71:2f:0f:26:21:88:fc:3e - 93:a9:74:36:e5:79:1b:4c:6f:e5:76:6c:c9:ec:ae:6d - a0:b2:7e:ee:25:1c:76:df:aa:f2:c1:74:a3:04:b8:85 - 1f:f7:55:45:cc:db:80:e0:05:db:b3:be:68:a1:0f:d1 - b4:93:4c:03:36:68:c7:bb:7b:5e:f2:d1:90:a2:0b:cc - 25:14:d4:b6:2c:35:00:43:b6:0e:25:14:66:a7:7a:a7 - fa:45:1a:b2:5f:47:2c:54:ea:71:c1:be:bd:40:0d:e0 - a5:07:07:10:07:48:47:da:65:43:e7:d6:9f:7d:ec:73 - 06:c5:30:86:8b:a4:de:fd:1f:4e:03:50:a5:7d:bf:a9 - f7:8a:c1:56:e7:57:86:c4:37:57:dd:fb:14:b0:cd:d9 - 92:7a:52:da:ea:b1:3e:a5:81:11:24:e9:7e:cd:d2:d2 - ab:40:2d:64:4d:86:4e:9d:80:eb:48:e4:f8:0b:ab:00 - b7:10:aa:17:4d:53:d6:15:f2:d0:95:84:54:e2:74:ee - 3d:19:7a:93:9a:e0:09:03:3b:ee:48:0a:08:a8:21:6e - 53:54:d6:7a:23:50:08:72:37:72:5c:f1:50:06:5c:a8 - 30:71:4d:95:2f:6e:43:f7:40:ae:ef:d0:86:f9:73:b1 - e2:46:9e:64:98:ee:02:42:e5:20:cb:c9:93:d5:16:61 - 68:ca:7d:a8:8c:b7:1c:a5:0d:10:17:58:c2:18:f8:e7 - 01: - -public exponent: - 01:00:01: - -private exponent: - 44:b8:a9:7e:b5:3b:cd:51:51:bb:ef:af:4b:63:d5:9e - 5f:01:ff:b6:00:4f:e2:a0:42:76:c3:eb:03:a9:5a:c3 - 01:2a:6e:18:6f:56:b8:cb:94:98:3b:55:4f:3a:2b:bc - 2a:5d:9a:8d:d1:79:d3:24:5f:c4:c9:95:c6:3a:6d:2b - 22:56:e8:9f:66:98:81:ce:81:eb:b9:2d:f0:73:41:20 - b7:40:50:51:7e:30:58:0b:75:09:23:b1:73:dc:9e:ac - 79:a5:e5:48:5f:ee:ca:ec:39:19:1c:aa:0d:de:40:d7 - 08:90:db:c6:67:8f:55:bf:81:be:5b:8a:15:f8:e9:e6 - ac:82:2a:0b:c3:45:fe:3b:15:04:8c:c9:fa:37:cc:0c - 71:b0:db:9c:d2:5c:df:9f:55:18:20:a0:4b:eb:53:c9 - b9:1f:0a:a8:98:9e:10:5a:35:68:a1:41:43:4e:a3:5f - e3:8c:22:94:55:2f:80:98:b4:a6:a9:9b:b2:d8:72:e1 - 55:5e:1c:06:d3:39:ec:c9:11:c0:6e:30:51:66:c4:47 - f2:ad:e1:30:83:0e:6e:c3:15:6b:26:97:b2:d4:2c:6a - 7b:c7:d9:33:5c:ca:24:ab:a8:dc:3b:1b:46:25:35:3d - fa:21:fe:ad:e7:a4:c4:58:eb:d8:48:c4:6a:e6:d3:ae - b2:f1:c6:b1:ed:7b:ca:23:fc:51:bc:9f:3a:3e:56:9e - 6d:23:b0:e5:9a:46:ae:f9:ee:8f:1a:5e:cc:42:37:22 - 6e:4b:52:37:0d:36:59:72:1a:19:c1:17:f9:45:55:bd - 33:ce:15:5b:b5:ee:a3:88:a2:b8:46:fe:b8:e2:93:54 - 0a:68:81:a3:b8:63:95:c6:b2:96:d0:f8:ac:d6:5d:df - 97:58:85:6f:eb:2d:66:3b:27:7a:db:2c:7d:8d:04:01 - 44:af:4a:91:2e:e8:f7:6c:e8:52:7a:70:a4:31:9a:f0 - b7:d3:bc:dd:f4:46:d0:61:f9:5f:57:98:c0:79:b9:e1 - 0f:6e:76:33:66:46:82:7a:31:61:a4:88:19:48:15:86 - 0e:b1:40:6a:af:1a:3f:75:db:12:bc:79:c7:f2:fe:41 - 49:02:88:3b:66:33:44:12:9d:43:f4:e6:cf:0d:4d:c5 - 44:d4:a9:4e:0d:fe:dd:00:87:bf:5e:f2:aa:5f:71:cf - f8:4a:24:4c:2c:bf:ee:88:3d:f5:78:e1:5c:95:a7:4c - 82:c8:65:7d:e7:b3:d7:98:b2:f2:4b:66:5c:1c:f7:d5 - 6a:6e:af:a7:e9:98:31:e8:92:99:1d:64:85:fb:8a:f6 - 6c:3b:a1:dc:22:be:fc:f8:8c:3b:51:c4:c0:dc:44:71 - - -prime1: - 00:cb:37:46:9c:11:91:72:90:0e:18:7a:ad:d9:c4:cb - 3d:64:2c:47:9a:2f:04:69:05:d5:22:b4:f0:af:3e:30 - 26:2e:32:35:83:ba:e1:80:3b:5a:5d:1c:c9:ac:c5:85 - f2:87:93:8e:f3:dd:7c:f5:cd:a5:77:8b:c2:bf:47:67 - 4d:c7:df:49:cd:41:bf:6c:14:f8:95:0e:db:00:70:d7 - 3a:43:b8:ae:43:be:a3:ce:48:5d:1b:c5:30:14:34:91 - 2c:6e:c5:00:81:ab:86:4b:9f:f0:ab:45:27:62:e0:ea - 2d:89:61:a2:bd:06:4d:2e:c7:2c:9d:bd:fd:f5:39:b8 - 29:2a:f4:2e:bf:1f:13:9d:c8:d0:bb:56:67:d3:de:2d - 31:63:22:61:5c:30:14:ea:b7:72:a8:f4:ea:d6:db:0b - 35:86:a6:e4:8d:c7:c2:9f:2b:33:85:d7:05:45:f7:05 - d4:5a:74:8f:26:4a:91:b3:7a:cc:8d:57:1b:c2:a8:b8 - e8:b5:ef:b0:24:4d:1d:8d:03:a1:79:0b:d4:0b:06:1a - 62:ca:5c:b8:aa:f9:ff:f4:cb:54:e1:6e:a5:d7:41:46 - 7c:2d:36:4b:15:01:c9:17:45:3c:33:44:79:4c:99:b0 - 43:0d:f7:c0:05:13:e5:82:3d:53:a3:2b:88:d9:ee:3b - 83: - -prime2: - 00:cf:c3:9b:a6:b3:b2:65:0b:88:8c:64:db:1e:00:21 - 42:a5:bd:47:75:0d:a4:29:e5:e9:9b:b7:1a:cf:df:9f - a3:44:9c:67:a4:a9:0c:87:97:94:94:40:54:44:3b:bf - 1a:f3:71:97:05:d6:a5:ad:28:f5:a9:94:92:f5:7f:f8 - cd:82:40:6e:ca:ff:d8:3f:18:84:42:83:dd:9e:87:bf - fa:2e:96:5e:fc:1d:db:d4:d5:dd:2d:c3:71:f4:bf:54 - 6d:06:21:95:8f:0a:89:b8:14:62:7c:a7:9b:10:fc:ae - 4b:0c:51:ac:46:5a:ee:04:cc:e2:ad:fc:97:50:32:b9 - c7:1e:40:6f:07:de:45:7f:2e:ac:12:39:30:b8:7b:6d - 0f:07:75:bd:58:c2:e2:e7:52:2a:6f:7e:8a:2f:07:c5 - eb:10:db:8d:10:2e:d4:a2:1c:28:ca:f5:c4:de:56:6a - 20:f3:47:21:b7:4c:b6:04:93:86:e6:78:b5:89:64:cb - 9e:f2:cd:78:e7:43:e6:a0:94:41:b4:c5:a8:c7:43:2b - 20:17:50:7a:a9:c0:ee:db:9a:f0:48:85:1f:1f:64:11 - 4e:4d:dd:99:6f:49:7a:f2:65:2a:83:98:96:5b:a3:7e - 64:da:77:00:7e:4b:7a:55:d9:cf:d7:73:fb:63:1a:f8 - 2b: - -coefficient: - 69:74:f7:c6:34:ec:d8:f9:08:c2:d8:5b:6a:61:4e:33 - b3:9b:dd:f1:30:8d:2e:bf:12:2d:74:de:53:6c:d2:88 - e5:9f:5d:5f:f1:e3:59:00:c4:1d:e3:5a:1d:07:6a:53 - ea:c8:46:0e:b1:61:5d:03:27:d9:1a:67:54:8d:bb:85 - 53:07:39:19:56:8a:dd:9f:f4:30:13:fa:2e:ce:1c:8e - 1c:cb:d6:54:b7:a9:92:26:87:f6:60:c1:04:41:fc:7c - f7:fd:fb:a7:fc:f2:d0:2c:4a:65:ef:62:5b:f9:b4:d6 - e8:f6:68:57:1f:b2:d1:d6:c4:78:0e:d4:66:39:cd:4c - 42:d7:5b:d1:ce:ae:5a:b6:25:12:7f:d1:2c:ae:95:4c - 14:cb:0a:31:7c:a8:0a:71:b8:98:c8:2b:03:19:92:d0 - e1:4c:a8:c4:0b:8c:b9:8b:d0:9a:b3:8f:27:b3:a4:09 - 8b:6d:95:e3:12:a0:60:e6:e7:b9:8f:53:fa:b4:56:f1 - cd:fe:11:31:20:01:fd:82:73:f1:51:ba:13:71:f1:0f - 8b:78:3f:36:71:7e:15:93:35:01:06:8b:40:a3:b5:23 - 22:9d:c7:c7:88:c8:25:07:dd:0d:1d:70:43:37:3e:ea - 25:b0:54:f0:5d:83:29:ca:5a:ec:4e:f5:a7:66:f0:fa - - -exp1: - 4a:04:cd:3c:45:8d:e3:db:a2:b6:b9:e0:9f:04:76:3e - db:40:e1:a7:c0:5e:6b:de:8a:fe:84:47:72:9d:45:2e - 72:ff:28:cc:dd:82:0b:92:12:dc:fd:82:5b:e2:ea:62 - 27:8d:d0:b0:f4:c8:f2:43:40:74:e5:bc:3e:ad:c4:6b - e9:54:64:6e:55:f7:62:67:d5:0f:7e:04:b9:09:60:eb - c1:05:00:bc:7e:30:ee:0f:1f:92:e0:e5:1d:46:f4:65 - e9:c6:e9:e3:51:55:ae:30:08:9a:69:aa:e9:f2:20:7a - 0b:a3:3b:82:7c:4c:1a:b0:c3:88:85:4e:7e:46:d2:d4 - 73:e7:d3:2b:1c:27:a9:fe:1e:41:4e:3c:ad:48:2c:cf - e3:5a:ff:79:73:ad:fa:bc:6d:10:2b:7d:6a:5b:08:9f - 2b:77:98:a2:27:d3:b4:e4:28:75:24:97:b0:1f:44:c9 - 4f:55:4b:5a:d8:28:6f:e6:57:a1:57:cc:2d:c0:04:f2 - 06:6a:d2:8e:b6:64:00:1c:05:71:b0:a4:40:8b:ad:8a - b4:48:c7:9e:c7:46:ba:a4:61:3b:67:71:12:91:9d:19 - d7:e2:01:c1:1a:10:63:e0:7d:07:f3:75:f7:37:b7:a3 - 04:f0:6b:c9:ad:b0:98:1a:bc:5f:1f:99:4e:3f:de:ff - - -exp2: - 0d:a2:8b:bb:83:fd:88:2e:1a:97:04:23:71:33:96:fb - 35:bf:57:4a:32:4b:fc:c7:ee:ed:de:35:6f:41:00:cc - 09:3b:ae:7d:9a:ee:8c:93:81:17:bd:a5:0a:19:55:b0 - 62:1b:a9:4a:a3:cc:99:b1:9f:75:b2:9f:76:67:20:9f - f4:15:60:70:08:1c:5b:ff:b2:e6:5e:9b:13:c5:5a:ef - 03:51:b1:08:20:b9:85:9d:47:77:b2:64:ef:28:03:55 - 68:5a:99:e3:1a:50:f1:78:bd:01:eb:49:fc:f2:68:49 - da:94:1d:97:3c:6e:74:78:31:c4:33:58:86:d5:dd:65 - 58:f1:e7:97:7f:99:d5:ff:ed:21:01:09:d6:81:9b:25 - aa:5a:aa:c3:81:7e:bc:a9:a2:c9:50:67:a7:30:7e:67 - af:e2:88:be:70:24:5a:43:38:d6:21:0c:fb:7e:76:56 - 95:40:ac:d0:c7:c3:06:47:dc:49:91:d0:70:24:e2:4c - 1b:29:2a:ef:1a:80:af:37:2b:9c:be:80:16:1b:ad:5f - dc:c7:d6:54:ff:a9:6d:56:1c:c0:d5:a3:b6:3e:ad:f8 - 12:9a:21:70:b1:44:d5:55:98:55:ac:94:e9:8c:b0:45 - d4:24:8d:2e:bc:ab:59:a9:02:bf:e4:07:b2:78:59:a3 - - - -Public Key ID: - sha256:99:0C:C5:EF:0C:3C:DC:71:32:9D:02:23:F8:28:E8:3B:55:D8:00:3D:D0:12:F3:56:33:03:B1:FD:30:3B:71:F1 - sha1:37:0D:50:BB:93:90:C0:E5:78:D6:E1:D0:FE:99:64:6D:84:5E:3C:36 -Public key's random art: -+--[ RSA 4096]----+ -| ...+oo o | -| .+ *.o. E | -| . *.=. = o | -| o ..=+ o | -| S *+.+ | -| . o+ | -| | -| | -| | -+-----------------+ - ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- From 1d59c210d222ab23f6e148ba667d52cdd1cf2604 Mon Sep 17 00:00:00 2001 From: Sebastian Carlos <88276600+sebastiancarlos@users.noreply.github.com> Date: Sun, 23 Jun 2024 20:25:43 -0300 Subject: [PATCH 047/242] Document 'modified' attribute in man page. (#3510) Co-authored-by: Sebastian Carlos --- doc/man/task.1.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/man/task.1.in b/doc/man/task.1.in index 985efe432..62a61a361 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -806,6 +806,10 @@ by '-', the specified tasks are removed from the dependency list. .B entry: For report purposes, specifies the date that a task was created. +.TP +.B modified: +Specifies the most recent modification date. + .SH ATTRIBUTE MODIFIERS Attribute modifiers improve filters. Supported modifiers are: From e7ad31c1c2cac0a6e856cf74a30dbf8934672161 Mon Sep 17 00:00:00 2001 From: Sebastian Carlos <88276600+sebastiancarlos@users.noreply.github.com> Date: Sun, 23 Jun 2024 20:45:08 -0300 Subject: [PATCH 048/242] Document the 'status' attribute in the man page (#3511) Co-authored-by: Sebastian Carlos --- doc/man/task.1.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/man/task.1.in b/doc/man/task.1.in index 62a61a361..e8835b1f0 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -760,6 +760,10 @@ add or remove a virtual tag. .B project: Specifies the project to which a task is related to. +.TP +.B status:pending|deleted|completed|waiting|recurring +Specifies the state of the task. + .TP .B priority:H|M|L or priority: Specifies High, Medium, Low and no priority for a task. From e3181aa8d4a5a86bdf38e002e0bf68867b2b1d08 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 24 Jun 2024 08:14:33 -0400 Subject: [PATCH 049/242] Consider all news "major" (#3493) This has the effect that `task news` will unconditionally update the config with the new version once news has been shown (assuming the user does not kill the process first). --- src/commands/CmdNews.cpp | 62 ++++------------------------------------ src/commands/CmdNews.h | 2 -- 2 files changed, 5 insertions(+), 59 deletions(-) diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 5e22fbb5b..1bbbab0a5 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -58,7 +58,7 @@ CmdNews::CmdNews () _uses_context = false; _accepts_filter = false; _accepts_modifications = false; - _accepts_miscellaneous = true; + _accepts_miscellaneous = false; _category = Command::Category::misc; } @@ -100,7 +100,6 @@ void wait_for_enter () // NewsItem::NewsItem ( Version version, - bool major, const std::string& title, const std::string& bg_title, const std::string& background, @@ -110,7 +109,6 @@ NewsItem::NewsItem ( const std::string& actions ) { _version = version; - _major = major; _title = title; _bg_title = bg_title; _background = background; @@ -194,7 +192,7 @@ std::vector NewsItem::all () { void NewsItem::version2_6_0 (std::vector& items) { Version version("2.6.0"); ///////////////////////////////////////////////////////////////////////////// - // - Writeable context (major) + // - Writeable context // Detect whether user uses any contexts auto config = Context::getContext ().config; @@ -253,7 +251,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem writeable_context ( version, - true, "'Writeable' context", "Background - what is context?", " The 'context' is a feature (introduced in 2.5.0) that allows users to apply a\n" @@ -293,11 +290,10 @@ void NewsItem::version2_6_0 (std::vector& items) { items.push_back(writeable_context); ///////////////////////////////////////////////////////////////////////////// - // - 64-bit datetime support (major) + // - 64-bit datetime support NewsItem uint64_support ( version, - false, "Support for 64-bit timestamps and numeric values", "", "", @@ -315,7 +311,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem waiting_status ( version, - true, "Deprecation of the status:waiting", "", " If a task has a 'wait' attribute set to a date in the future, it is modified\n" @@ -337,7 +332,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem env_vars ( version, - true, "Environment variables in the taskrc", "", "", @@ -356,7 +350,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem contextless_reports ( version, - true, "Context-less reports", "", " By default, every report is affected by currently active context.", @@ -378,7 +371,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem exportable_reports ( version, - false, "Exporting a particular report", "", "", @@ -402,7 +394,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem multi_holidays ( version, - false, "Multi-day holidays", "", " Holidays are currently used in 'task calendar' to visualize the workload during\n" @@ -425,7 +416,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem unicode_12 ( version, - false, "Extended Unicode support (Unicode 12)", "", "", @@ -444,7 +434,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem by_modifier ( version, - false, "The .by attribute modifier", "", "", @@ -463,7 +452,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem context_config ( version, - false, "Context-specific configuration overrides", "", "", @@ -488,7 +476,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem xdg_support ( version, - true, "Support for XDG Base Directory Specification", "", " The XDG Base Directory specification provides standard locations to store\n" @@ -517,7 +504,6 @@ void NewsItem::version2_6_0 (std::vector& items) { NewsItem holidata_2022 ( version, - false, "Updated holiday data for 2022", "", "", @@ -534,7 +520,6 @@ void NewsItem::version3_0_0 (std::vector& items) { Version version("3.0.0"); NewsItem sync { version, - /*major=*/true, /*title=*/"New data model and sync backend", /*bg_title=*/"", /*background=*/"", @@ -569,27 +554,6 @@ int CmdNews::execute (std::string& output) if (!news_version.is_valid()) news_version = Version("2.6.0"); - bool full_summary = false; - bool major_items = true; - - for (auto word: words) - { - if (word == "major") { - major_items = true; - break; - } - - if (word == "minor") { - major_items = false; - break; - } - - if (word == "all") { - full_summary = true; - break; - } - } - signal (SIGINT, signal_handler); // Remove items that have already been shown @@ -598,13 +562,6 @@ int CmdNews::execute (std::string& output) items.end () ); - // Remove non-major items if displaying a non-full (abbreviated) summary - int total_highlights = items.size (); - if (! full_summary) - items.erase ( - std::remove_if (items.begin (), items.end (), [&](const NewsItem& n){return n._major != major_items;}), - items.end () - ); Color bold = Color ("bold"); if (items.empty ()) { @@ -650,8 +607,9 @@ int CmdNews::execute (std::string& output) std::cout << outro.str (); // Set a mark in the config to remember which version's release notes were displayed - if (full_summary && news_version != current_version) + if (news_version != current_version) { + std::cout << "UPDATING\n"; CmdConfig::setConfigVariable ("news.version", std::string(current_version), false); // Revert back to default signal handling after displaying the outro @@ -689,15 +647,5 @@ int CmdNews::execute (std::string& output) else wait_for_enter (); // Do not display the outro and footnote at once - if (! items.empty() && ! full_summary && major_items) { - Context::getContext ().footnote (format ( - "Only major highlights were displayed ({1} out of {2} total).\n" - "If you're interested in more release highlights, run 'task news {3} minor'.", - items.size (), - total_highlights, - current_version - )); - } - return 0; } diff --git a/src/commands/CmdNews.h b/src/commands/CmdNews.h index a74b2dda3..ba5222176 100644 --- a/src/commands/CmdNews.h +++ b/src/commands/CmdNews.h @@ -36,7 +36,6 @@ class NewsItem { public: Version _version; - bool _major = false; std::string _title; std::string _bg_title; std::string _background; @@ -54,7 +53,6 @@ public: private: NewsItem ( Version, - bool, const std::string&, const std::string& = "", const std::string& = "", From 572268606f7464705fe6015db48d0e19c2116693 Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:31:53 +0200 Subject: [PATCH 050/242] update branch in coverage status badge (#3515) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 09565ac07..548b89af0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
    [![GitHub Actions build status](https://github.com/GothenburgBitFactory/taskwarrior/workflows/tests/badge.svg?branch=develop)](https://github.com/GothenburgBitFactory/taskwarrior/actions) -[![Coverage Status](https://coveralls.io/repos/github/GothenburgBitFactory/taskwarrior/badge.svg?branch=add-coverage-actions)](https://coveralls.io/github/GothenburgBitFactory/taskwarrior?branch=add-coverage-actions) +[![Coverage Status](https://coveralls.io/repos/github/GothenburgBitFactory/taskwarrior/badge.svg?branch=develop)](https://coveralls.io/github/GothenburgBitFactory/taskwarrior?branch=develop) [![Release](https://img.shields.io/github/v/release/GothenburgBitFactory/taskwarrior)](https://github.com/GothenburgBitFactory/taskwarrior/releases/latest) [![Release date](https://img.shields.io/github/release-date/GothenburgBitFactory/taskwarrior)](https://github.com/GothenburgBitFactory/taskwarrior/releases/latest) [![GitHub Sponsors](https://img.shields.io/github/sponsors/GothenburgBitFactory?color=green)](https://github.com/sponsors/GothenburgBitFactory/) From 71becf018560c8860ea22e04fa2743ac2918708a Mon Sep 17 00:00:00 2001 From: Sebastian Carlos <88276600+sebastiancarlos@users.noreply.github.com> Date: Mon, 24 Jun 2024 14:59:00 -0300 Subject: [PATCH 051/242] Remove mentions of legacy data files in the man pages (#3516) Co-authored-by: Sebastian Carlos --- doc/man/task.1.in | 30 ++++++++++-------------------- doc/man/taskrc.5.in | 14 +++++++------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/doc/man/task.1.in b/doc/man/task.1.in index e8835b1f0..7b3b57c90 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -497,11 +497,10 @@ are important. Running this command generates a summary of similar information that should accompany a bug report. It includes compiler, library and software information. It does not include -any personal information, other than the location and size of your task data -files. +any personal information, other than the location of your task data. -This command also performs a diagnostic scan of your data files looking for -common problems, such as duplicate UUIDs. +This command also performs a diagnostic scan of your data looking for common +problems, such as duplicate UUIDs. .TP .B task execute @@ -692,7 +691,7 @@ the online documentation at: .TP .B ID Tasks can be specified uniquely by IDs, which are simply the indexes of the -tasks in the data file. The ID of a task may therefore change, but only when +tasks in the database. The ID of a task may therefore change, but only when a command is run that displays IDs. When modifying tasks, it is safe to rely on the last displayed ID. Always run a report to check you have the right ID for a task. IDs can be given to task as a sequence, for example, @@ -1395,8 +1394,7 @@ intact, so you can use: $ task add project:\\'Three Word Project\\' description .RE -Taskwarrior supports Unicode using only the UTF8 encoding, with no Byte Order -Marks in the data files. +Taskwarrior supports Unicode using only the UTF8 encoding. .SH CONFIGURATION FILE AND OVERRIDE OPTIONS Taskwarrior stores its configuration in a file in the user's home directory: @@ -1447,21 +1445,13 @@ will check if $XDG_CONFIG_HOME/task/taskrc exists and attempt to read it .TP ~/.task -The default directory where task stores its data files. The location -can be configured in the configuration variable 'data.location', or -overridden with the TASKDATA environment variable.. +The default directory where task stores its data. The location can be +configured in the configuration variable 'data.location', or overridden with +the TASKDATA environment variable. .TP -~/.task/pending.data -The file that contains the tasks that are not yet done. - -.TP -~/.task/completed.data -The file that contains the completed ("done") tasks. - -.TP -~/.task/undo.data -The file that contains information needed by the "undo" command. +~/.task/taskchampion.sqlite3 +The database file. .SH "CREDITS & COPYRIGHTS" Copyright (C) 2006 \- 2021 T. Babej, P. Beckingham, F. Hernandez. diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index e1d4fbb3d..53d2ac515 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -173,7 +173,7 @@ These environment variables override defaults, but not command-line arguments. .TP .B TASKDATA=~/.task -This overrides the default path for the Taskwarrior data files. +This overrides the default path for the Taskwarrior data. .TP .B TASKRC=~/.taskrc @@ -197,7 +197,7 @@ Valid variable names and their default values are: .TP .B data.location=$HOME/.task -This is a path to the directory containing all the Taskwarrior files. By +This is a path to the directory containing all the Taskwarrior data. By default, it is set up to be ~/.task, for example: /home/paul/.task Note that you can use the @@ -212,11 +212,11 @@ This is a path to the hook scripts directory. By default it is ~/.task/hooks. .TP .B locking=1 -Determines whether to use file locking when accessing the pending.data and -completed.data files. Defaults to "1". Solaris users who store the data -files on an NFS mount may need to set locking to "0". Note that there is -danger in setting this value to "0" - another program (or another instance of -task) may write to the task.pending file at the same time. +Determines whether to use file locking when accessing the database. Defaults to +"1". Solaris users who store the database on an NFS mount may need to set +locking to "0". Note that there is danger in setting this value to "0" - +another program (or another instance of task) may write to the database at the +same time. .TP .B gc=1 From 910860ae1cf8d0d0be4dd6b968d15eeaa6ec0a1f Mon Sep 17 00:00:00 2001 From: Sebastian Carlos <88276600+sebastiancarlos@users.noreply.github.com> Date: Mon, 24 Jun 2024 15:00:12 -0300 Subject: [PATCH 052/242] Properly tag monospaced text in man pages. (Improves HTML rendering) (#3509) This would be immediately useful to improve the rendering of the man pages in third-party websites such as manned.org and the arch man pagesk - https://mankier.com/1/task - https://man.archlinux.org/man/task.1 The regular console output, or websites which render the manpage in monospaced fonts, will not be affected by this change. This change could also help with eventually rendering the manpages in the documentation website, and having a mix of monospaced and variable width fonts. To test the HTML output: ```bash git clone git@github.com:jacksonp/manner.git # used by ManKier ./manner/manner.php task.1.in >| task.1.html && $BROWSER task.1.html ``` Co-authored-by: Sebastian Carlos --- doc/man/task-color.5.in | 106 +++++++++----- doc/man/task-sync.5.in | 20 +++ doc/man/task.1.in | 309 +++++++++++++++++++++++++++------------ doc/man/taskrc.5.in | 314 +++++++++++++++++----------------------- 4 files changed, 436 insertions(+), 313 deletions(-) diff --git a/doc/man/task-color.5.in b/doc/man/task-color.5.in index 493fe713b..6464b8023 100644 --- a/doc/man/task-color.5.in +++ b/doc/man/task-color.5.in @@ -8,14 +8,18 @@ It should be mentioned that Taskwarrior is aware of whether its output is going to a terminal, or to a file or through a pipe. When Taskwarrior output goes to a terminal, color is desirable, but consider the following command: +.nf $ task list > file.txt +.fi Do we really want all those color control codes in the file? Taskwarrior assumes that you do not, and temporarily sets color to 'off' while generating the output. This explains the output from the following command: +.nf $ task show | grep '^color ' color off +.fi it always returns 'off', no matter what the setting, because the output is being sent to a pipe. @@ -23,20 +27,26 @@ sent to a pipe. If you wanted those color codes, you can override this behavior by setting the _forcecolor variable to on, like this: +.nf $ task config _forcecolor on $ task config | grep '^color ' color on +.fi or by temporarily overriding it like this: +.nf $ task rc._forcecolor=on config | grep '^color ' color on +.fi .SH AVAILABLE COLORS Taskwarrior has a 'color' command that will show all the colors it is capable of displaying. Try this: +.nf $ task color +.fi The output cannot be replicated here in a man page, but you should see a set of color samples. How many you see depends on your terminal program's ability to @@ -48,7 +58,9 @@ You should at least see the Basic colors and Effects - if you do, then you have .SH 16-COLOR SUPPORT The basic color support is provided through named colors: +.nf black, red, blue, green, magenta, cyan, yellow, white +.fi Foreground color (for text) is simply specified as one of the above colors, or not specified at all to use the default terminal text color. @@ -56,37 +68,49 @@ not specified at all to use the default terminal text color. Background color is specified by using the word 'on', and one of the above colors. Some examples: +.nf green # green text, default background color green on yellow # green text, yellow background on yellow # default text color, yellow background +.fi These colors can be modified further, by making the foreground bold, or by making the background bright. Some examples: +.nf bold green bold white on bright red on bright cyan +.fi The order of the words is not important, so the following are equivalent: +.nf bold green green bold +.fi But the 'on' is important - colors before the 'on' are foreground, and colors after 'on' are background. There is an additional 'underline' attribute that may be used: +.nf underline bold red on black +.fi And an 'inverse' attribute: +.nf inverse red +.fi Taskwarrior has a command that helps you visualize these color combinations. Try this: +.nf $ task color underline bold red on black +.fi You can use this command to see how the various color combinations work. You will also see some sample colors displayed, like the ones above, in addition to @@ -103,11 +127,13 @@ Using 256 colors follows the same form, but the names are different, and some colors can be referenced in different ways. First there is by color ordinal, which is like this: +.nf color0 color1 color2 ... color255 +.fi This gives you access to all 256 colors, but doesn't help you much. This range is a combination of 8 basic colors (color0 - color7), then 8 brighter variations @@ -119,31 +145,43 @@ be addressed via RGB values from 0 to 5 for each component color. A value of 0 means none of this component color, and a value of 5 means the most intense component color. For example, a bright red is specified as: +.nf rgb500 +.fi And a darker red would be: +.nf rgb300 +.fi Note that the three digits represent the three component values, so in this example the 5, 0 and 0 represent red=5, green=0, blue=0. Combining intense red with no green and no blue yields red. Similarly, blue and green are: +.nf rgb005 rgb050 +.fi Another example - bright yellow - is a mix of bright red and bright green, but no blue component, so bright yellow is addressed as: +.nf rgb550 +.fi A soft pink would be addressed as: +.nf rgb515 +.fi See if you agree, by running: +.nf $ task color black on rgb515 +.fi You may notice that the large color block is represented as 6 squares. All colors in the first square have a red value of 0. All colors in the 6th square @@ -163,7 +201,9 @@ will be disappointed, perhaps even appalled. There is some limited color mapping - for example, if you were to specify this combination: +.nf red on gray3 +.fi you are mixing a 16-color and 256-color specification. Taskwarrior will map red to color1, and proceed. Note that red and color1 are not quite the same tone. @@ -175,7 +215,9 @@ colors, but there is still underline available. Taskwarrior will show examples of all defined colors used in your .taskrc, or theme, if you run this command: +.nf $ task color legend +.fi This gives you an example of each of the colors, so you can see the effect, without necessarily creating a set of tasks that meet each of the rule criteria. @@ -185,20 +227,26 @@ Taskwarrior supports colorization rules. These are configuration values that specify a color, and the conditions under which that color is used. By example, let us add a few tasks: +.nf $ task add project:Home priority:H pay the bills (1) $ task add project:Home clean the rug (2) $ task add project:Garden clean out the garage (3) +.fi We can add a color rule that uses a blue background for all tasks in the Home project: +.nf $ task config color.project.Home 'on blue' +.fi We use quotes around 'on blue' because there are two words, but they represent one value in the .taskrc file. Now suppose we wish to use a bold yellow text color for all cleaning work: +.nf $ task config color.keyword.clean 'bold yellow' +.fi Now what happens to task 2, which belongs to project Home (blue background), and is also a cleaning task (bold yellow foreground)? The colors are combined, and @@ -219,7 +267,9 @@ color blending. The precedence for the color rules is determined by the configuration variable 'rule.precedence.color', which by default contains: +.nf deleted,completed,active,keyword.,tag.,project.,overdue,scheduled,due.today,due,blocked,blocking,recurring,tagged,uda. +.fi These are just the color rules with the 'color.' prefix removed. The rule 'color.deleted' has the highest precedence, and 'color.uda.' the lowest. @@ -238,50 +288,38 @@ be included. To get a good idea of what a color theme looks like, try adding this entry to your .taskrc file: -.RS -include dark-256.theme -.RE +.nf + include dark-256.theme +.fi You can use any of the standard Taskwarrior themes: -.RS -dark-16.theme -.br -dark-256.theme -.br -dark-blue-256.theme -.br -dark-gray-256.theme -.br -dark-green-256.theme -.br -dark-red-256.theme -.br -dark-violets-256.theme -.br -dark-yellow-green.theme -.br -light-16.theme -.br -light-256.theme -.br -solarized-dark-256.theme -.br -solarized-light-256.theme -.br -dark-default-16.theme -.br -dark-gray-blue-256.theme -.br -no-color.theme -.RE +.nf + dark-16.theme + dark-256.theme + dark-blue-256.theme + dark-gray-256.theme + dark-green-256.theme + dark-red-256.theme + dark-violets-256.theme + dark-yellow-green.theme + light-16.theme + light-256.theme + solarized-dark-256.theme + solarized-light-256.theme + dark-default-16.theme + dark-gray-blue-256.theme + no-color.theme +.fi Bear in mind that if you are using a terminal with a dark background, you will see better results using a dark theme. You can also see how the theme will color the various tasks with the command: +.nf $ task color legend +.fi Better yet, create your own, and share it. We will gladly host the theme file on . diff --git a/doc/man/task-sync.5.in b/doc/man/task-sync.5.in index 8f9b626c7..c8d7c5a53 100644 --- a/doc/man/task-sync.5.in +++ b/doc/man/task-sync.5.in @@ -35,7 +35,9 @@ This is the default, and is appropriate for local synchronization. For synchronization to a server, a better solution is to run +.nf $ task sync +.fi periodically, such as via .BR cron (8) . @@ -52,7 +54,9 @@ For most of these, you will need an encryption secret used to encrypt and decrypt your tasks. This can be any secret string, and must match for all replicas sharing tasks. +.nf $ task config sync.encryption_secret +.fi Tools such as .BR pwgen (1) @@ -70,8 +74,10 @@ information from the server administrator: Configure Taskwarrior with these details: +.nf $ task config sync.server.origin $ task config sync.server.client_id +.fi Note that the origin must include the scheme, such as 'http://' or 'https://'. @@ -83,12 +89,16 @@ the bucket are adequate. Authenticate to the project with: +.nf $ gcloud config set project $PROJECT_NAME $ gcloud auth application-default login +.fi Then configure Taskwarrior with: +.nf $ task config sync.gcp.bucket +.fi However you can bring your own service account credentials if your `application-default` is already being used by some other application @@ -124,8 +134,10 @@ Select the following permissions: Then configure Taskwarrior with: +.nf $ task config sync.gcp.bucket $ task config sync.gcp.credential_path +.fi .SS Local Synchronization @@ -133,7 +145,9 @@ In order to take advantage of synchronization's side effect of saving disk space without setting up a remote server, it is possible to sync tasks locally. To configure local sync: +.nf $ task config sync.local.server_dir /path/to/sync +.fi The default configuration is to sync to a database in the task directory ("data.location"). @@ -162,11 +176,15 @@ invent their own "encryption_secret". If you run multiple clients that sync to the same server, you will need to run this command on your primary client (the one you use most often): +.nf $ task config recurrence on +.fi And on the other clients, run: +.nf $ task config recurrence off +.fi This protects you against the effects of a sync/duplication bug. @@ -185,7 +203,9 @@ modifying the same task on two machines, without an intervening sync. Setup simply involves creating the directory and modifying your data.location configuration variable like this: +.nf $ task config data.location /path/to/shared/directory +.fi Strengths: .br diff --git a/doc/man/task.1.in b/doc/man/task.1.in index 7b3b57c90..e26eafebc 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -24,18 +24,24 @@ descriptors), project groups, etc. The consists of zero or more search criteria that select tasks. For example, to list all pending tasks belonging to the 'Home' project: +.nf task project:Home list +.fi You can specify multiple filter terms, each of which further restricts the result: +.nf task project:Home +weekend garden list +.fi This example applies three filters: the 'Home' project, the 'weekend' tag, and the description or annotations must contain the character sequence 'garden'. In this example, 'garden' is translated internally to: +.nf description.contains:garden +.fi as a convenient shortcut. The 'contains' here is an attribute modifier, which is used to exert more control over the filter than simply absence or presence. @@ -45,24 +51,30 @@ Note that a filter may have zero terms, which means that all tasks apply to the command. This can be dangerous, and this special case is confirmed, and cannot be overridden. For example, this command: +.nf task modify +work This command has no filter, and will modify all tasks. Are you sure? (yes/no) +.fi will add the 'work' tag to all tasks, but only after confirmation. More filter examples: +.nf task task 28 task +weekend task +bills due.by:eom task project:Home due.before:today task ebeeab00-ccf8-464b-8b58-f7f2d606edfb +.fi By default filter elements are combined with an implicit 'and' operator, but 'or' and 'xor' may also be used, provided parentheses are included: +.nf task '( /[Cc]at|[Dd]og/ or /[0-9]+/ )' +.fi The parentheses isolate the logical term from any default command filter or implicit report filter which would be combined with an implicit 'and'. @@ -71,10 +83,12 @@ A filter may target specific tasks using ID or UUID numbers. To specify multiple tasks use one of these forms (space-separated list of ID numbers, UUID numbers or ID ranges): +.nf task 1 2 3 delete task 1-3 info task 1 2-5 19 modify pri:H task 4-7 ebeeab00-ccf8-464b-8b58-f7f2d606edfb info +.fi Note that it may be necessary to properly escape special characters as well as quotes in order to avoid their special meanings in the shell. See also the @@ -85,11 +99,13 @@ section 'SPECIFYING DESCRIPTIONS' for more information. The consist of zero or more changes to apply to the selected tasks, such as: +.nf task project:Home task +weekend +garden due:tomorrow task Description/annotation text task /from/to/ <- replace first match task /from/to/g <- replace all matches +.fi .SH SUBCOMMANDS @@ -188,6 +204,7 @@ wish to save it, or pipe it to another command or script to convert it to another format. You'll find these example scripts online at : +.nf export-csv.pl export-sql.py export-xml.py @@ -198,6 +215,7 @@ another format. You'll find these example scripts online at export-ical.pl export-xml.pl export-yad.pl +.fi .TP .B task ghistory.annual @@ -243,12 +261,16 @@ Applies the filter then extracts only the task IDs and presents them as a space-separated list. This is useful as input to a task command, to achieve this: +.nf task $(task project:Home ids) modify priority:H +.fi This example first gets the IDs for the project:Home filter, then sets the priority to H for each of those tasks. This can also be achieved directly: +.nf task project:Home modify priority:H +.fi This command is mainly of use to external scripts. @@ -258,7 +280,9 @@ Applies the filter on all tasks (even deleted and completed tasks) then extracts only the task UUIDs and presents them as a space-separated list. This is useful as input to a task command, to achieve this: +.nf task $(task project:Home status:completed uuids) modify status:pending +.fi This example first gets the UUIDs for the project:Home and status:completed filters, then makes each of those tasks pending again. @@ -385,8 +409,10 @@ if import is to be used in automated workflows. See taskrc(5). For importing other file formats, the standard task release comes with a few example scripts, such as: +.nf import-todo.sh.pl import-yaml.pl +.fi .TP .B task log @@ -423,14 +449,20 @@ parses and evaluates the expression given on the command line. Examples: +.nf task calc 1 + 1 2 +.fi +.nf task calc now + 8d 2015-03-26T18:06:57 +.fi +.nf task calc eom 2015-03-31T23:59:59 +.fi .TP .B task config [ [ | '']] @@ -438,16 +470,22 @@ Add, modify and remove settings directly in the Taskwarrior configuration. This command either modifies the 'name' setting with a new value of 'value', or adds a new entry that is equivalent to 'name=value': +.nf task config name value +.fi This command sets a blank value. This has the effect of suppressing any default value: +.nf task config name '' +.fi Finally, this command removes any 'name=...' entry from the .taskrc file: +.nf task config name +.fi .TP .B task context @@ -455,7 +493,9 @@ Sets the currently active context. See the CONTEXT section. Example: +.nf task context work +.fi .TP .B task context delete @@ -464,7 +504,9 @@ set as active, it will be unset. Example: +.nf task context delete work +.fi .TP .B task context define @@ -473,9 +515,11 @@ does not affect the currently set context, just adds a new context definition. Examples: +.nf task context define work project:Work task context define home project:Home or +home task context define superurgent due:today and +urgent +.fi .TP .B task context list @@ -543,11 +587,15 @@ The sync command synchronizes data with the Taskserver, if configured. Note: If you use multiple sync clients, make sure this setting (which is the default) is on your primary client: +.nf recurrence=on +.fi and on all other clients (this is not the default): +.nf recurrence=off +.fi This is a workaround to avoid a recurrence bug that duplicates recurring tasks. @@ -607,7 +655,9 @@ by third-party applications. Reports a unique set of attribute values. For example, to see all the active projects: +.nf task +PENDING _unique project +.fi .TP .B task _uuids @@ -654,6 +704,7 @@ Shows the UUIDs and descriptions of matching tasks. Accesses and displays the DOM reference(s). Used to extract individual values from tasks, or the system. Supported DOM references are: +.nf rc. tw.syncneeded tw.program @@ -669,6 +720,7 @@ from tasks, or the system. Supported DOM references are: system.os . . +.fi Note that the 'rc.' reference may need to be escaped using '--' to prevent the reference from being interpreted as an override. @@ -679,8 +731,10 @@ missing value, the command exits with 1. Additionally, some components of the attributes of particular types may be extracted by DOM references. +.nf $ task _get 2.due.year 2015 +.fi For a full list of supported attribute-specific DOM references, consult the online documentation at: @@ -694,10 +748,11 @@ Tasks can be specified uniquely by IDs, which are simply the indexes of the tasks in the database. The ID of a task may therefore change, but only when a command is run that displays IDs. When modifying tasks, it is safe to rely on the last displayed ID. Always run a report to check you have the right -ID for a task. IDs can be given to task as a sequence, for example, -.br -.B +ID for a task. IDs can be given to task as a sequence, for example: + +.nf task 1,4-10,19 delete +.fi .TP .B +tag|-tag @@ -708,15 +763,18 @@ Certain tags (called 'special tags'), can be used to affect the way tasks are treated. For example, if a task has the special tag 'nocolor', then it is exempt from all color rules. The supported special tags are: +.nf +nocolor Disable color rules processing for this task +nonag Completion of this task suppresses all nag messages +nocal This task will not appear on the calendar +next Elevates task so it appears on 'next' report +.fi There are also virtual tags, which represent task metadata in tag form. These tags do not exist, but can be used to filter tasks. The supported virtual tags are: +.nf ACTIVE Matches if the task is started ANNOTATED Matches if the task has annotations BLOCKED Matches if the task is blocked @@ -748,6 +806,7 @@ are: WEEK Matches if the task is due this week YEAR Matches if the task is due this year YESTERDAY Matches if the task was due sometime yesterday +.fi .\" If you update the above list, update src/commands/CmdInfo.cpp and src/commands/CmdTags.cpp as well. @@ -853,9 +912,9 @@ calculated attributes: For example: -.RS -task due.before:eom priority.not:L list -.RE +.nf + task due.before:eom priority.not:L list +.fi The .I before @@ -875,15 +934,21 @@ The modifier is the same as 'before', except it also includes the moment in question. For example: +.nf task add test due:eoy +.fi will be found when using the inclusive filter 'by': +.nf task due.by:eoy +.fi but not when the non-inclusive filter 'before' is used: +.nf task due.before:eoy +.fi this applies equally to other named dates such as 'eom', 'eod', etc; the modifier compares using '<=' rather than '<' like 'before' does. @@ -892,8 +957,10 @@ The .I none modifier requires that the attribute does not have a value. For example: +.nf task priority: list task priority.none: list +.fi are equivalent, and list tasks that do not have a priority. @@ -915,8 +982,10 @@ The .I has modifier is used to search for a substring, such as: +.nf task description.has:foo list task foo list +.fi These are equivalent and will return any task that has 'foo' in the description or annotations. @@ -931,13 +1000,17 @@ The .I startswith modifier matches against the left, or beginning of an attribute, such that: +.nf task project.startswith:H list task project:H list +.fi are equivalent and will match any project starting with 'H'. Matching all projects not starting with 'H' is done with: +.nf task project.not:H list +.fi The .I endswith @@ -948,7 +1021,9 @@ The modifier requires that the attribute contain the whole word specified, such that this: +.nf task description.word:bar list +.fi Will match the description 'foo bar baz' but does not match 'dog food'. @@ -962,15 +1037,19 @@ modifier. You can use the following operators in filter expressions: +.nf and or xor ! Logical operators < <= = == != !== >= > Relational operators ( ) Precedence +.fi For example: +.nf task due.before:eom priority.not:L list task '( due < eom or priority != L )' list task '! ( project:Home or project:Garden )' list +.fi The .I = @@ -994,32 +1073,44 @@ Note that the parentheses are required when using a logical operator other than the 'and' operator. The reason is that some reports contain filters that must be combined with the command line. Consider this example: +.nf task project:Home or project:Garden list +.fi While this looks correct, it is not. The 'list' report contains a filter of: +.nf task show report.list.filter Config Variable Value ----------------- -------------- report.list.filter status:pending +.fi Which means the example is really: +.nf task status:pending project:Home or project:Garden list +.fi The implied 'and' operator makes it: +.nf task status:pending and project:Home or project:Garden list +.fi This is a precedence error - the 'and' and 'or' need to be grouped using parentheses, like this: +.nf task status:pending and ( project:Home or project:Garden ) list +.fi The original example therefore must be entered as: +.nf task '( project:Home or project:Garden )' list +.fi This includes quotes to escape the parentheses, so that the shell doesn't interpret them and hide them from Taskwarrior. @@ -1027,11 +1118,13 @@ interpret them and hide them from Taskwarrior. There is redundancy between operators, attribute modifiers and other syntactic sugar. For example, the following are all equivalent: +.nf task foo list task /foo/ list task description.contains:foo list task description.has:foo list task 'description ~ foo' list +.fi .SH SPECIFYING DATES AND FREQUENCIES @@ -1045,92 +1138,83 @@ configuration variable .RS .TP Exact specification -task ... due:7/14/2008 +.nf + task ... due:7/14/2008 +.fi .TP ISO-8601 -task ... due:2013-03-14T22:30:00Z +.nf + task ... due:2013-03-14T22:30:00Z +.fi .TP Relative wording -task ... due:now -.br -task ... due:today -.br -task ... due:yesterday -.br -task ... due:tomorrow +.nf + task ... due:now + task ... due:today + task ... due:yesterday + task ... due:tomorrow +.fi .TP Day number with ordinal -task ... due:23rd -.br -task ... due:3wks -.br -task ... due:1day -.br -task ... due:9hrs +.nf + task ... due:23rd + task ... due:3wks + task ... due:1day + task ... due:9hrs +.fi .TP Start of next (work) week (Monday), calendar week (Sunday or Monday), month, quarter and year -.br -task ... due:sow -.br -task ... due:soww -.br -task ... due:socw -.br -task ... due:som -.br -task ... due:soq -.br -task ... due:soy +.nf + task ... due:sow + task ... due:soww + task ... due:socw + task ... due:som + task ... due:soq + task ... due:soy +.fi .TP End of current (work) week (Friday), calendar week (Saturday or Sunday), month, quarter and year -.br -task ... due:eow -.br -task ... due:eoww -.br -task ... due:eocw -.br -task ... due:eom -.br -task ... due:eoq -.br -task ... due:eoy +.nf + task ... due:eow + task ... due:eoww + task ... due:eocw + task ... due:eom + task ... due:eoq + task ... due:eoy +.fi .TP At some point or later -.br -task ... wait:later -.br -task ... wait:someday +.nf + task ... wait:later + task ... wait:someday +.fi This sets the wait date to 12/30/9999. .TP Next occurring weekday -task ... due:fri +.nf + task ... due:fri +.fi .TP Predictable holidays -task ... due:goodfriday -.br -task ... due:easter -.br -task ... due:eastermonday -.br -task ... due:ascension -.br -task ... due:pentecost -.br -task ... due:midsommar -.br -task ... due:midsommarafton -.br -task ... due:juhannus +.nf + task ... due:goodfriday + task ... due:easter + task ... due:eastermonday + task ... due:ascension + task ... due:pentecost + task ... due:midsommar + task ... due:midsommarafton + task ... due:juhannus +.fi .RE .SS FREQUENCIES @@ -1181,7 +1265,8 @@ Context is a user-defined query, which is automatically applied to all commands that filter the task list and to commands that create new tasks (add, log). For example, any report command will have its result affected by the current active context. Here is a list of the commands that are affected: -.IP + +.nf add burndown count @@ -1199,33 +1284,44 @@ active context. Here is a list of the commands that are affected: stop summary tags +.fi All other commands are NOT affected by the context. +.nf $ task list ID Age Project Description Urg 1 2d Sport Run 5 miles 1.42 2 1d Home Clean the dishes 1.14 +.fi +.nf $ task context home Context 'home' set. Use 'task context none' to remove. +.fi +.nf $ task list ID Age Project Description Urg 2 1d Home Clean the dishes 1.14 Context 'home' set. Use 'task context none' to remove. +.fi Task list got automatically filtered for project:Home. +.nf $ task add Vaccuum the carpet Created task 3. Context 'home' set. Use 'task context none' to remove. +.fi +.nf $ task list ID Age Project Description Urg 2 1d Home Clean the dishes 1.14 3 5s Home Vaccuum the carpet 1.14 Context 'home' set. Use 'task context none' to remove. +.fi Note that the newly added task "Vaccuum the carpet" has "project:Home" set automatically. @@ -1236,22 +1332,28 @@ new context's name to the 'context' command. To unset any context, use the 'none' subcommand. +.nf $ task context none Context unset. +.fi +.nf $ task list ID Age Project Description Urg 1 2d Sport Run 5 miles 1.42 2 1d Home Clean the dishes 1.14 3 7s Home Vaccuum the carpet 1.14 +.fi Context can be defined using the 'define' subcommand, specifying both the name of the new context, and it's assigned filter. +.nf $ task context define home project:Home Are you sure you want to add 'context.home.read' with a value of 'project:Home'? (yes/no) yes Are you sure you want to add 'context.home.write' with a value of 'project:Home'? (yes/no) yes Context 'home' successfully defined. +.fi Note that you were separately prompted to set the 'read' and 'write' context. This allows you to specify contexts that only work for reporting commands or @@ -1259,13 +1361,16 @@ only for commands that create tasks. To remove the definition, use the 'delete' subcommand. +.nf $ task context delete home Are you sure you want to remove 'context.home.read'? (yes/no) yes Are you sure you want to remove 'context.home.write'? (yes/no) yes Context 'home' deleted. +.fi To check what is the currently active context, use the 'show' subcommand. +.nf $ task context show Context 'home' with @@ -1273,13 +1378,16 @@ To check what is the currently active context, use the 'show' subcommand. * write filter: '+home' is currently applied. +.fi Contexts can store arbitrarily complex filters. +.nf $ task context define family project:Family or +paul or +nancy Are you sure you want to add 'context.family.read' with a value of 'project:Family or +paul or +nancy'? (yes/no) yes Are you sure you want to add 'context.family.write' with a value of 'project:Family or +paul or +nancy'? (yes/no) no Context 'family' successfully defined. +.fi Contexts are permanent, and the currently set context name is stored in the "context" configuration variable. The context definition is stored in the @@ -1292,13 +1400,17 @@ filter as writeable context. The reason for this decision is that the complex filter in the example does not directly translate to a modification. In fact, if such a context is used as a writeable context, the following happens: +.nf $ task add Call Paul Created task 4. Context 'family' set. Use 'task context none' to remove. +.fi +.nf $ task 4 list ID Age Project Tags Description Urg 4 9min Family nancy paul or or Call Paul 0 +.fi There is no clear mapping between the complex filter used and the modifications @@ -1307,16 +1419,20 @@ operators being present in the description. Taskwarrior does not try to guess the user intention here, and instead, the user is expected to set the "context..write" variable to make their intention explicit, for example: +.nf $ task config context.family.write project:Family Are you sure you want to change the value of 'context.family.write' from 'project:Family or +paul or +nancy' to 'project:Family'? (yes/no) yes Config file /home/tbabej/.config/task/taskrc modified. +.fi +.nf $ task context Name Type Definition Active family read project:Family or +paul or +nancy yes write project:Family yes home read +home no write +home no +.fi Note how read and write contexts differ for context "family", while for context "home" they stay the same. @@ -1325,74 +1441,75 @@ In addition, every configuration parameter can be overridden for the current context, by specifying context..rc.. For example, if the default command for the family context should be displaying the family_report: +.nf $ task config context.family.rc.default.command family_report +.fi .SH COMMAND ABBREVIATION All Taskwarrior commands may be abbreviated as long as a unique prefix is used, for example: -.RS -$ task li -.RE +.nf + $ task li +.fi is an unambiguous abbreviation for -.RS -$ task list -.RE +.nf + $ task list +.fi but -.RS -$ task l -.RE +.nf + $ task l +.fi could be list, ls or long. Note that you can restrict the minimum abbreviation size using the configuration setting: -.RS -abbreviation.minimum=3 -.RE +.nf + abbreviation.minimum=3 +.fi .SH SPECIFYING DESCRIPTIONS Some task descriptions need to be escaped because of the shell and the special meaning of some characters to the shell. This can be done either by adding quotes to the description or escaping the special character: -.RS -$ task add "quoted ' quote" -.br -$ task add escaped \\' quote -.RE +.nf + $ task add "quoted ' quote" + $ task add escaped \\' quote +.fi The argument \-\- (a double dash) tells Taskwarrior to treat all other args as description: -.RS -$ task add -- project:Home needs scheduling -.RE +.nf + $ task add -- project:Home needs scheduling +.fi In other situations, the shell sees spaces and breaks up arguments. For example, this command: -.RS -$ task 123 modify /from this/to that/ -.RE +.nf + $ task 123 modify /from this/to that/ +.fi is broken up into several arguments, which is corrected with quotes: -.RS -$ task 123 modify "/from this/to that/" -.RE +.nf + $ task 123 modify "/from this/to that/" +.fi It is sometimes necessary to force the shell to pass quotes to Taskwarrior intact, so you can use: -.RS -$ task add project:\\'Three Word Project\\' description -.RE +.nf + $ task add project:\\'Three Word Project\\' description +.fi Taskwarrior supports Unicode using only the UTF8 encoding. diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 53d2ac515..8926d61f5 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -18,43 +18,43 @@ obtains its configuration data from a file called .I .taskrc \&. This file is normally located in the user's home directory: -.RS -$HOME/.taskrc -.RE +.nf + $HOME/.taskrc +.fi The default location can be overridden using the .I rc: attribute when running task: -.RS -$ task rc:/.taskrc ... -.RE +.nf + $ task rc:/.taskrc ... +.fi or using the TASKRC environment variable: -.RS -$ TASKRC=/tmp/.taskrc task ... -.RE +.nf + $ TASKRC=/tmp/.taskrc task ... +.fi Additionally, if no ~/.taskrc exists, taskwarrior will check if the XDG_CONFIG_HOME environment variable is defined: -.RS -$ XDG_CONFIG_HOME=~/.config task ... -.RE +.nf + $ XDG_CONFIG_HOME=~/.config task ... +.fi Individual options can be overridden by using the .I rc.: attribute when running task: -.RS -$ task rc.: ... -.RE +.nf + $ task rc.: ... +.fi or -.RS -$ task rc.= ... -.RE +.nf + $ task rc.= ... +.fi If .B Taskwarrior @@ -65,9 +65,9 @@ file in the user's home directory. The .taskrc file follows a very simple syntax defining name/value pairs: -.RS - = -.RE +.nf + = +.fi There may be whitespace around , '=' and , and it is ignored. Whitespace within the is left intact. @@ -79,9 +79,9 @@ Note that Taskwarrior is flexible about the values used to represent Boolean items. You can use "1" to enable, anything else is interpreted as disabled. The values "on", "yes", "y" and "true" are currently supported but deprecated. -.RS -include -.RE +.nf + include +.fi There may be whitespace around 'include' and . The file may be an absolute or relative path, and the special character '~' is expanded to mean @@ -95,9 +95,9 @@ respect to the following directories (listed in order of precedence): Note that environment variables are also expanded in paths (and any other taskrc variables). -.RS -# -.RE +.nf + # +.fi A comment consists of the character '#', and extends from the '#' to the end of the line. There is no way to comment a multi-line block. There may be @@ -108,9 +108,9 @@ that makes use of every default. The contents of the .taskrc file therefore represent overrides of the default values. To remove a default value completely there must be an entry like this: -.RS - = -.RE +.nf + = +.fi This entry overrides the default value with a blank value. @@ -118,28 +118,28 @@ This entry overrides the default value with a blank value. You can edit your .taskrc file by hand if you wish, or you can use the 'config' command. To permanently set a value in your .taskrc file, use this command: -.RS -$ task config nag "You have more urgent tasks." -.RE +.nf + $ task config nag "You have more urgent tasks." +.fi To delete an entry, use this command: -.RS -$ task config nag -.RE +.nf + $ task config nag +.fi Taskwarrior will then use the default value. To explicitly set a value to blank, and therefore avoid using the default value, use this command: -.RS -$ task config nag "" -.RE +.nf + $ task config nag "" +.fi Taskwarrior will also display all your settings with this command: -.RS -$ task show -.RE +.nf + $ task show +.fi and in addition, will also perform a check of all the values in the file, warning you of anything it finds amiss. @@ -149,20 +149,19 @@ The .taskrc can include other files containing configuration settings by using t .B include statement: -.RS -include -.RE +.nf + include +.fi By using include files you can divide your main configuration file into several ones containing just the relevant configuration data like colors, etc. There are two excellent uses of includes in your .taskrc, shown here: -.RS -include holidays.en-US.rc -.br -include dark-16.theme -.RE +.nf + include holidays.en-US.rc + include dark-16.theme +.fi This includes two standard files that are distributed with Taskwarrior, which define a set of US holidays, and set up a 16-color theme to use, to color the @@ -299,6 +298,7 @@ is most readily parsed and used by shell scripts. Alternatively, you can specify a comma-separated list of verbosity tokens that control specific occasions when output is generated. This list may contain: +.nf blank Inserts extra blank lines in output, for clarity header Messages that appear before report output (this includes .taskrc/.task overrides and the "[task next]" message) footnote Messages that appear after report output (mostly status messages and change descriptions) @@ -315,6 +315,7 @@ control specific occasions when output is generated. This list may contain: override Notification when configuration options are overridden recur Notification when a new recurring task instance is created default Notifications about taskwarrior choosing to perform a default action. +.fi The tokens "affected", "new-id", "new-uuid", "project", "override" and "recur" imply "footnote". @@ -326,14 +327,20 @@ and the "nothing" setting is equivalent to none of the tokens being specified. Here are the shortcut equivalents: +.nf verbose=on verbose=blank,header,footnote,label,new-id,affected,edit,special,project,sync,filter,override,recur +.fi +.nf verbose=0 verbose=blank,label,new-id,edit +.fi +.nf verbose=nothing verbose= +.fi Those additional comments are sent to the standard error for header, footnote and project. The others are sent to standard output. @@ -584,51 +591,29 @@ are formatted according to dateformat. The default value is the ISO-8601 standard: Y-M-D. The string can contain the characters: -.RS -.RS -m minimal-digit month, for example 1 or 12 -.br -d minimal-digit day, for example 1 or 30 -.br -y two-digit year, for example 09 or 12 -.br -D two-digit day, for example 01 or 30 -.br -M two-digit month, for example 01 or 12 -.br -Y four-digit year, for example 2009 or 2015 -.br -a short name of weekday, for example Mon or Wed -.br -A long name of weekday, for example Monday or Wednesday -.br -b short name of month, for example Jan or Aug -.br -B long name of month, for example January or August -.br -v minimal-digit week, for example 3 or 37 -.br -V two-digit week, for example 03 or 37 -.br -h minimal-digit hour, for example 3 or 21 -.br -n minimal-digit minutes, for example 5 or 42 -.br -s minimal-digit seconds, for example 7 or 47 -.br -H two-digit hour, for example 03 or 21 -.br -N two-digit minutes, for example 05 or 42 -.br -S two-digit seconds, for example 07 or 47 -.br -J three-digit Julian day, for example 023 or 365 -.br -j Julian day, for example 23 or 365 -.br -w Week day, for example 0 for Monday, 5 for Friday -.RE -.RE +.nf + m minimal-digit month, for example 1 or 12 + d minimal-digit day, for example 1 or 30 + y two-digit year, for example 09 or 12 + D two-digit day, for example 01 or 30 + M two-digit month, for example 01 or 12 + Y four-digit year, for example 2009 or 2015 + a short name of weekday, for example Mon or Wed + A long name of weekday, for example Monday or Wednesday + b short name of month, for example Jan or Aug + B long name of month, for example January or August + v minimal-digit week, for example 3 or 37 + V two-digit week, for example 03 or 37 + h minimal-digit hour, for example 3 or 21 + n minimal-digit minutes, for example 5 or 42 + s minimal-digit seconds, for example 7 or 47 + H two-digit hour, for example 03 or 21 + N two-digit minutes, for example 05 or 42 + S two-digit seconds, for example 07 or 47 + J three-digit Julian day, for example 023 or 365 + j Julian day, for example 23 or 365 + w Week day, for example 0 for Monday, 5 for Friday +.fi .RS The characters 'v', 'V', 'a' and 'A' can only be used for formatting printed @@ -640,37 +625,24 @@ The string may also contain other characters to act as spacers, or formatting. Examples for other values of dateformat: .RE -.RS -.RS -.br -d/m/Y would use for input and output 24/7/2009 -.br -yMD would use for input and output 090724 -.br -M-D-Y would use for input and output 07-24-2009 -.RE -.RE +.nf + d/m/Y would use for input and output 24/7/2009 + yMD would use for input and output 090724 + M-D-Y would use for input and output 07-24-2009 +.fi .RS Examples for other values of dateformat.report: .RE -.RS -.RS -.br -a D b Y (V) would emit "Fri 24 Jul 2009 (30)" -.br -A, B D, Y would emit "Friday, July 24, 2009" -.br -wV a Y-M-D would emit "w30 Fri 2009-07-24" -.br -yMD.HN would emit "110124.2342" -.br -m/d/Y H:N would emit "1/24/2011 10:42" -.br -a D b Y H:N:S would emit "Mon 24 Jan 2011 11:19:42" -.RE -.RE +.nf + a D b Y (V) would emit "Fri 24 Jul 2009 (30)" + A, B D, Y would emit "Friday, July 24, 2009" + wV a Y-M-D would emit "w30 Fri 2009-07-24" + yMD.HN would emit "110124.2342" + m/d/Y H:N would emit "1/24/2011 10:42" + a D b Y H:N:S would emit "Mon 24 Jan 2011 11:19:42" +.fi .RS Undefined fields are put to their minimal valid values (1 for month and day and @@ -679,14 +651,10 @@ field that is set. Otherwise, they are set to the corresponding values of "now". For example: .RE -.RS -.RS -.br -8/1/2013 with m/d/Y implies August 1, 2013 at midnight (inferred) -.br -8/1 20:40 with m/d H:N implies August 1, 2013 (inferred) at 20:40 -.RE -.RE +.nf + 8/1/2013 with m/d/Y implies August 1, 2013 at midnight (inferred) + 8/1 20:40 with m/d H:N implies August 1, 2013 (inferred) at 20:40 +.fi .TP .B date.iso=1 @@ -780,28 +748,19 @@ Holidays are entered either directly in the .taskrc file or via an include file that is specified in .taskrc. For single-day holidays the name and the date is required to be given: -.RS -.RS -.br -holiday.towel.name=Day of the towel -.br -holiday.towel.date=20100525 -.RE -.RE +.nf + holiday.towel.name=Day of the towel + holiday.towel.date=20100525 +.fi For holidays that span a range of days (i.e. vacation), you can use a start date and an end date: -.RS -.RS -.br -holiday.sysadmin.name=System Administrator Appreciation Week -.br -holiday.sysadmin.start=20100730 -.br -holiday.sysadmin.end=20100805 -.RE -.RE +.nf + holiday.sysadmin.name=System Administrator Appreciation Week + holiday.sysadmin.start=20100730 + holiday.sysadmin.end=20100805 +.fi .RS Dates are to be entered according to the setting in the dateformat.holiday @@ -814,24 +773,17 @@ Easter (easter), Easter Monday (eastermonday), Ascension (ascension), Pentecost (pentecost). The date for these holidays is the given keyword: .RE -.RS -.RS -.br -holiday.eastersunday.name=Easter -.br -holiday.eastersunday.date=easter -.RE -.RE +.nf + holiday.eastersunday.name=Easter + holiday.eastersunday.date=easter +.fi Note that the Taskwarrior distribution contains example holiday files that can be included like this: -.RS -.RS -.br -include holidays.en-US.rc -.RE -.RE +.nf + include holidays.en-US.rc +.fi .SS DEPENDENCIES @@ -919,10 +871,9 @@ Task is deleted. .RS To disable a coloration rule for which there is a default, set the value to nothing, for example: -.RS -.B color.tagged= -.RE -.RE +.nf + color.tagged= +.fi .RS By default, colors produced by rules blend. This has the advantage of @@ -1257,30 +1208,23 @@ default.command=next Provides a default command that is run every time Taskwarrior is invoked with no arguments. For example, if set to: -.RS -.RS -default.command=project:foo list -.RE -.RE +.nf + default.command=project:foo list +.fi .RS then Taskwarrior will run the "project:foo list" command if no command is specified. This means that by merely typing .RE -.RS -.RS -$ task -.br -[task project:foo list] -.br -\& -.br -ID Project Pri Description - 1 foo H Design foo - 2 foo Build foo -.RE -.RE +.nf + $ task + [task project:foo list] + + ID Project Pri Description + 1 foo H Design foo + 2 foo Build foo +.fi .SS REPORTS @@ -1319,12 +1263,16 @@ specified by using the column ids post-fixed by a "+" for ascending sort order or a "-" for descending sort order. The sort IDs are separated by commas. For example: +.nf report.list.sort=due+,priority-,start.active-,project+ +.fi Additionally, after the "+" or "-", there can be a solidus "/" which indicates that there are breaks after the column values change. For example: +.nf report.minimal.sort=project+/,description+ +.fi This sort order now specifies that there is a listing break between each project. A listing break is simply a blank line, which provides a visual From f1460013be6f778d405ead51b77958302a15653c Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 24 Jun 2024 17:19:12 -0400 Subject: [PATCH 053/242] Include fewer deps (#3489) Update chrono, and include fewer deps Somewhere in the process of moving TaskChampion back to its own repo, Taskwarrior's `Cargo.toml` ended up containing all of its dependencies, unnecessarily. This included an old version of `chrono` (addressed in https://github.com/GothenburgBitFactory/taskchampion/pull/399). Removing the old version requirement from `Cargo.toml` results in using a newer version, addressing a (benign) security vulnerability. --- Cargo.toml | 22 +--------------------- xtask/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3d6494992..d4bbe3f5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,28 +11,8 @@ resolver = "2" # Cargo.toml's in the members with `foo.workspace = true`. [workspace.dependencies] anyhow = "1.0" -byteorder = "1.5" -cc = "1.0.73" -chrono = { version = "^0.4.22", features = ["serde"] } ffizz-header = "0.5" -flate2 = "1" -google-cloud-storage = { version = "0.15.0", default-features = false, features = ["rustls-tls", "auth"] } -lazy_static = "1" libc = "0.2.136" -log = "^0.4.17" pretty_assertions = "1" -proptest = "^1.4.0" -ring = "0.17" -rstest = "0.17" -rusqlite = { version = "0.29", features = ["bundled"] } -serde_json = "^1.0" -serde = { version = "^1.0.147", features = ["derive"] } -strum = "0.25" -strum_macros = "0.25" -tempfile = "3" -tokio = { version = "1", features = ["rt-multi-thread"] } -thiserror = "1.0" -ureq = { version = "^2.9.0", features = ["tls"] } -uuid = { version = "^1.8.0", features = ["serde", "v4"] } -url = { version = "2" } +regex = "^1.10.2" taskchampion = "0.5" diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index e21069966..7bf176d4c 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -4,6 +4,6 @@ version = "0.4.1" edition = "2021" [dependencies] -anyhow = "1.0" +anyhow.workspace = true taskchampion-lib = { path = "../src/tc/lib" } -regex = "^1.10.2" +regex.workspace = true From 9cd1b96e1f947e609236b2c0460c828f04a6b13e Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 26 Jun 2024 01:33:46 -0400 Subject: [PATCH 054/242] Remove support for the F4 format (#3494) This was only still used in tests. --- src/Task.cpp | 106 +-------------------------------------------- src/Task.h | 1 - test/t.test.cpp | 77 ++------------------------------ test/view.test.cpp | 54 ++++++++++++----------- 4 files changed, 34 insertions(+), 204 deletions(-) diff --git a/src/Task.cpp b/src/Task.cpp index cb9626b41..36d690e3d 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -622,75 +622,10 @@ bool Task::is_waiting () const } //////////////////////////////////////////////////////////////////////////////// -// Attempt an FF4 parse first, using Task::parse, and in the event of an error -// try a JSON parse, otherwise a legacy parse (currently no legacy formats are -// supported). -// -// Note that FF1, FF2 and FF3 are no longer supported. -// -// start --> [ --> Att --> ] --> end -// ^ | -// +-------+ -// +// Try a JSON parse. void Task::parse (const std::string& input) { - try - { - // File format version 4, from 2009-5-16 - now, v1.7.1+ - // This is the parse format tried first, because it is most used. - data.clear (); - - if (input[0] == '[') - { - // Not using Pig to parse here (which would be idiomatic), because we - // don't need to differentiate betwen utf-8 and normal characters. - // Pig's scanning the string can be expensive. - auto ending_bracket = input.find_last_of (']'); - if (ending_bracket != std::string::npos) - { - std::string line = input.substr(1, ending_bracket); - - if (line.length () == 0) - throw std::string ("Empty record in input."); - - Pig attLine (line); - std::string name; - std::string value; - while (!attLine.eos ()) - { - if (attLine.getUntilAscii (':', name) && - attLine.skip (':') && - attLine.getQuoted ('"', value)) - { -#ifdef PRODUCT_TASKWARRIOR - legacyAttributeMap (name); -#endif - - if (isAnnotationAttr (name)) - ++annotation_count; - - data[name] = decode (json::decode (value)); - } - - attLine.skip (' '); - } - - std::string remainder; - attLine.getRemainder (remainder); - if (remainder.length ()) - throw std::string ("Unrecognized characters at end of line."); - } - } - else if (input[0] == '{') - parseJSON (input); - else - throw std::string ("Record not recognized as format 4."); - } - - catch (const std::string&) - { - parseLegacy (input); - } + parseJSON (input); // for compatibility, include all tags in `tags` as `tag_..` attributes if (data.find ("tags") != data.end ()) { @@ -965,43 +900,6 @@ void Task::parseLegacy (const std::string& line) recalc_urgency = true; } -//////////////////////////////////////////////////////////////////////////////// -// The format is: -// -// [ : ... ] -// -std::string Task::composeF4 () const -{ - std::string ff4 = "["; - - bool first = true; - for (const auto& it : data) - { - // Orphans have no type, treat as string. - std::string type = Task::attributes[it.first]; - if (type == "") - type = "string"; - - // If there is a value. - if (it.second != "") - { - ff4 += (first ? "" : " "); - ff4 += it.first; - ff4 += ":\""; - if (type == "string") - ff4 += encode (json::encode (it.second)); - else - ff4 += it.second; - ff4 += '"'; - - first = false; - } - } - - ff4 += ']'; - return ff4; -} - //////////////////////////////////////////////////////////////////////////////// std::string Task::composeJSON (bool decorate /*= false*/) const { diff --git a/src/Task.h b/src/Task.h index eb7b2b71f..3f4e16e16 100644 --- a/src/Task.h +++ b/src/Task.h @@ -69,7 +69,6 @@ public: Task (tc::Task); void parse (const std::string&); - std::string composeF4 () const; std::string composeJSON (bool decorate = false) const; // Status values. diff --git a/test/t.test.cpp b/test/t.test.cpp index a6ea56268..63739c6da 100644 --- a/test/t.test.cpp +++ b/test/t.test.cpp @@ -32,7 +32,7 @@ //////////////////////////////////////////////////////////////////////////////// int main (int, char**) { - UnitTest test (49); + UnitTest test (48); Context context; Context::setContext(&context); @@ -50,77 +50,6 @@ int main (int, char**) test.is (Task::statusToText (Task::deleted), "deleted", "statusToText deleted"); test.is (Task::statusToText (Task::recurring), "recurring", "statusToText recurring"); - // Round-trip testing. - Task t3; - t3.set ("name", "value"); - std::string before = t3.composeF4 (); - t3.parse (before); - std::string after = t3.composeF4 (); - t3.parse (after); - after = t3.composeF4 (); - t3.parse (after); - after = t3.composeF4 (); - test.is (before, after, "Task::composeF4 -> parse round trip 4 iterations"); - - // Legacy Format 1 (no longer supported) - // [tags] [attributes] description\n - // X [tags] [attributes] description\n - std::string sample = "[tag1 tag2] [att1:value1 att2:value2] Description"; - sample = "X " - "[one two] " - "[att1:value1 att2:value2] " - "Description"; - bool good = true; - try { Task ff1 (sample); } catch (...) { good = false; } - test.notok (good, "Support for ff1 removed"); - - // Legacy Format 2 (no longer supported) - // uuid status [tags] [attributes] description\n - sample = "00000000-0000-0000-0000-000000000000 " - "- " - "[tag1 tag2] " - "[att1:value1 att2:value2] " - "Description"; - good = true; - try { Task ff2 (sample); } catch (...) { good = false; } - test.notok (good, "Support for ff2 removed"); - - // Legacy Format 3 - // uuid status [tags] [attributes] [annotations] description\n - sample = "00000000-0000-0000-0000-000000000000 " - "- " - "[tag1 tag2] " - "[att1:value1 att2:value2] " - "[123:ann1 456:ann2] Description"; - good = true; - try { Task ff3 (sample); } catch (...) { good = false; } - test.notok (good, "Support for ff3 removed"); - - // Current Format 4 - // [name:"value" ...]\n - sample = "[" - "uuid:\"00000000-0000-0000-0000-000000000000\" " - "status:\"pending\" " - "tags:\"tag1,tag2\" " - "att1:\"value1\" " - "att2:\"value2\" " - "description:\"Description\"" - "]"; - Task ff4 (sample); - std::string value = ff4.get ("uuid"); - test.is (value, "00000000-0000-0000-0000-000000000000", "ff4 uuid"); - value = ff4.get ("status"); - test.is (value, "pending", "ff4 status"); - test.ok (ff4.hasTag ("tag1"), "ff4 tag1"); - test.ok (ff4.hasTag ("tag2"), "ff4 tag2"); - test.is (ff4.getTagCount (), 2, "ff4 # tags"); - value = ff4.get ("att1"); - test.is (value, "value1", "ff4 att1"); - value = ff4.get ("att2"); - test.is (value, "value2", "ff4 att2"); - value = ff4.get ("description"); - test.is (value, "Description", "ff4 description"); - /* TODO Task::composeCSV @@ -145,7 +74,7 @@ TODO Task::decode */ // Task::operator== - Task left ("[one:1 two:2 three:3]"); + Task left ("{\"one\":\"1\", \"two\":\"2\", \"three\":\"3\"}"); Task right (left); test.ok (left == right, "left == right -> true"); left.set ("one", "1.0"); @@ -188,7 +117,7 @@ TODO Task::decode Task::attributes["tags"] = "string"; Task::attributes["uuid"] = "string"; - good = true; + bool good = true; try {Task t4 ("{}");} catch (const std::string& e){test.diag (e); good = false;} test.ok (good, "Task::Task ('{}')"); diff --git a/test/view.test.cpp b/test/view.test.cpp index 7c0e4f500..c65045c5d 100644 --- a/test/view.test.cpp +++ b/test/view.test.cpp @@ -63,34 +63,38 @@ int main (int, char**) context.config.set ("indent.annotation", "2"); // Two sample tasks. - Task t1 ("[" - "status:\"pending\" " - "uuid:\"2a64f6e0-bf8e-430d-bf71-9ec3f0d9b661\" " - "description:\"This is the description text\" " - "project:\"Home\" " - "priority:\"H\" " - "annotation_1234567890:\"This is an annotation\" " - "start:\"1234567890\" " - "due:\"1234567890\" " - "tags:\"one,two\"" - "]"); + t.ok(true, "zero"); + Task t1 ("{" + "\"status\":\"pending\", " + "\"uuid\":\"2a64f6e0-bf8e-430d-bf71-9ec3f0d9b661\", " + "\"description\":\"This is the description text\", " + "\"project\":\"Home\", " + "\"priority\":\"H\", " + "\"annotation_1234567890\":\"This is an annotation\", " + "\"start\":\"1234567890\", " + "\"due\":\"1234567890\", " + "\"tags\":\"one,two\"" + "}"); t1.id = 1; - Task t2 ("[" - "status:\"pending\" " - "uuid:\"f30cb9c3-3fc0-483f-bfb2-3bf134f00694\" " - "description:\"This is the description text\" " - "project:\"Garden Care\" " - "recur:\"monthly\" " - "depends:\"2a64f6e0-bf8e-430d-bf71-9ec3f0d9b661\"" - "]"); + t.ok(true, "one"); + Task t2 ("{" + "\"status\":\"pending\", " + "\"uuid\":\"f30cb9c3-3fc0-483f-bfb2-3bf134f00694\", " + "\"description\":\"This is the description text\", " + "\"project\":\"Garden Care\", " + "\"recur\":\"monthly\", " + "\"depends\":\"2a64f6e0-bf8e-430d-bf71-9ec3f0d9b661\"" + "}"); t2.id = 11; - Task t3 ("[" - "status:\"pending\" " - "uuid:\"c44cb9c3-3fc0-483f-bfb2-3bf134f05554\" " - "description:\"Another description\" " - "project:\"Garden\" " - "]"); + t.ok(true, "two"); + Task t3 ("{" + "\"status\":\"pending\", " + "\"uuid\":\"c44cb9c3-3fc0-483f-bfb2-3bf134f05554\", " + "\"description\":\"Another description\", " + "\"project\":\"Garden\"" + "}"); t3.id = 8; + t.ok(true, "three"); std::vector data; data.push_back (t1); From 750af261aa1d38aa9cca457adf30aad145168b4a Mon Sep 17 00:00:00 2001 From: Sebastian Carlos <88276600+sebastiancarlos@users.noreply.github.com> Date: Wed, 26 Jun 2024 23:29:26 -0300 Subject: [PATCH 055/242] Un-deprecate the non-1/0 boolean values (#3522) As per https://github.com/GothenburgBitFactory/tw.org/issues/867 Co-authored-by: Sebastian Carlos --- doc/man/taskrc.5.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 8926d61f5..9f560f005 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -77,7 +77,7 @@ Values support UTF8 as well as JSON encoding, such as \\uNNNN. Note that Taskwarrior is flexible about the values used to represent Boolean items. You can use "1" to enable, anything else is interpreted as disabled. -The values "on", "yes", "y" and "true" are currently supported but deprecated. +The values "on", "yes", "y" and "true" are also supported. .nf include From 64609a04079597ac4efe37faf645a22f4298f43d Mon Sep 17 00:00:00 2001 From: Sebastian Carlos <88276600+sebastiancarlos@users.noreply.github.com> Date: Fri, 28 Jun 2024 19:02:37 -0300 Subject: [PATCH 056/242] Consistently exclude `WAITING` tasks from reports which filter by status (#3525) Consistently exclude WAITING tasks from reports which filter by status If I understand correctly, the virtual status "waiting" is deprecated. This is why some reports include the explicit "-WAITING" filter even though it is not necessary: The idea is that being explicit will be needed in the future when the "waiting" status is removed. But I noticed that some reports do not include the "-WAITING" filter, so I added it to all reports that filter by status. --- src/Context.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Context.cpp b/src/Context.cpp index c11e63632..b7a25c8b0 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -319,49 +319,49 @@ std::string configurationDefaults = "report.minimal.description=Minimal details of tasks\n" "report.minimal.labels=ID,Project,Tags,Description\n" "report.minimal.columns=id,project,tags.count,description.count\n" - "report.minimal.filter=status:pending\n" + "report.minimal.filter=status:pending -WAITING\n" "report.minimal.sort=project+/,description+\n" "report.minimal.context=1\n" "\n" "report.newest.description=Newest tasks\n" "report.newest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n" "report.newest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n" - "report.newest.filter=status:pending\n" + "report.newest.filter=status:pending -WAITING\n" "report.newest.sort=entry-\n" "report.newest.context=1\n" "\n" "report.oldest.description=Oldest tasks\n" "report.oldest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n" "report.oldest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n" - "report.oldest.filter=status:pending\n" + "report.oldest.filter=status:pending -WAITING\n" "report.oldest.sort=entry+\n" "report.oldest.context=1\n" "\n" "report.overdue.description=Overdue tasks\n" "report.overdue.labels=ID,Active,Age,Deps,P,Project,Tag,R,S,Due,Until,Description,Urg\n" "report.overdue.columns=id,start.age,entry.age,depends,priority,project,tags,recur.indicator,scheduled.countdown,due,until,description,urgency\n" - "report.overdue.filter=status:pending and +OVERDUE\n" + "report.overdue.filter=status:pending -WAITING +OVERDUE\n" "report.overdue.sort=urgency-,due+\n" "report.overdue.context=1\n" "\n" "report.active.description=Active tasks\n" "report.active.labels=ID,Started,Active,Age,D,P,Project,Tags,Recur,W,Sch,Due,Until,Description\n" "report.active.columns=id,start,start.age,entry.age,depends.indicator,priority,project,tags,recur,wait,scheduled.remaining,due,until,description\n" - "report.active.filter=status:pending and +ACTIVE\n" + "report.active.filter=status:pending -WAITING +ACTIVE\n" "report.active.sort=project+,start+\n" "report.active.context=1\n" "\n" "report.completed.description=Completed tasks\n" "report.completed.labels=ID,UUID,Created,Completed,Age,Deps,P,Project,Tags,R,Due,Description\n" "report.completed.columns=id,uuid.short,entry,end,entry.age,depends,priority,project,tags,recur.indicator,due,description\n" - "report.completed.filter=status:completed\n" + "report.completed.filter=status:completed -WAITING \n" "report.completed.sort=end+\n" "report.completed.context=1\n" "\n" "report.recurring.description=Recurring Tasks\n" "report.recurring.labels=ID,Active,Age,D,P,Parent,Project,Tags,Recur,Sch,Due,Until,Description,Urg\n" "report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,parent.short,project,tags,recur,scheduled.countdown,due,until.remaining,description,urgency\n" - "report.recurring.filter=(status:pending and +CHILD) or (status:recurring and +PARENT)\n" + "report.recurring.filter=(status:pending -WAITING +CHILD) or (status:recurring -WAITING +PARENT)\n" "report.recurring.sort=due+,urgency-,entry+\n" "report.recurring.context=1\n" "\n" @@ -413,7 +413,7 @@ std::string configurationDefaults = "report.blocking.filter=status:pending -WAITING +BLOCKING\n" "report.blocking.context=1\n" "\n" - "report.timesheet.filter=(+PENDING and start.after:now-4wks) or (+COMPLETED and end.after:now-4wks)\n" + "report.timesheet.filter=(+PENDING -WAITING start.after:now-4wks) or (+COMPLETED -WAITING end.after:now-4wks)\n" "report.timesheet.context=0\n" "\n"; From 5ab51271b0aaab990accbd00213e37c4cb05d35d Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Sat, 29 Jun 2024 00:11:53 +0200 Subject: [PATCH 057/242] increase timout to 10s and run on ubuntu-latest (#3523) As identified in #3512 it seams as the problem was the internal timeout. Closes #3507. --- .github/workflows/tests.yaml | 2 +- test/basetest/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 72a871e2d..85e36b216 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -5,7 +5,7 @@ on: [push, pull_request] jobs: coverage: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - name: Install apt packages run: sudo apt-get install -y build-essential cmake git uuid-dev faketime locales python3 curl gcovr ninja-build diff --git a/test/basetest/utils.py b/test/basetest/utils.py index 65859a464..908ea1a0a 100644 --- a/test/basetest/utils.py +++ b/test/basetest/utils.py @@ -66,13 +66,13 @@ def binary_location(cmd, USE_PATH=False): return os.path.join(BIN_PREFIX, cmd) -def wait_condition(cond, timeout=1, sleeptime=.01): +def wait_condition(cond, timeout=10, sleeptime=.01): """Wait for condition to return anything other than None """ # NOTE Increasing sleeptime can dramatically increase testsuite runtime # It also reduces CPU load significantly if timeout is None: - timeout = 1 + timeout = 10 if timeout < sleeptime: print("Warning, timeout cannot be smaller than", sleeptime) From b70d8ef574e53c548b2839e059d9c28240b1564a Mon Sep 17 00:00:00 2001 From: Sebastian Carlos <88276600+sebastiancarlos@users.noreply.github.com> Date: Fri, 28 Jun 2024 19:12:10 -0300 Subject: [PATCH 058/242] Remove `locking` config (#3519) This flag is no longer needed. It was used to control file locking on the *.data files. Co-authored-by: Sebastian Carlos --- doc/man/taskrc.5.in | 8 -------- scripts/hooks/on-exit.shadow-file | 3 +-- src/Context.cpp | 1 - src/commands/CmdDiagnostics.cpp | 6 ------ src/commands/CmdShow.cpp | 1 - test/diag.test.py | 1 - 6 files changed, 1 insertion(+), 19 deletions(-) diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 9f560f005..266d276bb 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -209,14 +209,6 @@ Note that the TASKDATA environment variable overrides this setting. .B hooks.location=$HOME/.task/hooks This is a path to the hook scripts directory. By default it is ~/.task/hooks. -.TP -.B locking=1 -Determines whether to use file locking when accessing the database. Defaults to -"1". Solaris users who store the database on an NFS mount may need to set -locking to "0". Note that there is danger in setting this value to "0" - -another program (or another instance of task) may write to the database at the -same time. - .TP .B gc=1 Can be used to temporarily suspend garbage collection (gc), so that task IDs diff --git a/scripts/hooks/on-exit.shadow-file b/scripts/hooks/on-exit.shadow-file index 9942d2633..3434c83f5 100755 --- a/scripts/hooks/on-exit.shadow-file +++ b/scripts/hooks/on-exit.shadow-file @@ -22,9 +22,8 @@ SHADOW_FILE=$(task _get rc.shadow.file) # rc.detection=off Disables terminal size detection # rc.gc=off Disables GC, thus not changing IDs unexpectedly # rc.color=off Disable color in the shadow file -# rc.locking=off Disable file locking, to prevent race condition # rc.hooks=off Disable hooks, to prevent race condition -task $SHADOW_COMMAND rc.detection=off rc.gc=off rc.color=off rc.locking=off rc.hooks=off > $SHADOW_FILE 2>/dev/null +task $SHADOW_COMMAND rc.detection=off rc.gc=off rc.color=off rc.hooks=off > $SHADOW_FILE 2>/dev/null if [[ $? != 0 ]] then echo Could not create $SHADOW_FILE diff --git a/src/Context.cpp b/src/Context.cpp index b7a25c8b0..47604af60 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -78,7 +78,6 @@ std::string configurationDefaults = "\n" "# Files\n" "data.location=~/.task\n" - "locking=1 # Use file-level locking\n" "gc=1 # Garbage-collect data files - DO NOT CHANGE unless you are sure\n" "exit.on.missing.db=0 # Whether to exit if ~/.task is not found\n" "hooks=1 # Master control switch for hooks\n" diff --git a/src/commands/CmdDiagnostics.cpp b/src/commands/CmdDiagnostics.cpp index 6d274bfc3..e1fe06a99 100644 --- a/src/commands/CmdDiagnostics.cpp +++ b/src/commands/CmdDiagnostics.cpp @@ -177,12 +177,6 @@ int CmdDiagnostics::execute (std::string& output) << env << '\n'; - out << " Locking: " - << (Context::getContext ().config.getBoolean ("locking") - ? "Enabled" - : "Disabled") - << '\n'; - out << " GC: " << (Context::getContext ().config.getBoolean ("gc") ? "Enabled" diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index 04456840c..2e6c3cd7f 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -176,7 +176,6 @@ int CmdShow::execute (std::string& output) " limit" " list.all.projects" " list.all.tags" - " locking" " nag" " news.version" " obfuscate" diff --git a/test/diag.test.py b/test/diag.test.py index 85d781df9..4fae86ce2 100755 --- a/test/diag.test.py +++ b/test/diag.test.py @@ -52,7 +52,6 @@ class TestDiagnostics(TestCase): self.tap(out) self.assertRegex(out, r"Compliance:\s+C\+\+17") self.assertIn("edlin", out) - self.assertIn("Locking", out) def test_64bit_time_t(self): """Test that time_t has size of 64 bits""" From 1b81813223e523d7c358f9fefc826048e2677f92 Mon Sep 17 00:00:00 2001 From: Sebastian Carlos <88276600+sebastiancarlos@users.noreply.github.com> Date: Fri, 28 Jun 2024 19:13:02 -0300 Subject: [PATCH 059/242] Clarify relation between the DUE tag and the rc.due setting in task(1) (#3526) Also, clarify that DUE is based on a number of days since the current date. Co-authored-by: Sebastian Carlos --- doc/man/task.1.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/man/task.1.in b/doc/man/task.1.in index e26eafebc..ae982b53b 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -782,7 +782,7 @@ are: CHILD Matches if the task has a parent (deprecated in 2.6.0) COMPLETED Matches if the task has completed status DELETED Matches if the task has deleted status - DUE Matches if the task is due + DUE Matches if the task is due within the next 7 days (See rc.due) INSTANCE Matches if the task is a recurrent instance LATEST Matches if the task is the newest added task MONTH Matches if the task is due this month From 9372c988fa2945e7bfca7513253140ad16fcb66e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 09:37:08 -0400 Subject: [PATCH 060/242] Bump docker/build-push-action from 6.0.0 to 6.2.0 (#3529) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.0.0 to 6.2.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.0.0...v6.2.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 7119397a4..a1865d1e5 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -40,7 +40,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.0.0 + uses: docker/build-push-action@v6.2.0 with: context: . file: "./docker/task.dockerfile" From eb22036f6bc34ea4328c882f110f04647e6b951f Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Thu, 4 Jul 2024 08:45:11 +0200 Subject: [PATCH 061/242] Update to taskchampion 0.6 (#3531) * update dependency to taskchampion 0.6 * change from origin to url in remote server --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- src/tc/lib/src/server.rs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c717d2849..d3d6f3b81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1436,9 +1436,9 @@ dependencies = [ [[package]] name = "taskchampion" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9e2d64086cc515f801ba0e1f366e5e029dd47db3c499a9e173a60b62145410" +checksum = "55b167a2bea718f6f75f68c8d29f1550a6095d8917504d3b9c62626f4c4ef7cb" dependencies = [ "anyhow", "byteorder", @@ -1715,9 +1715,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "uuid" -version = "1.8.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" dependencies = [ "getrandom", "serde", diff --git a/Cargo.toml b/Cargo.toml index d4bbe3f5c..b8e486d6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,4 @@ ffizz-header = "0.5" libc = "0.2.136" pretty_assertions = "1" regex = "^1.10.2" -taskchampion = "0.5" +taskchampion = "0.6" diff --git a/src/tc/lib/src/server.rs b/src/tc/lib/src/server.rs index 47b79debc..023782a9c 100644 --- a/src/tc/lib/src/server.rs +++ b/src/tc/lib/src/server.rs @@ -33,7 +33,7 @@ impl AsMut> for TCServer { } } -/// Utility function to allow using `?` notation to return an error value. +/// Utility function to allow using `?` notation to return an error value. fn wrap(f: F, error_out: *mut TCString, err_value: T) -> T where F: FnOnce() -> anyhow::Result, @@ -125,7 +125,7 @@ pub unsafe extern "C" fn tc_server_new_sync( // SAFETY: // - origin is valid (promised by caller) // - origin ownership is transferred to this function - let origin = unsafe { TCString::val_from_arg(origin) }.into_string()?; + let url = unsafe { TCString::val_from_arg(origin) }.into_string()?; // SAFETY: // - client_id is a valid Uuid (any 8-byte sequence counts) @@ -139,7 +139,7 @@ pub unsafe extern "C" fn tc_server_new_sync( .to_vec(); let server_config = ServerConfig::Remote { - origin, + url, client_id, encryption_secret, }; From 85f52e3630e599c324410ddac5b4cfabe4445f2b Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Thu, 4 Jul 2024 08:45:42 +0200 Subject: [PATCH 062/242] Update performance scripts (#3532) * update performance script to work with out-of-source build * update displayed messages and remove perf.rc file * remove .gitignore in performance folder --- CMakeLists.txt | 4 ++-- performance/.gitignore | 3 --- performance/CMakeLists.txt | 11 ++++++++--- performance/load | 8 ++++---- performance/run_perf | 16 ++++++++-------- 5 files changed, 22 insertions(+), 20 deletions(-) delete mode 100644 performance/.gitignore diff --git a/CMakeLists.txt b/CMakeLists.txt index a96ccaf81..0c1187509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,9 +149,9 @@ add_subdirectory (scripts) if (EXISTS ${CMAKE_SOURCE_DIR}/test) add_subdirectory (test EXCLUDE_FROM_ALL) endif (EXISTS ${CMAKE_SOURCE_DIR}/test) -if (EXISTS performance) +if (EXISTS ${CMAKE_SOURCE_DIR}/performance) add_subdirectory (performance EXCLUDE_FROM_ALL) -endif (EXISTS performance) +endif (EXISTS ${CMAKE_SOURCE_DIR}/performance) set (doc_FILES ChangeLog README.md INSTALL AUTHORS COPYING LICENSE) foreach (doc_FILE ${doc_FILES}) diff --git a/performance/.gitignore b/performance/.gitignore deleted file mode 100644 index e82c8156a..000000000 --- a/performance/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.data -*.rc -export.json diff --git a/performance/CMakeLists.txt b/performance/CMakeLists.txt index adc77d414..293c5b942 100644 --- a/performance/CMakeLists.txt +++ b/performance/CMakeLists.txt @@ -1,6 +1,11 @@ cmake_minimum_required (VERSION 3.22) -add_custom_target (performance ./run_perf - DEPENDS task_executable - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/performance) +configure_file(compare_runs.py compare_runs.py COPYONLY) +configure_file(load load) +configure_file(run_perf run_perf) + +add_custom_target (performance ${CMAKE_BINARY_DIR}/performance/run_perf + DEPENDS task_executable + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/performance) + diff --git a/performance/load b/performance/load index 7f6dded6d..56df91f35 100755 --- a/performance/load +++ b/performance/load @@ -14,7 +14,7 @@ if (open my $fh, '>', 'perf.rc') close $fh; } -my $filename = 'sample-text.txt'; +my $filename = '${CMAKE_SOURCE_DIR}/performance/sample-text.txt'; open(my $fh, '<:encoding(UTF-8)', $filename) or die "Could not open file '$filename' $!"; @@ -31,18 +31,18 @@ while (my $line = <$fh>) if ($. % 20 == 19) { my $anno_id = $id - 1; - qx{../build/src/task rc:perf.rc rc.gc=off $anno_id annotate $line}; + qx{${CMAKE_BINARY_DIR}/src/task rc:perf.rc rc.gc=off $anno_id annotate $line}; print "[$.] task rc:perf.rc rc.gc=off $anno_id annotate $line\n" if $?; } elsif ($. % 4 == 1) { - qx{../build/src/task rc:perf.rc rc.gc=off add $line}; + qx{${CMAKE_BINARY_DIR}/src/task rc:perf.rc rc.gc=off add $line}; print "[$.] task rc:perf.rc rc.gc=off add $line\n" if $?; ++$id; } else { - qx{../build/src/task rc:perf.rc rc.gc=off log $line}; + qx{${CMAKE_BINARY_DIR}/src/task rc:perf.rc rc.gc=off log $line}; print "[$.] task rc:perf.rc rc.gc=off log $line\n" if $?; } } diff --git a/performance/run_perf b/performance/run_perf index 869640b63..8d925efb9 100755 --- a/performance/run_perf +++ b/performance/run_perf @@ -1,22 +1,22 @@ #! /bin/bash echo 'Performance: setup' -rm -f ./pending.data ./completed.data ./undo.data ./backlog.data perf.rc -if [[ -e data/pending.data && -e data/completed.data ]] +rm -f ./taskchampion.sqlite3 +if [[ -e ./data/taskchampion.sqlite3 ]] then - echo ' - Using existing data' + echo ' - Using existing data.' cp data/* . else - echo ' - This step will take several minutes' + echo ' - Loading data. This step will take several minutes.' ./load mkdir -p data - cp *.data perf.rc data + cp taskchampion.sqlite3 perf.rc data fi # Allow override. if [[ -z $TASK ]] then - TASK=../build/src/task + TASK=${CMAKE_BINARY_DIR}/src/task fi # Run benchmarks. @@ -45,8 +45,8 @@ $TASK rc.debug:1 rc:perf.rc export >/dev/null 2>&1 $TASK rc.debug:1 rc:perf.rc export 2>&1 >export.json | grep "Perf task" echo ' - task import...' -rm -f ./pending.data ./completed.data ./undo.data ./backlog.data -$TASK rc.debug:1 rc:perf.rc import export.json 2>&1 | grep "Perf task" +rm -f ./taskchampion.sqlite3 +$TASK rc.debug:1 rc:perf.rc import ${CMAKE_SOURCE_DIR}/performance/export.json 2>&1 | grep "Perf task" echo 'End' exit 0 From fa5604ea8d7bfbb5e7ed14040405409c3ac75e5a Mon Sep 17 00:00:00 2001 From: Hector Dearman Date: Fri, 5 Jul 2024 00:17:52 +0100 Subject: [PATCH 063/242] Relax sync.server.origin to allow paths (#3423) --- doc/man/task-sync.5.in | 12 ++++++++---- scripts/vim/syntax/taskrc.vim | 2 +- src/Context.cpp | 4 ++-- src/commands/CmdShow.cpp | 1 + src/commands/CmdSync.cpp | 18 ++++++++++++++---- src/tc/Server.cpp | 10 +++++----- src/tc/Server.h | 2 +- src/tc/lib/src/server.rs | 10 +++++----- src/tc/lib/taskchampion.h | 2 +- 9 files changed, 38 insertions(+), 23 deletions(-) diff --git a/doc/man/task-sync.5.in b/doc/man/task-sync.5.in index c8d7c5a53..d3998c957 100644 --- a/doc/man/task-sync.5.in +++ b/doc/man/task-sync.5.in @@ -68,18 +68,22 @@ To synchronize your tasks to a sync server, you will need the following information from the server administrator: .br - - The server's URL ("origin", such as "https://tw.example.com") + - The server's URL (such as "https://tw.example.com/path") .br - A client ID ("client_id") identifying your tasks Configure Taskwarrior with these details: .nf - $ task config sync.server.origin + $ task config sync.server.url $ task config sync.server.client_id .fi -Note that the origin must include the scheme, such as 'http://' or 'https://'. +Note that the URL must include the scheme, such as 'http://' or 'https://'. + + $ task config sync.server.origin + +Is a deprecated synonym for "sync.server.url". .SS Google Cloud Platform @@ -168,7 +172,7 @@ To add a new user to the server, invent a new client ID with a tool like `uuidgen` or an online UUID generator. There is no need to configure the server for this new client ID: the sync server will automatically create a new user whenever presented with a new client ID. Supply the ID, along with the -origin, to the user for inclusion in their Taskwarrior config. The user should +URL, to the user for inclusion in their Taskwarrior config. The user should invent their own "encryption_secret". .SH AVOIDING DUPLICATE RECURRING TASKS diff --git a/scripts/vim/syntax/taskrc.vim b/scripts/vim/syntax/taskrc.vim index 8efddad9b..983cee93e 100644 --- a/scripts/vim/syntax/taskrc.vim +++ b/scripts/vim/syntax/taskrc.vim @@ -164,7 +164,7 @@ syn match taskrcGoodKey '^\s*\Vrule.precedence.color='he=e-1 syn match taskrcGoodKey '^\s*\Vsearch.case.sensitive='he=e-1 syn match taskrcGoodKey '^\s*\Vsummary.all.projects='he=e-1 syn match taskrcGoodKey '^\s*\Vsugar='he=e-1 -syn match taskrcGoodKey '^\s*\Vsync.\(server.\(origin\|client_id\|encryption_secret\)\|local.server_dir\)='he=e-1 +syn match taskrcGoodKey '^\s*\Vsync.\(server.\(url\|origin\|client_id\|encryption_secret\)\|local.server_dir\)='he=e-1 syn match taskrcGoodKey '^\s*\Vtag.indicator='he=e-1 syn match taskrcGoodKey '^\s*\Vuda.\S\{-}.\(default\|type\|label\|values\|indicator\)='he=e-1 syn match taskrcGoodKey '^\s*\Vundo.style='he=e-1 diff --git a/src/Context.cpp b/src/Context.cpp index 47604af60..14b3106e7 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -281,9 +281,9 @@ std::string configurationDefaults = "\n" "#sync.encryption_secret # Encryption secret for sync to a server\n" "#sync.server.client_id # Client ID for sync to a server\n" - "#sync.server.origin # Origin of the sync server\n" + "#sync.server.url # URL of the sync server\n" "#sync.local.server_dir # Directory for local sync\n" - "#sync.gcp.credential_path # Path to JSON file containing credentials to authenticate GCP Sync\n" + "#sync.gcp.credential_path # Path to JSON file containing credentials to authenticate GCP Sync\n" "#sync.gcp.bucket # Bucket for sync to GCP\n" "\n" "# Aliases - alternate names for commands\n" diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index 2e6c3cd7f..c120e7499 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -197,6 +197,7 @@ int CmdShow::execute (std::string& output) " sync.gcp.bucket" " sync.server.client_id" " sync.encryption_secret" + " sync.server.url" " sync.server.origin" " tag.indicator" " undo.style" diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index 5b0775fb3..0f117209c 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -63,10 +63,15 @@ int CmdSync::execute (std::string& output) // If no server is set up, quit. std::string origin = Context::getContext ().config.get ("sync.server.origin"); + std::string url = Context::getContext ().config.get ("sync.server.url"); std::string server_dir = Context::getContext ().config.get ("sync.local.server_dir"); std::string gcp_credential_path = Context::getContext ().config.get ("sync.gcp.credential_path"); std::string gcp_bucket = Context::getContext ().config.get ("sync.gcp.bucket"); std::string encryption_secret = Context::getContext ().config.get ("sync.encryption_secret"); + + // sync.server.origin is a deprecated synonym for sync.server.url + std::string server_url = url == "" ? origin : url; + if (server_dir != "") { server = tc::Server::new_local (server_dir); server_ident = server_dir; @@ -78,23 +83,28 @@ int CmdSync::execute (std::string& output) std::ostringstream os; os << "GCP bucket " << gcp_bucket; server_ident = os.str(); - } else if (origin != "") { + } else if (server_url != "") { std::string client_id = Context::getContext ().config.get ("sync.server.client_id"); if (client_id == "" || encryption_secret == "") { throw std::string ("sync.server.client_id and sync.encryption_secret are required"); } - server = tc::Server::new_sync (origin, client_id, encryption_secret); + server = tc::Server::new_sync (server_url, client_id, encryption_secret); std::ostringstream os; - os << "Sync server at " << origin; + os << "Sync server at " << server_url; server_ident = os.str(); } else { throw std::string ("No sync.* settings are configured. See task-sync(5)."); } std::stringstream out; - if (Context::getContext ().verbose ("sync")) + if (origin != "") { + out << "sync.server.origin is deprecated. Use sync.server.url instead.\n"; + } + + if (Context::getContext ().verbose ("sync")) { out << format ("Syncing with {1}", server_ident) << '\n'; + } Context::getContext ().tdb2.sync(std::move(server), false); diff --git a/src/tc/Server.cpp b/src/tc/Server.cpp index 9d595e901..fe4717afd 100644 --- a/src/tc/Server.cpp +++ b/src/tc/Server.cpp @@ -51,24 +51,24 @@ tc::Server::new_local (const std::string &server_dir) //////////////////////////////////////////////////////////////////////////////// tc::Server -tc::Server::new_sync (const std::string &origin, const std::string &client_id, const std::string &encryption_secret) +tc::Server::new_sync (const std::string &url, const std::string &client_id, const std::string &encryption_secret) { - TCString tc_origin = tc_string_borrow (origin.c_str ()); + TCString tc_url = tc_string_borrow (url.c_str ()); TCString tc_client_id = tc_string_borrow (client_id.c_str ()); TCString tc_encryption_secret = tc_string_borrow (encryption_secret.c_str ()); TCUuid tc_client_uuid; if (tc_uuid_from_str(tc_client_id, &tc_client_uuid) != TC_RESULT_OK) { - tc_string_free(&tc_origin); + tc_string_free(&tc_url); tc_string_free(&tc_encryption_secret); throw format ("client_id '{1}' is not a valid UUID", client_id); } TCString error; - auto tcserver = tc_server_new_sync (tc_origin, tc_client_uuid, tc_encryption_secret, &error); + auto tcserver = tc_server_new_sync (tc_url, tc_client_uuid, tc_encryption_secret, &error); if (!tcserver) { std::string errmsg = format ("Could not configure connection to server at {1}: {2}", - origin, tc_string_content (&error)); + url, tc_string_content (&error)); tc_string_free (&error); throw errmsg; } diff --git a/src/tc/Server.h b/src/tc/Server.h index 08700c4bc..bc5266b4d 100644 --- a/src/tc/Server.h +++ b/src/tc/Server.h @@ -54,7 +54,7 @@ namespace tc { static Server new_local (const std::string& server_dir); // Construct a remote server (tc_server_new_sync). - static Server new_sync (const std::string &origin, const std::string &client_id, const std::string &encryption_secret); + static Server new_sync (const std::string &url, const std::string &client_id, const std::string &encryption_secret); // Construct a GCP server (tc_server_new_gcp). static Server new_gcp (const std::string &bucket, const std::string &credential_path, const std::string &encryption_secret); diff --git a/src/tc/lib/src/server.rs b/src/tc/lib/src/server.rs index 023782a9c..4b3ffc3b9 100644 --- a/src/tc/lib/src/server.rs +++ b/src/tc/lib/src/server.rs @@ -108,14 +108,14 @@ pub unsafe extern "C" fn tc_server_new_local( /// The server must be freed after it is used - tc_replica_sync does not automatically free it. /// /// ```c -/// EXTERN_C struct TCServer *tc_server_new_sync(struct TCString origin, +/// EXTERN_C struct TCServer *tc_server_new_sync(struct TCString url, /// struct TCUuid client_id, /// struct TCString encryption_secret, /// struct TCString *error_out); /// ``` #[no_mangle] pub unsafe extern "C" fn tc_server_new_sync( - origin: TCString, + url: TCString, client_id: TCUuid, encryption_secret: TCString, error_out: *mut TCString, @@ -123,9 +123,9 @@ pub unsafe extern "C" fn tc_server_new_sync( wrap( || { // SAFETY: - // - origin is valid (promised by caller) - // - origin ownership is transferred to this function - let url = unsafe { TCString::val_from_arg(origin) }.into_string()?; + // - url is valid (promised by caller) + // - url ownership is transferred to this function + let url = unsafe { TCString::val_from_arg(url) }.into_string()?; // SAFETY: // - client_id is a valid Uuid (any 8-byte sequence counts) diff --git a/src/tc/lib/taskchampion.h b/src/tc/lib/taskchampion.h index 2f3237ab2..dfbe1ba7d 100644 --- a/src/tc/lib/taskchampion.h +++ b/src/tc/lib/taskchampion.h @@ -433,7 +433,7 @@ EXTERN_C struct TCServer *tc_server_new_local(struct TCString server_dir, struct // returned. The caller must free this string. // // The server must be freed after it is used - tc_replica_sync does not automatically free it. -EXTERN_C struct TCServer *tc_server_new_sync(struct TCString origin, +EXTERN_C struct TCServer *tc_server_new_sync(struct TCString url, struct TCUuid client_id, struct TCString encryption_secret, struct TCString *error_out); From d1a3573c5f65d91181e61a394f8a9bab2c3a890b Mon Sep 17 00:00:00 2001 From: Sebastian Carlos <88276600+sebastiancarlos@users.noreply.github.com> Date: Thu, 4 Jul 2024 23:32:22 -0300 Subject: [PATCH 064/242] Replace "gc" with "rebuild" in man pages. (#3533) Replace "gc" with "rebuild" in man page. Also, explain IDs in the context of the working set of tasks. --- doc/man/task.1.in | 11 ++++++----- doc/man/taskrc.5.in | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/doc/man/task.1.in b/doc/man/task.1.in index ae982b53b..9a0444cc3 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -744,11 +744,12 @@ the online documentation at: .TP .B ID -Tasks can be specified uniquely by IDs, which are simply the indexes of the -tasks in the database. The ID of a task may therefore change, but only when -a command is run that displays IDs. When modifying tasks, it is safe to -rely on the last displayed ID. Always run a report to check you have the right -ID for a task. IDs can be given to task as a sequence, for example: +Tasks can be specified uniquely by IDs, which are the indexes of the "working +set" of tasks (mostly pending and recurrent tasks). The ID of a task may +therefore change, but only when a report that displays IDs is run. When +modifying tasks, it is safe to rely on the last displayed ID. Always run a +report to check you have the right ID for a task. IDs can be given to task as a +sequence, for example: .nf task 1,4-10,19 delete diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 266d276bb..1a764af43 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -211,10 +211,10 @@ This is a path to the hook scripts directory. By default it is ~/.task/hooks. .TP .B gc=1 -Can be used to temporarily suspend garbage collection (gc), so that task IDs -don't change. Note that this should be used in the form of a command line -override (task rc.gc=0 ...), and not permanently used in the .taskrc file, -as this significantly affects performance in the long term. +Can be used to temporarily suspend rebuilding, so that task IDs don't change. +Note that this should be used in the form of a command line override (task +rc.gc=0 ...), and not permanently used in the .taskrc file, as this +significantly affects performance in the long term. .TP .B hooks=1 From 6d3519419e0b69a721a48679acee3c7a54a04faa Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 7 Jul 2024 08:51:09 -0400 Subject: [PATCH 065/242] Do not create recurring tasks before today (#3542) Tasks can be due "today", as `task add foo due:today ..` is a common form. However, recurrences before that are just not created. This avoids a lengthy "hang" when recurrences are updated on an old task database, as many tasks in the past are created. --- src/recur.cpp | 10 ++++-- test/recurrence.test.py | 67 +++++++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/recur.cpp b/src/recur.cpp index 524c912d3..0c4220ff3 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -135,9 +135,10 @@ void handleRecurrence () //////////////////////////////////////////////////////////////////////////////// // Determine a start date (due), an optional end date (until), and an increment -// period (recur). Then generate a set of corresponding dates. +// period (recur). Then generate a set of corresponding dates. Only recurrences +// in the future are returned; see #3501. // -// Returns false if the parent recurring task is depleted. +// Returns false if the parent recurring task is deleted. bool generateDueDates (Task& parent, std::vector & allDue) { // Determine due date, recur period and until date. @@ -158,9 +159,12 @@ bool generateDueDates (Task& parent, std::vector & allDue) auto recurrence_limit = Context::getContext ().config.getInteger ("recurrence.limit"); int recurrence_counter = 0; Datetime now; + Datetime today = now.startOfDay(); for (Datetime i = due; ; i = getNextRecurrence (i, recur)) { - allDue.push_back (i); + // Do not add tasks before today (but allow today for the common `due:today` form). + if (i >= today) + allDue.push_back (i); if (specificEnd && i > until) { diff --git a/test/recurrence.test.py b/test/recurrence.test.py index 2a5c4e248..313b9e803 100755 --- a/test/recurrence.test.py +++ b/test/recurrence.test.py @@ -289,6 +289,7 @@ class TestBug932(TestCase): self.t = Task() self.t.config("report.id_pri_proj.columns", "id,priority,project") self.t.config("report.id_pri_proj.labels", "ID,P,Proj") + self.t.config("recurrence.limit", "3") def test_modify_due_propagate(self): """932: Verify due date modifications propagate""" @@ -296,7 +297,7 @@ class TestBug932(TestCase): # add a recurring task with multiple child tasks # - modify a child task and test for propagation # - modify the parent task and test for propagation - self.t("add R due:yesterday recur:daily") + self.t("add R due:tomorrow recur:daily") self.t("list") # GC/handleRecurrence self.t("2 modify project:P", input="y\n") @@ -316,7 +317,8 @@ class TestBug955(TestCase): def setUp(self): self.t = Task() - self.t("add foo due:now recur:1day") + self.t.config("recurrence.limit", "2") + self.t("add foo due:tomorrow recur:1day") code, out, err = self.t("ls") self.assertRegex(out, re.compile("^2 tasks", re.MULTILINE)) @@ -612,47 +614,52 @@ class TestBugAnnual(TestCase): def test_annual_creep(self): """Verify 'annual' recurring tasks don't creep""" + self.t.config("recurrence.limit", "17") self.t.config("dateformat", "YMD") self.t.config("report.annual.labels", "ID,Due") self.t.config("report.annual.columns", "id,due") self.t.config("report.annual.filter", "status:pending") self.t.config("report.annual.sort", "due+") + this_year = time.gmtime(time.time())[0] + def jan1(year_offset): + return f"{this_year+year_offset}0101" + # If a task is added with a due date ten years ago, with an annual recurrence, # then the synthetic tasks in between then and now have a due date that creeps. # # ID Due Description # -- ---------- ----------- - # 4 1/1/2002 foo - # 5 1/1/2003 foo - # 6 1/1/2004 foo - # 7 1/1/2005 foo - # 8 1/1/2006 foo - # 9 1/1/2007 foo - # 10 1/1/2008 foo - # 11 1/1/2009 foo - # 12 1/1/2010 foo - # 2 1/1/2000 foo - # 3 1/1/2001 foo + # 4 1/1/2102 foo + # 5 1/1/2103 foo + # 6 1/1/2104 foo + # 7 1/1/2105 foo + # 8 1/1/2106 foo + # 9 1/1/2107 foo + # 10 1/1/2108 foo + # 11 1/1/2109 foo + # 12 1/1/2110 foo + # 2 1/1/2100 foo + # 3 1/1/2101 foo - self.t("add foo due:20000101 recur:annual until:20150101") + self.t(f"add foo due:{jan1(1)} recur:annual until:{jan1(16)}") code, out, err = self.t("annual") - self.assertIn(" 2 20000101", out) - self.assertIn(" 3 20010101", out) - self.assertIn(" 4 20020101", out) - self.assertIn(" 5 20030101", out) - self.assertIn(" 6 20040101", out) - self.assertIn(" 7 20050101", out) - self.assertIn(" 8 20060101", out) - self.assertIn(" 9 20070101", out) - self.assertIn("10 20080101", out) - self.assertIn("11 20090101", out) - self.assertIn("12 20100101", out) - self.assertIn("13 20110101", out) - self.assertIn("14 20120101", out) - self.assertIn("15 20130101", out) - self.assertIn("16 20140101", out) - self.assertIn("17 20150101", out) + self.assertIn(f" 2 {jan1(1)}", out) + self.assertIn(f" 3 {jan1(2)}", out) + self.assertIn(f" 4 {jan1(3)}", out) + self.assertIn(f" 5 {jan1(4)}", out) + self.assertIn(f" 6 {jan1(5)}", out) + self.assertIn(f" 7 {jan1(6)}", out) + self.assertIn(f" 8 {jan1(7)}", out) + self.assertIn(f" 9 {jan1(8)}", out) + self.assertIn(f"10 {jan1(9)}", out) + self.assertIn(f"11 {jan1(10)}", out) + self.assertIn(f"12 {jan1(11)}", out) + self.assertIn(f"13 {jan1(12)}", out) + self.assertIn(f"14 {jan1(13)}", out) + self.assertIn(f"15 {jan1(14)}", out) + self.assertIn(f"16 {jan1(15)}", out) + self.assertIn(f"17 {jan1(16)}", out) # TODO Wait a recurring task From 847c482c250345e3264dc88fd1fec1d186344b4b Mon Sep 17 00:00:00 2001 From: koleesch Date: Sun, 7 Jul 2024 19:19:54 +0200 Subject: [PATCH 066/242] Remove duplicate check from task diag (#3545) --- src/commands/CmdDiagnostics.cpp | 29 +------------------------- test/recurrence.test.py | 15 -------------- test/uuid.test.py | 36 --------------------------------- 3 files changed, 1 insertion(+), 79 deletions(-) diff --git a/src/commands/CmdDiagnostics.cpp b/src/commands/CmdDiagnostics.cpp index e1fe06a99..625dbcd26 100644 --- a/src/commands/CmdDiagnostics.cpp +++ b/src/commands/CmdDiagnostics.cpp @@ -300,35 +300,8 @@ int CmdDiagnostics::execute (std::string& output) << Context::getContext ().getHeight () << '\n'; - // Scan tasks for duplicate UUIDs. - auto all = Context::getContext ().tdb2.all_tasks (); - std::map seen; - std::vector dups; - std::string uuid; - for (auto& i : all) - { - uuid = i.get ("uuid"); - if (seen.find (uuid) != seen.end ()) - dups.push_back (uuid); - else - seen[uuid] = 0; - } - - out << " Dups: " - << format ("Scanned {1} tasks for duplicate UUIDs:", all.size ()) - << '\n'; - - if (dups.size ()) - { - for (auto& d : dups) - out << " " << format ("Found duplicate {1}", d) << '\n'; - } - else - { - out << " No duplicates found\n"; - } - // Check all the UUID references + auto all = Context::getContext ().tdb2.all_tasks (); bool noBrokenRefs = true; out << " Broken ref: " diff --git a/test/recurrence.test.py b/test/recurrence.test.py index 313b9e803..41b3c4c5a 100755 --- a/test/recurrence.test.py +++ b/test/recurrence.test.py @@ -206,11 +206,6 @@ class TestRecurrenceTasks(TestCase): code, out, err = self.t("3 delete", input="y\n") self.assertIn("Deleted 1 task.", out) - # Check for duplicate UUIDs. - code, out, err = self.t("diag") - self.assertIn("No duplicates found", out) - - class TestBug972(TestCase): def setUp(self): """972: Executed before each test in the class""" @@ -478,11 +473,6 @@ class TestBug360AllowedChanges(BaseTestBug360): expected = "You cannot remove the due date from a recurring task." self.assertNotIn(expected, err) - # Make sure no duplicate tasks were created - code, out, err = self.t.diag() - expected = "No duplicates found" - self.assertIn(expected, out) - class TestBug649(TestCase): def setUp(self): """Executed before each test in the class""" @@ -602,11 +592,6 @@ class TestPeriod(TestCase): self.assertIn(" 2q ", out); self.assertIn(" 2y ", out); - # Duplicate check - code, out, err = self.t("diag") - self.assertIn("No duplicates found", out) - - class TestBugAnnual(TestCase): def setUp(self): """Executed before each test in the class""" diff --git a/test/uuid.test.py b/test/uuid.test.py index 55fe20169..48bd0be12 100755 --- a/test/uuid.test.py +++ b/test/uuid.test.py @@ -174,42 +174,6 @@ class TestUUID(TestCase): self.assertIn('"description":"seven"', out) -class TestUUIDuplicates(TestCase): - def setUp(self): - """Executed before each test in the class""" - self.t = Task() - - def test_uuid_duplicates_dupe(self): - """Verify that duplicating tasks does not create duplicate UUIDs""" - self.t("add simple") - self.t("1 duplicate") - - uuids = list() - for id in range(1,3): - code, out, err = self.t("_get %d.uuid" % id) - uuids.append(out.strip()) - - self.assertEqual(len(uuids), len(set(uuids))) - - code, out, err = self.t("diag") - self.assertIn("No duplicates found", out) - - def test_uuid_duplicates_recurrence(self): - """Verify that recurring tasks do not create duplicate UUIDs""" - print(self.t("add periodic recur:daily due:yesterday")) - self.t("list") # GC/handleRecurrence - - uuids = list() - for id in range(1,5): - code, out, err = self.t("_get %d.uuid" % id) - uuids.append(out.strip()) - - self.assertEqual(len(uuids), len(set(uuids))) - - code, out, err = self.t("diag") - self.assertIn("No duplicates found", out) - - class TestBug954(TestCase): def setUp(self): """Executed before each test in the class""" From 4a042752668f4a1b5c4b8afe5a0788084e338331 Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:04:09 +0200 Subject: [PATCH 067/242] add faketime and change to apt-get in devcontainer (#3543) We require faketime to be installed for running the tests, therefore its added. Further for non-interactive usage apt-get is recommended, therefore the change. --- .devcontainer/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 385cfafb7..84685922f 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -10,11 +10,11 @@ RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \ fi \ && rm -f /tmp/reinstall-cmake.sh -RUN sudo apt update && sudo apt install uuid-dev +RUN sudo apt-get update && sudo apt-get install uuid-dev faketime # [Optional] Uncomment this section to install additional vcpkg ports. # RUN su vscode -c "${VCPKG_ROOT}/vcpkg install " # [Optional] Uncomment this section to install additional packages. # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ -# && apt-get -y install --no-install-recommends \ No newline at end of file +# && apt-get -y install --no-install-recommends From 61c9b486642a386cecf2cba64cce3a8292de96b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 07:49:05 -0400 Subject: [PATCH 068/242] Bump docker/build-push-action from 6.2.0 to 6.3.0 (#3547) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.2.0 to 6.3.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.2.0...v6.3.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index a1865d1e5..92cc10926 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -40,7 +40,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.2.0 + uses: docker/build-push-action@v6.3.0 with: context: . file: "./docker/task.dockerfile" From 2bd609afe38516dc7e4112fd6e4e6f62c7103098 Mon Sep 17 00:00:00 2001 From: Will R S Hansen Date: Mon, 8 Jul 2024 20:22:14 -0700 Subject: [PATCH 069/242] Export tasks in a deterministic order (#3549) fix issue #3527 --- src/commands/CmdExport.cpp | 2 +- test/CMakeLists.txt | 1 + test/tw-3527.test.py | 39 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100755 test/tw-3527.test.py diff --git a/src/commands/CmdExport.cpp b/src/commands/CmdExport.cpp index f5c43f128..e758c9078 100644 --- a/src/commands/CmdExport.cpp +++ b/src/commands/CmdExport.cpp @@ -118,7 +118,7 @@ int CmdExport::execute (std::string& output) // if no sort order, sort by id if (!sortOrder.size ()) { - reportSort = "id"; + reportSort = "id,uuid"; } // Sort the tasks. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 877bb6863..26a18b1ce 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -170,6 +170,7 @@ set (pythonTests tw-2575.test.py tw-262.test.py tw-295.test.py + tw-3527.test.py uda.test.py uda_orphan.test.py uda_report.test.py diff --git a/test/tw-3527.test.py b/test/tw-3527.test.py new file mode 100755 index 000000000..a6674f60c --- /dev/null +++ b/test/tw-3527.test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import sys +import os +import unittest +import re +import json +import string + +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Task, TestCase + + +class TestExport(TestCase): + def setUp(self): + self.t = Task() + + # pretty arbitrary, just need several unique tasks + for letter in string.ascii_lowercase: + self.t(f"add test_task +{letter}") + self.t(f"+{letter} done") + + def test_export_stability_for_multiple_id_0(self): + exports = [self.t("export")[1] for _ in range(2)] + json_lists = [json.loads(s.strip()) for s in exports] + # to rule out a typo causing two failed exports + self.assertEqual(len(json_lists[0]), len(string.ascii_lowercase)) + # for better diff view + self.assertEqual(json_lists[0], json_lists[1]) + # the real test + self.assertEqual(exports[0], exports[1]) + + +if __name__ == "__main__": + from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) + From 213b9d3aee099a86e1efdeb32e73632e30f0844b Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 9 Jul 2024 16:39:39 -0400 Subject: [PATCH 070/242] Add support for task expiration (#3546) --- doc/man/taskrc.5.in | 6 +++ scripts/vim/syntax/taskrc.vim | 1 + src/Context.cpp | 6 +-- src/Context.h | 1 - src/TDB2.cpp | 7 +++ src/TDB2.h | 1 + src/commands/CmdNews.cpp | 22 +++++++++ src/commands/CmdNews.h | 1 + src/commands/CmdShow.cpp | 1 + src/commands/CmdSync.cpp | 7 ++- src/tc/Replica.cpp | 9 ++++ src/tc/Replica.h | 1 + src/tc/lib/src/replica.rs | 25 ++++++++++ src/tc/lib/taskchampion.h | 9 ++++ test/CMakeLists.txt | 1 + test/expiration.test.py | 89 +++++++++++++++++++++++++++++++++++ 16 files changed, 180 insertions(+), 7 deletions(-) create mode 100755 test/expiration.test.py diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 1a764af43..2a459d83d 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -216,6 +216,12 @@ Note that this should be used in the form of a command line override (task rc.gc=0 ...), and not permanently used in the .taskrc file, as this significantly affects performance in the long term. +.TP +.B expiration.on-sync=0 +If set, old tasks will be deleted automatically after each synchronization. +Tasks are identified as "old" when they have status "Deleted" and have not +been modified for 180 days. + .TP .B hooks=1 This master control switch enables hook script processing. The default value diff --git a/scripts/vim/syntax/taskrc.vim b/scripts/vim/syntax/taskrc.vim index 983cee93e..069d51049 100644 --- a/scripts/vim/syntax/taskrc.vim +++ b/scripts/vim/syntax/taskrc.vim @@ -132,6 +132,7 @@ syn match taskrcGoodKey '^\s*\Vexpressions='he=e-1 syn match taskrcGoodKey '^\s*\Vextensions='he=e-1 syn match taskrcGoodKey '^\s*\Vfontunderline='he=e-1 syn match taskrcGoodKey '^\s*\Vgc='he=e-1 +syn match taskrcGoodKey '^\s*\Vexpiration.on-sync='he=e-1 syn match taskrcGoodKey '^\s*\Vhooks='he=e-1 syn match taskrcGoodKey '^\s*\Vhooks.location='he=e-1 syn match taskrcGoodKey '^\s*\Vhyphenate='he=e-1 diff --git a/src/Context.cpp b/src/Context.cpp index 14b3106e7..b64203442 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -118,6 +118,7 @@ std::string configurationDefaults = "json.array=1 # Enclose JSON output in [ ]\n" "abbreviation.minimum=2 # Shortest allowed abbreviation\n" "news.version= # Latest version highlights read by the user\n" + "expiration.on-sync=0 # Expire old tasks on sync\n" "\n" "# Dates\n" "dateformat=Y-M-D # Preferred input and display date format\n" @@ -853,13 +854,8 @@ int Context::dispatch (std::string &out) // The command know whether they need a GC. if (c->needs_gc ()) { - run_gc = config.getBoolean ("gc"); tdb2.gc (); } - else - { - run_gc = false; - } // This is something that is only needed for write commands with no other // filter processing. diff --git a/src/Context.h b/src/Context.h index 310613eef..023178ccc 100644 --- a/src/Context.h +++ b/src/Context.h @@ -98,7 +98,6 @@ public: Hooks hooks {}; bool determine_color_use {true}; bool use_color {true}; - bool run_gc {true}; bool verbosity_legacy {false}; std::set verbosity {}; std::vector headers {}; diff --git a/src/TDB2.cpp b/src/TDB2.cpp index e871d4854..069e81b74 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -303,6 +303,7 @@ void TDB2::show_diff ( } } +//////////////////////////////////////////////////////////////////////////////// void TDB2::gc () { Timer timer; @@ -316,6 +317,12 @@ void TDB2::gc () Context::getContext ().time_gc_us += timer.total_us (); } +//////////////////////////////////////////////////////////////////////////////// +void TDB2::expire_tasks () +{ + replica.expire_tasks (); +} + //////////////////////////////////////////////////////////////////////////////// // Latest ID is that of the last pending task. int TDB2::latest_id () diff --git a/src/TDB2.h b/src/TDB2.h index 5716ec844..8f83c9452 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -56,6 +56,7 @@ public: void get_changes (std::vector &); void revert (); void gc (); + void expire_tasks (); int latest_id (); // Generalized task accessors. diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 1bbbab0a5..2d236d3b9 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -172,6 +172,7 @@ std::vector NewsItem::all () { std::vector items; version2_6_0(items); version3_0_0(items); + version3_1_0(items); return items; } @@ -537,6 +538,27 @@ void NewsItem::version3_0_0 (std::vector& items) { items.push_back(sync); } +void NewsItem::version3_1_0 (std::vector& items) { + Version version("3.1.0"); + NewsItem sync { + version, + /*title=*/"Purging and Expiring Tasks", + /*bg_title=*/"", + /*background=*/"", + /*punchline=*/ + "Support for `task purge` has been restored, and new support added for automatically expiring\n" + "old tasks.\n\n" + /*update=*/ + "The `task purge` command removes tasks entirely, in contrast to `task delete` which merely sets\n" + "the task status to 'Deleted'. This functionality existed in versions 2.x but was temporarily\n" + "removed in 3.0.\n\n" + "The new `expiration.on-sync` configuration parameter controls automatic expiration of old tasks.\n" + "An old task is one with status 'Deleted' that has not been modified in 180 days. This\n" + "functionality is optional and not enabled by default." + }; + items.push_back(sync); +} + //////////////////////////////////////////////////////////////////////////////// int CmdNews::execute (std::string& output) { diff --git a/src/commands/CmdNews.h b/src/commands/CmdNews.h index ba5222176..beee1af1e 100644 --- a/src/commands/CmdNews.h +++ b/src/commands/CmdNews.h @@ -49,6 +49,7 @@ public: static std::vector all(); static void version2_6_0 (std::vector&); static void version3_0_0 (std::vector&); + static void version3_1_0 (std::vector&); private: NewsItem ( diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index c120e7499..97de521fc 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -160,6 +160,7 @@ int CmdShow::execute (std::string& output) " due" " editor" " exit.on.missing.db" + " expiration.on-sync" " expressions" " fontunderline" " gc" diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index 0f117209c..cbf871d4c 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -106,7 +106,12 @@ int CmdSync::execute (std::string& output) << '\n'; } - Context::getContext ().tdb2.sync(std::move(server), false); + Context &context = Context::getContext (); + context.tdb2.sync(std::move(server), false); + + if (context.config.getBoolean ("expiration.on-sync")) { + context.tdb2.expire_tasks (); + } output = out.str (); return status; diff --git a/src/tc/Replica.cpp b/src/tc/Replica.cpp index 2cc0765bd..bc3a4654c 100644 --- a/src/tc/Replica.cpp +++ b/src/tc/Replica.cpp @@ -144,6 +144,15 @@ tc::Task tc::Replica::import_task_with_uuid (const std::string &uuid) return Task (tctask); } +//////////////////////////////////////////////////////////////////////////////// +void tc::Replica::expire_tasks () +{ + auto res = tc_replica_expire_tasks (&*inner); + if (res != TC_RESULT_OK) { + throw replica_error (); + } +} + //////////////////////////////////////////////////////////////////////////////// void tc::Replica::sync (Server server, bool avoid_snapshots) { diff --git a/src/tc/Replica.h b/src/tc/Replica.h index 6a7fcc371..f0a10098c 100644 --- a/src/tc/Replica.h +++ b/src/tc/Replica.h @@ -92,6 +92,7 @@ namespace tc { tc::Task new_task (Status status, const std::string &description); tc::Task import_task_with_uuid (const std::string &uuid); // TODO: struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid); + void expire_tasks(); void sync(Server server, bool avoid_snapshots); tc::ffi::TCReplicaOpList get_undo_ops (); void commit_undo_ops (tc::ffi::TCReplicaOpList tc_undo_ops, int32_t *undone_out); diff --git a/src/tc/lib/src/replica.rs b/src/tc/lib/src/replica.rs index e6956f431..9e63aa041 100644 --- a/src/tc/lib/src/replica.rs +++ b/src/tc/lib/src/replica.rs @@ -533,6 +533,31 @@ pub unsafe extern "C" fn tc_replica_sync( ) } +#[ffizz_header::item] +#[ffizz(order = 902)] +/// Expire old, deleted tasks. +/// +/// Expiration entails removal of tasks from the replica. Any modifications that occur after +/// the deletion (such as operations synchronized from other replicas) will do nothing. +/// +/// Tasks are eligible for expiration when they have status Deleted and have not been modified +/// for 180 days (about six months). Note that completed tasks are not eligible. +/// +/// ```c +/// EXTERN_C TCResult tc_replica_expire_tasks(struct TCReplica *rep); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn tc_replica_expire_tasks(rep: *mut TCReplica) -> TCResult { + wrap( + rep, + |rep| { + rep.expire_tasks()?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + #[ffizz_header::item] #[ffizz(order = 902)] /// Return undo local operations until the most recent UndoPoint. diff --git a/src/tc/lib/taskchampion.h b/src/tc/lib/taskchampion.h index dfbe1ba7d..a8f5760b0 100644 --- a/src/tc/lib/taskchampion.h +++ b/src/tc/lib/taskchampion.h @@ -552,6 +552,15 @@ EXTERN_C TCResult tc_replica_commit_undo_ops(struct TCReplica *rep, TCReplicaOpL // free the returned string. EXTERN_C struct TCString tc_replica_error(struct TCReplica *rep); +// Expire old, deleted tasks. +// +// Expiration entails removal of tasks from the replica. Any modifications that occur after +// the deletion (such as operations synchronized from other replicas) will do nothing. +// +// Tasks are eligible for expiration when they have status Deleted and have not been modified +// for 180 days (about six months). Note that completed tasks are not eligible. +EXTERN_C TCResult tc_replica_expire_tasks(struct TCReplica *rep); + // Get an existing task by its UUID. // // Returns NULL when the task does not exist, and on error. Consult tc_replica_error diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 26a18b1ce..433f0b8c3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -111,6 +111,7 @@ set (pythonTests encoding.test.py enpassant.test.py exec.test.py + expiration.test.py export.test.py feature.559.test.py feature.default.project.test.py diff --git a/test/expiration.test.py b/test/expiration.test.py new file mode 100755 index 000000000..c8a76a44a --- /dev/null +++ b/test/expiration.test.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +############################################################################### +# +# Copyright 2006 - 2024, 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 +# +############################################################################### + +import sys +import os +import unittest +import time +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Task, TestCase +from basetest.utils import mkstemp + + +class TestImport(TestCase): + def setUp(self): + self.t = Task() + # Set up local sync within the TASKDATA directory, so that it will be + # deleted properly. + self.t.config("sync.local.server_dir", self.t.datadir) + + def exists(self, uuid): + code, out, err = self.t(f"_get {uuid}.status") + return out.strip() != "" + + def test_expiration(self): + """Only tasks that are deleted and have a modification in the past are expired.""" + yesterday = int(time.time()) - 3600 * 24 + last_year = int(time.time()) - 265 * 3600 * 24 + old_pending = "a1111111-a111-a111-a111-a11111111111" + old_completed = "a2222222-a222-a222-a222-a22222222222" + new_deleted = "a3333333-a333-a333-a333-a33333333333" + old_deleted = "a4444444-a444-a444-a444-a44444444444" + task_data = f"""[ + {{"uuid":"{old_pending}","status":"pending","modified":"{last_year}","description":"x"}}, + {{"uuid":"{old_completed}","status":"completed","modified":"{last_year}","description":"x"}}, + {{"uuid":"{new_deleted}","status":"deleted","modified":"{yesterday}","description":"x"}}, + {{"uuid":"{old_deleted}","status":"deleted","modified":"{last_year}","description":"x"}} +] +""" + code, out, err = self.t("import -", input=task_data) + self.assertIn("Imported 4 tasks", err) + + # By default, expiration does not occur. + code, out, err = self.t("sync") + self.assertTrue(self.exists(old_pending)) + self.assertTrue(self.exists(old_completed)) + self.assertTrue(self.exists(new_deleted)) + self.assertTrue(self.exists(old_deleted)) + + # Configure expiration on sync. The old_deleted task + # should be removed. + self.t.config("expiration.on-sync", "1") + code, out, err = self.t("sync") + self.assertTrue(self.exists(old_pending)) + self.assertTrue(self.exists(old_completed)) + self.assertTrue(self.exists(new_deleted)) + self.assertFalse(self.exists(old_deleted)) + +if __name__ == "__main__": + from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) + +# vim: ai sts=4 et sw=4 ft=python From c477b2b59cfc40f53716b30656a9c56ff21f6bbf Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 12 Jul 2024 21:23:20 -0400 Subject: [PATCH 071/242] Do not claim that Taskwarrior automatically syncs (#3537) Taskwarrior only syncs when `task sync` is run. --- doc/man/task-sync.5.in | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/man/task-sync.5.in b/doc/man/task-sync.5.in index d3998c957..6ca7d2cf6 100644 --- a/doc/man/task-sync.5.in +++ b/doc/man/task-sync.5.in @@ -30,10 +30,7 @@ the existing replica, and run `task sync`. .SS When to Synchronize -Taskwarrior can perform a sync operation at every garbage collection (gc) run. -This is the default, and is appropriate for local synchronization. - -For synchronization to a server, a better solution is to run +For synchronization to a server, a common solution is to run .nf $ task sync From 9db275fedbd88dd81fc22f26579c3be8c5b55ab6 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 12 Jul 2024 21:27:11 -0400 Subject: [PATCH 072/242] Fix link in issue template (#3554) --- .github/issue_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/issue_template.md b/.github/issue_template.md index b65581ad7..4d80be6cf 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -9,4 +9,4 @@ * Clearly describe the feature. * Clearly state the use case. We are only interested in use cases, do not waste time with implementation details or suggested syntax. -* Please see our notes on [How to request a feature](https://taskwarrior.org/docs/features.html) +* Please see our notes on [How to request a feature](https://taskwarrior.org/docs/features/) From d4649dd21017fc2c3c3b78001792bdcc44a94279 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sat, 13 Jul 2024 19:12:49 -0400 Subject: [PATCH 073/242] Revert "Do not create recurring tasks before today (#3542)" (#3555) This reverts commit 6d3519419e0b69a721a48679acee3c7a54a04faa. --- src/recur.cpp | 10 ++---- test/recurrence.test.py | 67 ++++++++++++++++++----------------------- 2 files changed, 33 insertions(+), 44 deletions(-) diff --git a/src/recur.cpp b/src/recur.cpp index 0c4220ff3..524c912d3 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -135,10 +135,9 @@ void handleRecurrence () //////////////////////////////////////////////////////////////////////////////// // Determine a start date (due), an optional end date (until), and an increment -// period (recur). Then generate a set of corresponding dates. Only recurrences -// in the future are returned; see #3501. +// period (recur). Then generate a set of corresponding dates. // -// Returns false if the parent recurring task is deleted. +// Returns false if the parent recurring task is depleted. bool generateDueDates (Task& parent, std::vector & allDue) { // Determine due date, recur period and until date. @@ -159,12 +158,9 @@ bool generateDueDates (Task& parent, std::vector & allDue) auto recurrence_limit = Context::getContext ().config.getInteger ("recurrence.limit"); int recurrence_counter = 0; Datetime now; - Datetime today = now.startOfDay(); for (Datetime i = due; ; i = getNextRecurrence (i, recur)) { - // Do not add tasks before today (but allow today for the common `due:today` form). - if (i >= today) - allDue.push_back (i); + allDue.push_back (i); if (specificEnd && i > until) { diff --git a/test/recurrence.test.py b/test/recurrence.test.py index 41b3c4c5a..56e4e1b24 100755 --- a/test/recurrence.test.py +++ b/test/recurrence.test.py @@ -284,7 +284,6 @@ class TestBug932(TestCase): self.t = Task() self.t.config("report.id_pri_proj.columns", "id,priority,project") self.t.config("report.id_pri_proj.labels", "ID,P,Proj") - self.t.config("recurrence.limit", "3") def test_modify_due_propagate(self): """932: Verify due date modifications propagate""" @@ -292,7 +291,7 @@ class TestBug932(TestCase): # add a recurring task with multiple child tasks # - modify a child task and test for propagation # - modify the parent task and test for propagation - self.t("add R due:tomorrow recur:daily") + self.t("add R due:yesterday recur:daily") self.t("list") # GC/handleRecurrence self.t("2 modify project:P", input="y\n") @@ -312,8 +311,7 @@ class TestBug955(TestCase): def setUp(self): self.t = Task() - self.t.config("recurrence.limit", "2") - self.t("add foo due:tomorrow recur:1day") + self.t("add foo due:now recur:1day") code, out, err = self.t("ls") self.assertRegex(out, re.compile("^2 tasks", re.MULTILINE)) @@ -599,52 +597,47 @@ class TestBugAnnual(TestCase): def test_annual_creep(self): """Verify 'annual' recurring tasks don't creep""" - self.t.config("recurrence.limit", "17") self.t.config("dateformat", "YMD") self.t.config("report.annual.labels", "ID,Due") self.t.config("report.annual.columns", "id,due") self.t.config("report.annual.filter", "status:pending") self.t.config("report.annual.sort", "due+") - this_year = time.gmtime(time.time())[0] - def jan1(year_offset): - return f"{this_year+year_offset}0101" - # If a task is added with a due date ten years ago, with an annual recurrence, # then the synthetic tasks in between then and now have a due date that creeps. # # ID Due Description # -- ---------- ----------- - # 4 1/1/2102 foo - # 5 1/1/2103 foo - # 6 1/1/2104 foo - # 7 1/1/2105 foo - # 8 1/1/2106 foo - # 9 1/1/2107 foo - # 10 1/1/2108 foo - # 11 1/1/2109 foo - # 12 1/1/2110 foo - # 2 1/1/2100 foo - # 3 1/1/2101 foo + # 4 1/1/2002 foo + # 5 1/1/2003 foo + # 6 1/1/2004 foo + # 7 1/1/2005 foo + # 8 1/1/2006 foo + # 9 1/1/2007 foo + # 10 1/1/2008 foo + # 11 1/1/2009 foo + # 12 1/1/2010 foo + # 2 1/1/2000 foo + # 3 1/1/2001 foo - self.t(f"add foo due:{jan1(1)} recur:annual until:{jan1(16)}") + self.t("add foo due:20000101 recur:annual until:20150101") code, out, err = self.t("annual") - self.assertIn(f" 2 {jan1(1)}", out) - self.assertIn(f" 3 {jan1(2)}", out) - self.assertIn(f" 4 {jan1(3)}", out) - self.assertIn(f" 5 {jan1(4)}", out) - self.assertIn(f" 6 {jan1(5)}", out) - self.assertIn(f" 7 {jan1(6)}", out) - self.assertIn(f" 8 {jan1(7)}", out) - self.assertIn(f" 9 {jan1(8)}", out) - self.assertIn(f"10 {jan1(9)}", out) - self.assertIn(f"11 {jan1(10)}", out) - self.assertIn(f"12 {jan1(11)}", out) - self.assertIn(f"13 {jan1(12)}", out) - self.assertIn(f"14 {jan1(13)}", out) - self.assertIn(f"15 {jan1(14)}", out) - self.assertIn(f"16 {jan1(15)}", out) - self.assertIn(f"17 {jan1(16)}", out) + self.assertIn(" 2 20000101", out) + self.assertIn(" 3 20010101", out) + self.assertIn(" 4 20020101", out) + self.assertIn(" 5 20030101", out) + self.assertIn(" 6 20040101", out) + self.assertIn(" 7 20050101", out) + self.assertIn(" 8 20060101", out) + self.assertIn(" 9 20070101", out) + self.assertIn("10 20080101", out) + self.assertIn("11 20090101", out) + self.assertIn("12 20100101", out) + self.assertIn("13 20110101", out) + self.assertIn("14 20120101", out) + self.assertIn("15 20130101", out) + self.assertIn("16 20140101", out) + self.assertIn("17 20150101", out) # TODO Wait a recurring task From e156efae7d046ea06ebe09f444da688442f36b71 Mon Sep 17 00:00:00 2001 From: Sebastian Carlos <88276600+sebastiancarlos@users.noreply.github.com> Date: Sat, 13 Jul 2024 23:54:35 -0300 Subject: [PATCH 074/242] Note in taskrc(5) that "undo" configurations are not currently supported (#3518) --- doc/man/taskrc.5.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 2a459d83d..a55925c4e 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -514,6 +514,8 @@ comparison of the data. This can be in either the 'side' style, which compares values side-by-side in a table, or 'diff' style, which uses a format similar to the 'diff' command. +Currently not supported. + .TP .B abbreviation.minimum=2 Minimum length of any abbreviated command/value. This means that "ve", "ver", @@ -1048,6 +1050,9 @@ yellow bars. .RS Colors used by the undo command, to indicate the values both before and after a change that is to be reverted. + +Currently not supported. + .RE .TP From 1304d6361c9967a0a93ab30009362d053d3b3bf8 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 14 Jul 2024 11:59:00 -0400 Subject: [PATCH 075/242] Restore 'task purge' functionality (#3540) Co-authored-by: ryneeverett --- doc/man/task.1.in | 10 +++ src/TDB2.cpp | 7 ++ src/TDB2.h | 1 + src/commands/CmdPurge.cpp | 137 ++++++++++++++++++++++++++++++-- src/commands/CmdPurge.h | 5 ++ src/tc/Replica.cpp | 9 +++ src/tc/Replica.h | 1 + src/tc/lib/src/replica.rs | 29 +++++++ src/tc/lib/taskchampion.h | 9 +++ test/CMakeLists.txt | 1 + test/purge.test.py | 162 ++++++++++++++++++++++++++++++++++++++ 11 files changed, 366 insertions(+), 5 deletions(-) create mode 100755 test/purge.test.py diff --git a/doc/man/task.1.in b/doc/man/task.1.in index 9a0444cc3..e7e8a71b9 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -427,6 +427,15 @@ Modifies the existing task with provided information. .B task prepend Prepends description text to an existing task. Is affected by the context. +.TP +.B task purge +Permanently removes the specified tasks from the data files. Only +tasks that are already deleted can be purged. This command has a +local-only effect and changes introduced by it are not synced. +Is affected by the context. + +Warning: causes permanent, non-revertible loss of data. + .TP .B task start Marks the specified tasks as started. Is affected by the context. @@ -1280,6 +1289,7 @@ active context. Here is a list of the commands that are affected: log prepend projects + purge start stats stop diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 069e81b74..5bfb9133b 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -194,6 +194,13 @@ void TDB2::modify (Task& task) } } +//////////////////////////////////////////////////////////////////////////////// +void TDB2::purge (Task& task) +{ + auto uuid = task.get ("uuid"); + replica.delete_task (uuid); +} + //////////////////////////////////////////////////////////////////////////////// const tc::WorkingSet &TDB2::working_set () { diff --git a/src/TDB2.h b/src/TDB2.h index 8f83c9452..87bfa325d 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -53,6 +53,7 @@ public: void open_replica (const std::string&, bool create_if_missing); void add (Task&); void modify (Task&); + void purge (Task&); void get_changes (std::vector &); void revert (); void gc (); diff --git a/src/commands/CmdPurge.cpp b/src/commands/CmdPurge.cpp index ffde6543e..09550cdcd 100644 --- a/src/commands/CmdPurge.cpp +++ b/src/commands/CmdPurge.cpp @@ -27,13 +27,17 @@ #include #include #include +#include +#include +#include +#include //////////////////////////////////////////////////////////////////////////////// CmdPurge::CmdPurge () { _keyword = "purge"; _usage = "task purge"; - _description = "(deprecated; does nothing)"; + _description = "Removes the specified tasks from the data files. Causes permanent loss of data."; _read_only = false; _displays_id = false; _needs_confirm = true; @@ -46,11 +50,134 @@ CmdPurge::CmdPurge () } //////////////////////////////////////////////////////////////////////////////// -int CmdPurge::execute (std::string&) +// Purges the task, while taking care of: +// - dependencies on this task +// - child tasks +void CmdPurge::handleRelations (Task& task, std::vector& tasks) { - Context::getContext ().footnote ("As of version 3.0, this command has no effect."); - Context::getContext ().footnote ("Deleted tasks are removed from the task list automatically after they expire."); - return 0; + handleDeps (task); + handleChildren (task, tasks); + tasks.push_back(task); +} + +//////////////////////////////////////////////////////////////////////////////// +// Makes sure that any task having the dependency on the task being purged +// has that dependency removed, to preserve referential integrity. +void CmdPurge::handleDeps (Task& task) +{ + std::string uuid = task.get ("uuid"); + + for (auto& blockedConst: Context::getContext ().tdb2.all_tasks ()) + { + Task& blocked = const_cast(blockedConst); + if (blocked.hasDependency (uuid)) + { + blocked.removeDependency (uuid); + Context::getContext ().tdb2.modify (blocked); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Makes sure that with any recurrence parent are all the child tasks removed +// as well. If user chooses not to, the whole command is aborted. +void CmdPurge::handleChildren (Task& task, std::vector& tasks) +{ + // If this is not a recurrence parent, we have no job here + if (!task.has ("mask")) + return; + + std::string uuid = task.get ("uuid"); + std::vector children; + + // Find all child tasks + for (auto& childConst: Context::getContext ().tdb2.all_tasks ()) + { + Task& child = const_cast (childConst); + + if (child.get ("parent") == uuid) + { + if (child.getStatus () != Task::deleted) + // In case any child task is not deleted, bail out + throw format ("Task '{1}' is a recurrence template. Its child task {2} must be deleted before it can be purged.", + task.get ("description"), + child.identifier (true)); + else + children.push_back (child); + } + } + + // If there are no children, our job is done + if (children.empty ()) + return; + + // Ask for confirmation to purge them, if needed + std::string question = format ("Task '{1}' is a recurrence template. All its {2} deleted children tasks will be purged as well. Continue?", + task.get ("description"), + children.size ()); + + if (Context::getContext ().config.getBoolean ("recurrence.confirmation") || + (Context::getContext ().config.get ("recurrence.confirmation") == "prompt" + && confirm (question))) + { + for (auto& child: children) + handleRelations (child, tasks); + } + else + throw std::string ("Purge operation aborted."); +} + + +//////////////////////////////////////////////////////////////////////////////// +int CmdPurge::execute (std::string&) +{ + int rc = 0; + std::vector tasks; + bool matched_deleted = false; + + Filter filter; + std::vector filtered; + + // Apply filter. + filter.subset (filtered); + if (filtered.size () == 0) + { + Context::getContext ().footnote ("No tasks specified."); + return 1; + } + + for (auto& task : filtered) + { + // Allow purging of deleted tasks only. Hence no need to deal with: + // - unblocked tasks notifications (deleted tasks are not blocking) + // - project changes (deleted tasks not included in progress) + // It also has the nice property of being explicit - users need to + // mark tasks as deleted before purging. + if (task.getStatus () == Task::deleted) + { + // Mark that at least one deleted task matched the filter + matched_deleted = true; + + std::string question; + question = format ("Permanently remove task {1} '{2}'?", + task.identifier (true), + task.get ("description")); + + if (permission (question, filtered.size ())) + handleRelations (task, tasks); + } + } + + // Now that any exceptions are handled, actually purge the tasks. + for (auto& task: tasks) { + Context::getContext ().tdb2.purge (task); + } + + if (filtered.size () > 0 and ! matched_deleted) + Context::getContext ().footnote ("No deleted tasks specified. Maybe you forgot to delete tasks first?"); + + feedback_affected (tasks.size() == 1 ? "Purged {1} task." : "Purged {1} tasks.", tasks.size()); + return rc; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdPurge.h b/src/commands/CmdPurge.h index 96db2977d..c97fbf970 100644 --- a/src/commands/CmdPurge.h +++ b/src/commands/CmdPurge.h @@ -28,10 +28,15 @@ #define INCLUDED_CMDPURGE #include +#include #include class CmdPurge : public Command { +private: + void handleRelations (Task& task, std::vector& tasks); + void handleChildren (Task& task, std::vector& tasks); + void handleDeps (Task& task); public: CmdPurge (); int execute (std::string&); diff --git a/src/tc/Replica.cpp b/src/tc/Replica.cpp index bc3a4654c..f33a617dc 100644 --- a/src/tc/Replica.cpp +++ b/src/tc/Replica.cpp @@ -144,6 +144,15 @@ tc::Task tc::Replica::import_task_with_uuid (const std::string &uuid) return Task (tctask); } +//////////////////////////////////////////////////////////////////////////////// +void tc::Replica::delete_task (const std::string &uuid) +{ + auto res = tc_replica_delete_task (&*inner, uuid2tc (uuid)); + if (res != TC_RESULT_OK) { + throw replica_error (); + } +} + //////////////////////////////////////////////////////////////////////////////// void tc::Replica::expire_tasks () { diff --git a/src/tc/Replica.h b/src/tc/Replica.h index f0a10098c..4b0ff3eda 100644 --- a/src/tc/Replica.h +++ b/src/tc/Replica.h @@ -91,6 +91,7 @@ namespace tc { std::optional get_task (const std::string &uuid); tc::Task new_task (Status status, const std::string &description); tc::Task import_task_with_uuid (const std::string &uuid); + void delete_task (const std::string &uuid); // TODO: struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid); void expire_tasks(); void sync(Server server, bool avoid_snapshots); diff --git a/src/tc/lib/src/replica.rs b/src/tc/lib/src/replica.rs index 9e63aa041..340ef878b 100644 --- a/src/tc/lib/src/replica.rs +++ b/src/tc/lib/src/replica.rs @@ -501,6 +501,35 @@ pub unsafe extern "C" fn tc_replica_import_task_with_uuid( ) } +#[ffizz_header::item] +#[ffizz(order = 902)] +/// Delete a task. The task must exist. Note that this is different from setting status to +/// Deleted; this is the final purge of the task. +/// +/// Deletion may interact poorly with modifications to the same task on other replicas. For +/// example, if a task is deleted on replica 1 and its description modified on replica 2, then +/// after both replicas have fully synced, the resulting task will only have a `description` +/// property. +/// +/// ```c +/// EXTERN_C TCResult tc_replica_delete_task(struct TCReplica *rep, struct TCUuid tcuuid); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn tc_replica_delete_task(rep: *mut TCReplica, tcuuid: TCUuid) -> TCResult { + wrap( + rep, + |rep| { + // SAFETY: + // - tcuuid is a valid TCUuid (all bytes are valid) + // - tcuuid is Copy so ownership doesn't matter + let uuid = unsafe { TCUuid::val_from_arg(tcuuid) }; + rep.delete_task(uuid)?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + #[ffizz_header::item] #[ffizz(order = 902)] /// Synchronize this replica with a server. diff --git a/src/tc/lib/taskchampion.h b/src/tc/lib/taskchampion.h index a8f5760b0..f0571d469 100644 --- a/src/tc/lib/taskchampion.h +++ b/src/tc/lib/taskchampion.h @@ -547,6 +547,15 @@ EXTERN_C struct TCTaskList tc_replica_all_tasks(struct TCReplica *rep); // there are no operations that can be done. EXTERN_C TCResult tc_replica_commit_undo_ops(struct TCReplica *rep, TCReplicaOpList tc_undo_ops, int32_t *undone_out); +// Delete a task. The task must exist. Note that this is different from setting status to +// Deleted; this is the final purge of the task. +// +// Deletion may interact poorly with modifications to the same task on other replicas. For +// example, if a task is deleted on replica 1 and its description modified on replica 1, then +// after both replicas have fully synced, the resulting task will only have a `description` +// property. +EXTERN_C TCResult tc_replica_delete_task(struct TCReplica *rep, struct TCUuid tcuuid); + // Get the latest error for a replica, or a string with NULL ptr if no error exists. Subsequent // calls to this function will return NULL. The rep pointer must not be NULL. The caller must // free the returned string. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 433f0b8c3..1f2447386 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -147,6 +147,7 @@ set (pythonTests prepend.test.py pri_sort.test.py project.test.py + purge.test.py quotes.test.py rc.override.test.py recurrence.test.py diff --git a/test/purge.test.py b/test/purge.test.py new file mode 100755 index 000000000..092814ba8 --- /dev/null +++ b/test/purge.test.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +############################################################################### +# +# 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 +# +############################################################################### + +import sys +import os +import unittest +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Task, TestCase + + +class TestDelete(TestCase): + def setUp(self): + self.t = Task() + + def test_add_delete_purge(self): + """Verify that add/delete/purge successfully purges a task""" + self.t("add one") + uuid = self.t("_get 1.uuid")[1].strip() + + code, out, err = self.t("1 delete", input="y\n") + self.assertIn("Deleted 1 task.", out) + + code, out, err = self.t(uuid + " purge", input="y\n") + self.assertIn("Purged 1 task.", out) + + code, out, err = self.t("uuids") + self.assertNotIn(uuid, out) + + def test_purge_remove_deps(self): + """Purge command removes broken dependency references""" + self.t("add one") + self.t("add two dep:1") + uuid = self.t("_get 1.uuid")[1].strip() + + code, out, err = self.t("1 delete", input="y\n") + self.assertIn("Deleted 1 task.", out) + + code, out, err = self.t(uuid + " purge", input="y\n") + self.assertIn("Purged 1 task.", out) + + code, out, err = self.t("uuids") + self.assertNotIn(uuid, out) + + dependencies = self.t("_get 1.depends")[1].strip() + self.assertNotIn(uuid, dependencies) + + def test_purge_children(self): + """Purge command indirectly purges child tasks""" + self.t("add one recur:daily due:yesterday") + uuid = self.t("_get 1.uuid")[1].strip() + + # A dummy call to report, so that recurrence tasks get generated + self.t("list") + + code, out, err = self.t("1 delete", input="y\ny\n") + self.assertIn("Deleted 4 tasks.", out) + + code, out, err = self.t(uuid + " purge", input="y\ny\n") + self.assertIn("Purged 4 tasks.", out) + + code, out, err = self.t("uuids") + self.assertEqual('\n', out) + + def test_purge_children_fail_pending(self): + """Purge aborts if task has pending children""" + self.t("add one recur:daily due:yesterday") + uuid = self.t("_get 1.uuid")[1].strip() + + # A dummy call to report, so that recurrence tasks get generated + self.t("list") + + code, out, err = self.t("1 delete", input="y\nn\n") + self.assertIn("Deleted 1 task.", out) + + code, out, err = self.t.runError(uuid + " purge", input="y\n") + # The id of the problematic task is not deterministic, as there are + # three child tasks. + self.assertIn("child task", err) + self.assertIn("must be deleted before", err) + + # Check that nothing was purged + code, out, err = self.t("count") + self.assertEqual('4\n', out) + + def test_purge_children_fail_confirm(self): + """Purge aborts if user does not agree with it affecting child tasks""" + self.t("add one recur:daily due:yesterday") + uuid = self.t("_get 1.uuid")[1].strip() + + # A dummy call to report, so that recurrence tasks get generated + self.t("list") + + code, out, err = self.t("1 delete", input="y\ny\n") + self.assertIn("Deleted 4 tasks.", out) + + # Do not agree with purging of the child tasks + code, out, err = self.t.runError(uuid + " purge", input="y\nn\n") + self.assertIn("Purge operation aborted.", err) + + # Check that nothing was purged + code, out, err = self.t("count") + self.assertEqual('4\n', out) + + def test_purge_children(self): + """Purge command removes dependencies on indirectly purged tasks""" + self.t("add one recur:daily due:yesterday") + uuid = self.t("_get 1.uuid")[1].strip() + + # A dummy call to report, so that recurrence tasks get generated + self.t("list") + self.t("add two dep:4") + + # Check that the dependency is present + dependencies = self.t("_get 5.depends")[1].strip() + self.assertNotEqual("", dependencies) + + code, out, err = self.t("1 delete", input="y\ny\n") + self.assertIn("Deleted 4 tasks.", out) + + code, out, err = self.t(uuid + " purge", input="y\ny\n") + self.assertIn("Purged 4 tasks.", out) + + # Make sure we are dealing with the intended task + description = self.t("_get 1.description")[1].strip() + self.assertEqual("two", description) + + # Check that the dependency was removed + dependencies = self.t("_get 1.depends")[1].strip() + self.assertEqual("", dependencies) + +if __name__ == "__main__": + from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) + +# vim: ai sts=4 et sw=4 ft=python From 7d79b9e5160bbddc2b0083bbdcdc9bd6ceb1fc8a Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 14 Jul 2024 15:45:26 -0400 Subject: [PATCH 076/242] Rename 'expiration.on-sync' to 'purge.on-sync' (#3556) Taskwarrior uses "expire" to refer to deletion of tasks past their "until" date, so let's use `purge` to link this semantically to the `task purge` command. --- doc/man/taskrc.5.in | 4 +- scripts/vim/syntax/taskrc.vim | 2 +- src/Context.cpp | 2 +- src/commands/CmdNews.cpp | 4 +- src/commands/CmdShow.cpp | 2 +- src/commands/CmdSync.cpp | 2 +- test/CMakeLists.txt | 1 - test/expiration.test.py | 89 ----------------------------------- test/purge.test.py | 50 +++++++++++++++++++- 9 files changed, 57 insertions(+), 99 deletions(-) delete mode 100755 test/expiration.test.py diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index a55925c4e..ce7be4c23 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -217,8 +217,8 @@ rc.gc=0 ...), and not permanently used in the .taskrc file, as this significantly affects performance in the long term. .TP -.B expiration.on-sync=0 -If set, old tasks will be deleted automatically after each synchronization. +.B purge.on-sync=0 +If set, old tasks will be purged automatically after each synchronization. Tasks are identified as "old" when they have status "Deleted" and have not been modified for 180 days. diff --git a/scripts/vim/syntax/taskrc.vim b/scripts/vim/syntax/taskrc.vim index 069d51049..0e5eb13d0 100644 --- a/scripts/vim/syntax/taskrc.vim +++ b/scripts/vim/syntax/taskrc.vim @@ -132,7 +132,7 @@ syn match taskrcGoodKey '^\s*\Vexpressions='he=e-1 syn match taskrcGoodKey '^\s*\Vextensions='he=e-1 syn match taskrcGoodKey '^\s*\Vfontunderline='he=e-1 syn match taskrcGoodKey '^\s*\Vgc='he=e-1 -syn match taskrcGoodKey '^\s*\Vexpiration.on-sync='he=e-1 +syn match taskrcGoodKey '^\s*\Vpurge.on-sync='he=e-1 syn match taskrcGoodKey '^\s*\Vhooks='he=e-1 syn match taskrcGoodKey '^\s*\Vhooks.location='he=e-1 syn match taskrcGoodKey '^\s*\Vhyphenate='he=e-1 diff --git a/src/Context.cpp b/src/Context.cpp index b64203442..108721118 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -118,7 +118,7 @@ std::string configurationDefaults = "json.array=1 # Enclose JSON output in [ ]\n" "abbreviation.minimum=2 # Shortest allowed abbreviation\n" "news.version= # Latest version highlights read by the user\n" - "expiration.on-sync=0 # Expire old tasks on sync\n" + "purge.on-sync=0 # Purge old tasks on sync\n" "\n" "# Dates\n" "dateformat=Y-M-D # Preferred input and display date format\n" diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 2d236d3b9..798c4f43f 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -542,7 +542,7 @@ void NewsItem::version3_1_0 (std::vector& items) { Version version("3.1.0"); NewsItem sync { version, - /*title=*/"Purging and Expiring Tasks", + /*title=*/"Purging Tasks, Manually or Automatically", /*bg_title=*/"", /*background=*/"", /*punchline=*/ @@ -552,7 +552,7 @@ void NewsItem::version3_1_0 (std::vector& items) { "The `task purge` command removes tasks entirely, in contrast to `task delete` which merely sets\n" "the task status to 'Deleted'. This functionality existed in versions 2.x but was temporarily\n" "removed in 3.0.\n\n" - "The new `expiration.on-sync` configuration parameter controls automatic expiration of old tasks.\n" + "The new `purge.on-sync` configuration parameter controls automatic purging of old tasks.\n" "An old task is one with status 'Deleted' that has not been modified in 180 days. This\n" "functionality is optional and not enabled by default." }; diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index 97de521fc..53fb6ac03 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -160,7 +160,7 @@ int CmdShow::execute (std::string& output) " due" " editor" " exit.on.missing.db" - " expiration.on-sync" + " purge.on-sync" " expressions" " fontunderline" " gc" diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index cbf871d4c..836d6ebe0 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -109,7 +109,7 @@ int CmdSync::execute (std::string& output) Context &context = Context::getContext (); context.tdb2.sync(std::move(server), false); - if (context.config.getBoolean ("expiration.on-sync")) { + if (context.config.getBoolean ("purge.on-sync")) { context.tdb2.expire_tasks (); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1f2447386..d450df5c8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -111,7 +111,6 @@ set (pythonTests encoding.test.py enpassant.test.py exec.test.py - expiration.test.py export.test.py feature.559.test.py feature.default.project.test.py diff --git a/test/expiration.test.py b/test/expiration.test.py deleted file mode 100755 index c8a76a44a..000000000 --- a/test/expiration.test.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -############################################################################### -# -# Copyright 2006 - 2024, 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 -# -############################################################################### - -import sys -import os -import unittest -import time -# Ensure python finds the local simpletap module -sys.path.append(os.path.dirname(os.path.abspath(__file__))) - -from basetest import Task, TestCase -from basetest.utils import mkstemp - - -class TestImport(TestCase): - def setUp(self): - self.t = Task() - # Set up local sync within the TASKDATA directory, so that it will be - # deleted properly. - self.t.config("sync.local.server_dir", self.t.datadir) - - def exists(self, uuid): - code, out, err = self.t(f"_get {uuid}.status") - return out.strip() != "" - - def test_expiration(self): - """Only tasks that are deleted and have a modification in the past are expired.""" - yesterday = int(time.time()) - 3600 * 24 - last_year = int(time.time()) - 265 * 3600 * 24 - old_pending = "a1111111-a111-a111-a111-a11111111111" - old_completed = "a2222222-a222-a222-a222-a22222222222" - new_deleted = "a3333333-a333-a333-a333-a33333333333" - old_deleted = "a4444444-a444-a444-a444-a44444444444" - task_data = f"""[ - {{"uuid":"{old_pending}","status":"pending","modified":"{last_year}","description":"x"}}, - {{"uuid":"{old_completed}","status":"completed","modified":"{last_year}","description":"x"}}, - {{"uuid":"{new_deleted}","status":"deleted","modified":"{yesterday}","description":"x"}}, - {{"uuid":"{old_deleted}","status":"deleted","modified":"{last_year}","description":"x"}} -] -""" - code, out, err = self.t("import -", input=task_data) - self.assertIn("Imported 4 tasks", err) - - # By default, expiration does not occur. - code, out, err = self.t("sync") - self.assertTrue(self.exists(old_pending)) - self.assertTrue(self.exists(old_completed)) - self.assertTrue(self.exists(new_deleted)) - self.assertTrue(self.exists(old_deleted)) - - # Configure expiration on sync. The old_deleted task - # should be removed. - self.t.config("expiration.on-sync", "1") - code, out, err = self.t("sync") - self.assertTrue(self.exists(old_pending)) - self.assertTrue(self.exists(old_completed)) - self.assertTrue(self.exists(new_deleted)) - self.assertFalse(self.exists(old_deleted)) - -if __name__ == "__main__": - from simpletap import TAPTestRunner - unittest.main(testRunner=TAPTestRunner()) - -# vim: ai sts=4 et sw=4 ft=python diff --git a/test/purge.test.py b/test/purge.test.py index 092814ba8..1d9609995 100755 --- a/test/purge.test.py +++ b/test/purge.test.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- ############################################################################### # -# Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. +# Copyright 2006 - 2024, 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 @@ -29,10 +29,58 @@ import sys import os import unittest +import time # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase +from basetest.utils import mkstemp + + +class TestAutoPurge(TestCase): + def setUp(self): + self.t = Task() + # Set up local sync within the TASKDATA directory, so that it will be + # deleted properly. + self.t.config("sync.local.server_dir", self.t.datadir) + + def exists(self, uuid): + code, out, err = self.t(f"_get {uuid}.status") + return out.strip() != "" + + def test_auto_purge(self): + """Only tasks that are deleted and have a modification in the past are purged.""" + yesterday = int(time.time()) - 3600 * 24 + last_year = int(time.time()) - 265 * 3600 * 24 + old_pending = "a1111111-a111-a111-a111-a11111111111" + old_completed = "a2222222-a222-a222-a222-a22222222222" + new_deleted = "a3333333-a333-a333-a333-a33333333333" + old_deleted = "a4444444-a444-a444-a444-a44444444444" + task_data = f"""[ + {{"uuid":"{old_pending}","status":"pending","modified":"{last_year}","description":"x"}}, + {{"uuid":"{old_completed}","status":"completed","modified":"{last_year}","description":"x"}}, + {{"uuid":"{new_deleted}","status":"deleted","modified":"{yesterday}","description":"x"}}, + {{"uuid":"{old_deleted}","status":"deleted","modified":"{last_year}","description":"x"}} +] +""" + code, out, err = self.t("import -", input=task_data) + self.assertIn("Imported 4 tasks", err) + + # By default, purge does not occur. + code, out, err = self.t("sync") + self.assertTrue(self.exists(old_pending)) + self.assertTrue(self.exists(old_completed)) + self.assertTrue(self.exists(new_deleted)) + self.assertTrue(self.exists(old_deleted)) + + # Configure purge on sync. The old_deleted task + # should be removed. + self.t.config("purge.on-sync", "1") + code, out, err = self.t("sync") + self.assertTrue(self.exists(old_pending)) + self.assertTrue(self.exists(old_completed)) + self.assertTrue(self.exists(new_deleted)) + self.assertFalse(self.exists(old_deleted)) class TestDelete(TestCase): From 0650fe509f2e4895be06ec9535798a1fcb6385eb Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 15 Jul 2024 08:50:01 -0400 Subject: [PATCH 077/242] Fix formatting in task-sync manpage (#3535) --- doc/man/task-sync.5.in | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/doc/man/task-sync.5.in b/doc/man/task-sync.5.in index 6ca7d2cf6..9e1cdb320 100644 --- a/doc/man/task-sync.5.in +++ b/doc/man/task-sync.5.in @@ -116,28 +116,27 @@ Select the following permissions: - storage.buckets.get - storage.buckets.update - storage.objects.create + - storage.objects.delete - storage.objects.get - storage.objects.list - storage.objects.update - - storage.objects.delete - Create your new role. +Create your new role. - On the left sidebar, navigate to "Service accounts." +On the left sidebar, navigate to "Service accounts." - On the top menu bar within the "Service accounts" section, click "CREATE SERVICE ACCOUNT." - Provide an appropriate name and description for the new service account. - Select the role you just created and complete the service account creation process. - - Now, in the Service Account dashboard, click into the new service account and select "keys" on the top menu bar. - Click on "ADD KEY" to create and download a new key (a JSON key). +On the top menu bar within the "Service accounts" section, click "CREATE SERVICE ACCOUNT." +Provide an appropriate name and description for the new service account. +Select the role you just created and complete the service account creation process. +Now, in the Service Account dashboard, click into the new service account and select "keys" on the top menu bar. +Click on "ADD KEY" to create and download a new key (a JSON key). Then configure Taskwarrior with: .nf $ task config sync.gcp.bucket - $ task config sync.gcp.credential_path + $ task config sync.gcp.credential_path .fi .SS Local Synchronization From 7ea4baed77f7113b7d2a40048d457cd46e564b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Sad=C5=82ocha?= Date: Sat, 20 Jul 2024 03:27:16 +0100 Subject: [PATCH 078/242] Warn if an import contains multiple occurrences of the same UUID (#3560) --- src/commands/CmdImport.cpp | 19 ++++++++++++++++++- src/commands/CmdImport.h | 1 + test/import.test.py | 9 +++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/commands/CmdImport.cpp b/src/commands/CmdImport.cpp index 4bfe3072b..77d6f0ce1 100644 --- a/src/commands/CmdImport.cpp +++ b/src/commands/CmdImport.cpp @@ -32,6 +32,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////////////////// CmdImport::CmdImport () @@ -90,6 +91,20 @@ int CmdImport::execute (std::string&) } Context::getContext ().footnote (format ("Imported {1} tasks.", count)); + + // Warn the user about multiple occurrences of the same UUID. + auto duplicates = false; + for (const auto& [uuid, occurrences] : uuid_occurrences) + { + if (occurrences > 1) + { + duplicates = true; + Context::getContext ().footnote (format ("Input contains UUID '{1}' {2} times.", uuid, occurrences)); + } + } + if (duplicates) + Context::getContext ().footnote ("Tasks with the same UUID have been merged. Please check the results."); + return rc; } @@ -186,7 +201,9 @@ void CmdImport::importSingleTask (json::object* obj) // Check whether the imported task is new or a modified existing task. Task before; - if (Context::getContext ().tdb2.get (task.get ("uuid"), before)) + auto uuid = task.get("uuid"); + uuid_occurrences[uuid]++; + if (Context::getContext ().tdb2.get (uuid, before)) { // We need to neglect updates from attributes with dynamic defaults // unless they have been explicitly specified on import. diff --git a/src/commands/CmdImport.h b/src/commands/CmdImport.h index f492481ea..b27d94362 100644 --- a/src/commands/CmdImport.h +++ b/src/commands/CmdImport.h @@ -38,6 +38,7 @@ public: int execute (std::string&); private: + std::unordered_map uuid_occurrences; int import (const std::string&); void importSingleTask (json::object*); }; diff --git a/test/import.test.py b/test/import.test.py index 83b17b2ec..3bf6118b5 100755 --- a/test/import.test.py +++ b/test/import.test.py @@ -224,6 +224,15 @@ class TestImport(TestCase): code, out2, err = self.t("export") self.assertEqual(out1, out2) + def test_import_duplicate_uuid_in_input(self): + """Test import warns if input contains the same UUID twice.""" + _data = """[ +{"uuid":"a0000000-a000-a000-a000-a00000000000","description":"first description"}, +{"uuid":"a0000000-a000-a000-a000-a00000000000","description":"second description"} +]""" + _, _, err = self.t("import", input=_data) + self.assertIn("Input contains UUID 'a0000000-a000-a000-a000-a00000000000' 2 times", err) + class TestImportExportRoundtrip(TestCase): def setUp(self): From 40ea3f2f5418ab5795e2aa0e73a1206c1d18cc1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Sad=C5=82ocha?= Date: Sat, 20 Jul 2024 11:46:06 +0100 Subject: [PATCH 079/242] Fix conversion from `TCStatus::Unknown` (#3561) Before this patch, the messsage would be "unknown TCStatus 4294967295" (i.e. `u32::MAX`) instead of "unknown TCStatus -1". --- src/tc/lib/src/status.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/tc/lib/src/status.rs b/src/tc/lib/src/status.rs index e0d370136..df5332401 100644 --- a/src/tc/lib/src/status.rs +++ b/src/tc/lib/src/status.rs @@ -42,7 +42,7 @@ impl From for Status { TCStatus::Completed => Status::Completed, TCStatus::Deleted => Status::Deleted, TCStatus::Recurring => Status::Recurring, - _ => Status::Unknown(format!("unknown TCStatus {}", status as u32)), + _ => Status::Unknown(format!("unknown TCStatus {}", status as i32)), } } } @@ -58,3 +58,16 @@ impl From for TCStatus { } } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn conversion_from_unknown_tc_status_provides_discriminant_in_message() { + let tc_status = TCStatus::Unknown; + let status = Status::from(tc_status); + + assert!(matches!(status, Status::Unknown(msg) if msg == "unknown TCStatus -1")); + } +} From 4dc3093b22e9d5f4a1d4ecec13a2f1239970f722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Sad=C5=82ocha?= Date: Thu, 25 Jul 2024 00:52:25 +0100 Subject: [PATCH 080/242] Update a comment to match the code (#3565) --- src/commands/CmdCustom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index bb240bf7d..b1e75a3eb 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -260,7 +260,7 @@ int CmdCustom::execute (std::string& output) std::mt19937 random_generator(device()); std::uniform_int_distribution twentyfive_percent(1, 4); - // 1 in 10 chance to display the message. + // 25% chance to display the message. if (twentyfive_percent(random_generator) == 4) { std::ostringstream notice; From 9dde68f9180d1824a5c943007a96d05f65b18fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Sad=C5=82ocha?= Date: Thu, 25 Jul 2024 00:54:56 +0100 Subject: [PATCH 081/242] Remove unused variables in `task history` implementation (#3564) --- src/commands/CmdHistory.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/commands/CmdHistory.cpp b/src/commands/CmdHistory.cpp index 8e6fa7076..8b8f7ab83 100644 --- a/src/commands/CmdHistory.cpp +++ b/src/commands/CmdHistory.cpp @@ -97,20 +97,12 @@ void CmdHistoryBase::outputGraphical (std::string& output) { unsigned int leftOffset = (widthOfBar * maxAddedLine) / maxLine; - auto totalAdded = 0; - auto totalCompleted = 0; - auto totalDeleted = 0; - time_t priorTime = 0; auto row = 0; for (auto& i : groups) { row = view.addRow (); - totalAdded += addedGroup[i.first]; - totalCompleted += completedGroup[i.first]; - totalDeleted += deletedGroup[i.first]; - HistoryStrategy::insertRowDate (view, row, i.first, priorTime); priorTime = i.first; From 9c49863795f9ca066c299ec51006058eb01f3ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Sad=C5=82ocha?= Date: Sat, 27 Jul 2024 01:30:54 +0100 Subject: [PATCH 082/242] Make `task news` nag configurable and deterministic (#3567) This patch fixes #3497. --- doc/man/taskrc.5.in | 3 +- src/Context.cpp | 5 +-- src/commands/CmdCustom.cpp | 26 +++++--------- test/CMakeLists.txt | 1 + test/news.test.py | 69 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 20 deletions(-) create mode 100755 test/news.test.py diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index ce7be4c23..96767beea 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -303,6 +303,7 @@ control specific occasions when output is generated. This list may contain: label Column labels on tabular reports new-id Provides feedback on any new task with IDs (and UUIDs for new tasks with ID 0, such as new completed tasks). new-uuid Provides feedback on any new task with UUIDs. Overrides new-id. Useful for automation. + news Reminds to read new release highlights until the user runs "task news". affected Reports 'N tasks affected' and similar edit Used the verbose template for the 'edit' command special Feedback when applying special tags @@ -327,7 +328,7 @@ Here are the shortcut equivalents: .nf verbose=on - verbose=blank,header,footnote,label,new-id,affected,edit,special,project,sync,filter,override,recur + verbose=blank,header,footnote,label,new-id,news,affected,edit,special,project,sync,filter,override,recur .fi .nf diff --git a/src/Context.cpp b/src/Context.cpp index 108721118..0f66aebba 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -93,8 +93,8 @@ std::string configurationDefaults = "\n" "# Miscellaneous\n" "# verbose= # Comma-separated list. May contain any subset of:\n" - "# affected,blank,context,default,edit,filter,footnote,header,label,new-id,new-uuid,override,project,recur,special,sync\n" - "verbose=affected,blank,context,edit,header,footnote,label,new-id,project,special,sync,override,recur\n" + "# affected,blank,context,default,edit,filter,footnote,header,label,new-id,new-uuid,news,override,project,recur,special,sync\n" + "verbose=affected,blank,context,edit,header,footnote,label,new-id,news,project,special,sync,override,recur\n" "confirmation=1 # Confirmation on delete, big changes\n" "recurrence=1 # Enable recurrence\n" "recurrence.confirmation=prompt # Confirmation for propagating changes among recurring tasks (yes/no/prompt)\n" @@ -1050,6 +1050,7 @@ bool Context::verbose (const std::string& token) v != "label" && // v != "new-id" && // v != "new-uuid" && // + v != "news" && // v != "override" && // v != "project" && // v != "recur" && // diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index b1e75a3eb..378cdc11e 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -254,23 +253,16 @@ int CmdCustom::execute (std::string& output) // Inform user about the new release highlights if not presented yet Version news_version(Context::getContext ().config.get ("news.version")); Version current_version = Version::Current(); - if (news_version != current_version) + auto should_nag = news_version != current_version && Context::getContext ().verbose ("news"); + if (should_nag) { - std::random_device device; - std::mt19937 random_generator(device()); - std::uniform_int_distribution twentyfive_percent(1, 4); - - // 25% chance to display the message. - if (twentyfive_percent(random_generator) == 4) - { - std::ostringstream notice; - notice << "Recently upgraded to " << current_version << ". " - "Please run 'task news' to read highlights about the new release."; - if (Context::getContext ().verbose ("footnote")) - Context::getContext ().footnote (notice.str()); - else if (Context::getContext ().verbose ("header")) - Context::getContext ().header (notice.str()); - } + std::ostringstream notice; + notice << "Recently upgraded to " << current_version << ". " + "Please run 'task news' to read highlights about the new release."; + if (Context::getContext ().verbose ("footnote")) + Context::getContext ().footnote (notice.str()); + else if (Context::getContext ().verbose ("header")) + Context::getContext ().header (notice.str()); } std::string location = (Context::getContext ().data_dir); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d450df5c8..57c81bf5f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -138,6 +138,7 @@ set (pythonTests math.test.py modify.test.py nag.test.py + news.test.py obfuscate.test.py oldest.test.py operators.test.py diff --git a/test/news.test.py b/test/news.test.py new file mode 100755 index 000000000..f4ff93c19 --- /dev/null +++ b/test/news.test.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +############################################################################### +# +# Copyright 2024, Adrian Sadłocha. +# +# 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 +# +############################################################################### + +import os +import sys +import unittest + +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Task, TestCase + + +class TestNewsNag(TestCase): + def setUp(self): + self.t = Task() + self.t("add Sample") + + def test_news_nag_gets_displayed_with_default_verbosity_levels(self): + """Default verbosity""" + + _, _, err = self.t("") + self.assertIn("Please run 'task news'", err) + + def test_news_nag_gets_displayed_when_explicitly_toggled_on(self): + """Explicitly toggled on""" + + # Add `footnote` so there is a sink for the nag message. + _, _, err = self.t("rc.verbose:news,footnote") + self.assertIn("Please run 'task news'", err) + + def test_news_nag_does_not_get_displayed_when_explicitly_toggled_off(self): + """Explicitly toggled off""" + + # Add `footnote` so there is a sink for the nag message. + _, _, err = self.t("rc.verbose:footnote") + self.assertNotIn("Please run 'task news'", err) + + +if __name__ == "__main__": + from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) + +# vim: ai sts=4 et sw=4 ft=python From 3a07f7025371cd7219b9d95a01d4417bd29554a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 23:35:18 -0400 Subject: [PATCH 083/242] Bump docker/build-push-action from 6.3.0 to 6.4.1 (#3562) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.3.0 to 6.4.1. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.3.0...v6.4.1) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 92cc10926..e7e552e87 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -40,7 +40,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.3.0 + uses: docker/build-push-action@v6.4.1 with: context: . file: "./docker/task.dockerfile" From bb72ea61693556c9c05f2e61a8608f673017b104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Sad=C5=82ocha?= Date: Sat, 27 Jul 2024 04:36:23 +0100 Subject: [PATCH 084/242] Remove encoding declaration in Python files (#3568) As per https://docs.python.org/3.11/reference/lexical_analysis.html#encoding-declarations, the default encoding of Python files is UTF-8. In fact, it's been the default encoding since Python 3.0 (released in 2008). --- test/abbreviation.test.py | 1 - test/add.test.py | 1 - test/alias.test.py | 1 - test/annotate.test.py | 1 - test/append.test.py | 1 - test/args.test.py | 1 - test/basetest/__init__.py | 2 -- test/basetest/compat.py | 2 -- test/basetest/exceptions.py | 1 - test/basetest/hooks.py | 2 -- test/basetest/meta.py | 2 -- test/basetest/task.py | 2 -- test/basetest/testing.py | 2 -- test/basetest/utils.py | 1 - test/bash_completion.test.py | 1 - test/blocked.test.py | 1 - test/bulk.test.py | 1 - test/burndown.test.py | 1 - test/calc.test.py | 1 - test/calendar.test.py | 1 - test/caseless.test.py | 1 - test/color.cmd.test.py | 1 - test/color.rules.test.py | 1 - test/columns.test.py | 1 - test/commands.test.py | 1 - test/completed.test.py | 1 - test/configuration.test.py | 1 - test/confirmation.test.py | 1 - test/context.test.py | 1 - test/count.test.py | 1 - test/custom.config.test.py | 1 - test/custom.recur_ind.test.py | 1 - test/custom.tag_ind.test.py | 1 - test/custom.test.py | 1 - test/date.iso.test.py | 1 - test/dateformat.test.py | 1 - test/datesort.test.py | 1 - test/datetime-negative.test.py | 1 - test/debug.test.py | 1 - test/default.test.py | 1 - test/delete.test.py | 1 - test/denotate.test.py | 1 - test/dependencies.test.py | 1 - test/diag.test.py | 1 - test/diag_color.test.py | 1 - test/dom2.test.py | 1 - test/due.test.py | 1 - test/duplicate.test.py | 1 - test/edit.test.py | 1 - test/encoding.test.py | 1 - test/enpassant.test.py | 1 - test/exec.test.py | 1 - test/export.test.py | 1 - test/feature.559.test.py | 1 - test/feature.default.project.test.py | 1 - test/feature.print.empty.columns.test.py | 1 - test/feature.recurrence.test.py | 1 - test/feedback.test.py | 1 - test/filter.test.py | 1 - test/fontunderline.test.py | 1 - test/format.test.py | 1 - test/gc.test.py | 1 - test/helpers.test.py | 1 - test/history.test.py | 1 - test/hooks.env.test.py | 1 - test/hooks.on-add.test.py | 1 - test/hooks.on-exit.test.py | 1 - test/hooks.on-launch.test.py | 1 - test/hooks.on-modify.test.py | 1 - test/hyphenate.test.py | 1 - test/ids.test.py | 1 - test/import.test.py | 1 - test/info.test.py | 1 - test/limit.test.py | 1 - test/list.all.projects.test.py | 1 - test/log.test.py | 1 - test/logo.test.py | 1 - test/math.test.py | 1 - test/modify.test.py | 1 - test/nag.test.py | 1 - test/news.test.py | 1 - test/obfuscate.test.py | 1 - test/oldest.test.py | 1 - test/operators.test.py | 1 - test/overdue.test.py | 1 - test/partial.test.py | 1 - test/prepend.test.py | 1 - test/pri_sort.test.py | 1 - test/project.test.py | 1 - test/purge.test.py | 1 - test/quotes.test.py | 1 - test/rc.override.test.py | 1 - test/recurrence.test.py | 1 - test/reports.test.py | 1 - test/search.test.py | 1 - test/sequence.test.py | 1 - test/shell.test.py | 1 - test/show.test.py | 1 - test/sorting.test.py | 1 - test/special.test.py | 1 - test/start.test.py | 1 - test/stats.test.py | 1 - test/substitute.test.py | 1 - test/sugar.test.py | 1 - test/summary.test.py | 1 - test/tag.test.py | 1 - test/taskrc.test.py | 1 - test/template.test.py | 1 - test/test_hooks/on-modify-for-template-badexit.py | 1 - test/test_hooks/on-modify-for-template.py | 1 - test/timesheet.test.py | 1 - test/tw-1379.test.py | 1 - test/tw-1837.test.py | 1 - test/tw-20.test.py | 1 - test/tw-2575.test.py | 1 - test/tw-262.test.py | 1 - test/tw-295.test.py | 1 - test/uda.test.py | 1 - test/uda_orphan.test.py | 1 - test/uda_report.test.py | 1 - test/uda_sort.test.py | 1 - test/undo.test.py | 1 - test/unicode.test.py | 1 - test/unique.test.py | 1 - test/upgrade.test.py | 1 - test/urgency.test.py | 1 - test/urgency_inherit.test.py | 1 - test/uuid.test.py | 1 - test/verbose.test.py | 1 - test/version.test.py | 1 - test/wait.test.py | 1 - 131 files changed, 137 deletions(-) diff --git a/test/abbreviation.test.py b/test/abbreviation.test.py index c3acf4fd6..f85c883fa 100755 --- a/test/abbreviation.test.py +++ b/test/abbreviation.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/add.test.py b/test/add.test.py index e83fe5103..60331b7a2 100755 --- a/test/add.test.py +++ b/test/add.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/alias.test.py b/test/alias.test.py index 4ba584514..816d978a0 100755 --- a/test/alias.test.py +++ b/test/alias.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/annotate.test.py b/test/annotate.test.py index 588052bfc..c0c6cf05b 100755 --- a/test/annotate.test.py +++ b/test/annotate.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/append.test.py b/test/append.test.py index 4dccf0329..589941f1c 100755 --- a/test/append.test.py +++ b/test/append.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/args.test.py b/test/args.test.py index 875ae61dd..277704ad4 100755 --- a/test/args.test.py +++ b/test/args.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/basetest/__init__.py b/test/basetest/__init__.py index 08aa9ac92..4a97018a5 100644 --- a/test/basetest/__init__.py +++ b/test/basetest/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from .task import Task from .testing import TestCase diff --git a/test/basetest/compat.py b/test/basetest/compat.py index e60cb97a3..33696e8d0 100644 --- a/test/basetest/compat.py +++ b/test/basetest/compat.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - try: STRING_TYPE = basestring except NameError: diff --git a/test/basetest/exceptions.py b/test/basetest/exceptions.py index c960442bc..89ad8bbe9 100644 --- a/test/basetest/exceptions.py +++ b/test/basetest/exceptions.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import signal sig_names = dict((k, v) for v, k in reversed(sorted(signal.__dict__.items())) diff --git a/test/basetest/hooks.py b/test/basetest/hooks.py index e0f7ea7ad..a8ff5184d 100644 --- a/test/basetest/hooks.py +++ b/test/basetest/hooks.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from __future__ import division import errno import os diff --git a/test/basetest/meta.py b/test/basetest/meta.py index 2be78665e..055f5ca95 100644 --- a/test/basetest/meta.py +++ b/test/basetest/meta.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from __future__ import print_function, division diff --git a/test/basetest/task.py b/test/basetest/task.py index f6afc9fea..d426ff9db 100644 --- a/test/basetest/task.py +++ b/test/basetest/task.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import atexit import errno import json diff --git a/test/basetest/testing.py b/test/basetest/testing.py index 5d557df8a..ab6a026cc 100644 --- a/test/basetest/testing.py +++ b/test/basetest/testing.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import unittest import sys from .utils import TASKW_SKIP diff --git a/test/basetest/utils.py b/test/basetest/utils.py index 908ea1a0a..9bcb6bc06 100644 --- a/test/basetest/utils.py +++ b/test/basetest/utils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from __future__ import division import errno import os diff --git a/test/bash_completion.test.py b/test/bash_completion.test.py index 7c6de41e1..6730236b7 100755 --- a/test/bash_completion.test.py +++ b/test/bash_completion.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/blocked.test.py b/test/blocked.test.py index 6e63623af..b673f76f0 100755 --- a/test/blocked.test.py +++ b/test/blocked.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/bulk.test.py b/test/bulk.test.py index a66620169..75f7adc79 100755 --- a/test/bulk.test.py +++ b/test/bulk.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/burndown.test.py b/test/burndown.test.py index 2ea44011c..ce5d18ae7 100755 --- a/test/burndown.test.py +++ b/test/burndown.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/calc.test.py b/test/calc.test.py index f18a71e7d..7072aca70 100755 --- a/test/calc.test.py +++ b/test/calc.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/calendar.test.py b/test/calendar.test.py index ddf30b1a6..52a13b8dd 100755 --- a/test/calendar.test.py +++ b/test/calendar.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/caseless.test.py b/test/caseless.test.py index c2e800452..5e6e46cdc 100755 --- a/test/caseless.test.py +++ b/test/caseless.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/color.cmd.test.py b/test/color.cmd.test.py index 9e1857648..305d1dab3 100755 --- a/test/color.cmd.test.py +++ b/test/color.cmd.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/color.rules.test.py b/test/color.rules.test.py index 394557c36..52cc4026a 100755 --- a/test/color.rules.test.py +++ b/test/color.rules.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/columns.test.py b/test/columns.test.py index 89ab32a4c..2a7dd6afb 100755 --- a/test/columns.test.py +++ b/test/columns.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/commands.test.py b/test/commands.test.py index 9dfafd0bb..bed870791 100755 --- a/test/commands.test.py +++ b/test/commands.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/completed.test.py b/test/completed.test.py index fb0787833..92799fa99 100755 --- a/test/completed.test.py +++ b/test/completed.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/configuration.test.py b/test/configuration.test.py index b9b0b5e33..a96bee9aa 100755 --- a/test/configuration.test.py +++ b/test/configuration.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/confirmation.test.py b/test/confirmation.test.py index b59777216..1c904def7 100755 --- a/test/confirmation.test.py +++ b/test/confirmation.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/context.test.py b/test/context.test.py index d2ae70cd3..2b3661ec2 100755 --- a/test/context.test.py +++ b/test/context.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/count.test.py b/test/count.test.py index 0d87d01ca..3cd712d30 100755 --- a/test/count.test.py +++ b/test/count.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/custom.config.test.py b/test/custom.config.test.py index edf950faa..329c0f516 100755 --- a/test/custom.config.test.py +++ b/test/custom.config.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/custom.recur_ind.test.py b/test/custom.recur_ind.test.py index 9f4ab5959..d0084cdae 100755 --- a/test/custom.recur_ind.test.py +++ b/test/custom.recur_ind.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/custom.tag_ind.test.py b/test/custom.tag_ind.test.py index 04da051ab..ae24bf9a4 100755 --- a/test/custom.tag_ind.test.py +++ b/test/custom.tag_ind.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/custom.test.py b/test/custom.test.py index 2c8949057..bc839e37a 100755 --- a/test/custom.test.py +++ b/test/custom.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/date.iso.test.py b/test/date.iso.test.py index 818df122f..ca656b7a3 100755 --- a/test/date.iso.test.py +++ b/test/date.iso.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/dateformat.test.py b/test/dateformat.test.py index c07df4c32..60c46470d 100755 --- a/test/dateformat.test.py +++ b/test/dateformat.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/datesort.test.py b/test/datesort.test.py index 85cafb2b8..5f270cb37 100755 --- a/test/datesort.test.py +++ b/test/datesort.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/datetime-negative.test.py b/test/datetime-negative.test.py index 31d3e4b76..8dfc258a4 100755 --- a/test/datetime-negative.test.py +++ b/test/datetime-negative.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/debug.test.py b/test/debug.test.py index 2469213be..2f4c70467 100755 --- a/test/debug.test.py +++ b/test/debug.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/default.test.py b/test/default.test.py index daed3107f..5bab560b6 100755 --- a/test/default.test.py +++ b/test/default.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/delete.test.py b/test/delete.test.py index 02d544b22..bc998724d 100755 --- a/test/delete.test.py +++ b/test/delete.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/denotate.test.py b/test/denotate.test.py index d0e65f815..341346434 100755 --- a/test/denotate.test.py +++ b/test/denotate.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/dependencies.test.py b/test/dependencies.test.py index 171fd1d4b..971cf34a1 100755 --- a/test/dependencies.test.py +++ b/test/dependencies.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/diag.test.py b/test/diag.test.py index 4fae86ce2..60aab3c39 100755 --- a/test/diag.test.py +++ b/test/diag.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/diag_color.test.py b/test/diag_color.test.py index aef9bf780..edbf069ee 100755 --- a/test/diag_color.test.py +++ b/test/diag_color.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/dom2.test.py b/test/dom2.test.py index 8907ac97f..85025c0f0 100755 --- a/test/dom2.test.py +++ b/test/dom2.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/due.test.py b/test/due.test.py index 060980135..80943dfc4 100755 --- a/test/due.test.py +++ b/test/due.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/duplicate.test.py b/test/duplicate.test.py index 573c2355b..86c66695f 100755 --- a/test/duplicate.test.py +++ b/test/duplicate.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/edit.test.py b/test/edit.test.py index 9a56f60a9..92378378d 100755 --- a/test/edit.test.py +++ b/test/edit.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/encoding.test.py b/test/encoding.test.py index 89e55e0ad..b00632a07 100755 --- a/test/encoding.test.py +++ b/test/encoding.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/enpassant.test.py b/test/enpassant.test.py index ce0d9ccde..3f28d76a8 100755 --- a/test/enpassant.test.py +++ b/test/enpassant.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/exec.test.py b/test/exec.test.py index e9c8742b8..394e1b025 100755 --- a/test/exec.test.py +++ b/test/exec.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/export.test.py b/test/export.test.py index 7addc8566..c3637fbb2 100755 --- a/test/export.test.py +++ b/test/export.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/feature.559.test.py b/test/feature.559.test.py index df71de226..42b1bec25 100755 --- a/test/feature.559.test.py +++ b/test/feature.559.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/feature.default.project.test.py b/test/feature.default.project.test.py index 4e32f6c08..dc2cce7fc 100755 --- a/test/feature.default.project.test.py +++ b/test/feature.default.project.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/feature.print.empty.columns.test.py b/test/feature.print.empty.columns.test.py index 09fb5cd1c..66281e017 100755 --- a/test/feature.print.empty.columns.test.py +++ b/test/feature.print.empty.columns.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/feature.recurrence.test.py b/test/feature.recurrence.test.py index 69e18e0ca..0f50da486 100755 --- a/test/feature.recurrence.test.py +++ b/test/feature.recurrence.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/feedback.test.py b/test/feedback.test.py index 25f7fdfb9..de652da48 100755 --- a/test/feedback.test.py +++ b/test/feedback.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/filter.test.py b/test/filter.test.py index 486943a0a..6c39b3906 100755 --- a/test/filter.test.py +++ b/test/filter.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/fontunderline.test.py b/test/fontunderline.test.py index 76721d639..8b2b4c11e 100755 --- a/test/fontunderline.test.py +++ b/test/fontunderline.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/format.test.py b/test/format.test.py index b82a7e6b9..997aa452f 100755 --- a/test/format.test.py +++ b/test/format.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/gc.test.py b/test/gc.test.py index 06ec60f78..36681964b 100755 --- a/test/gc.test.py +++ b/test/gc.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/helpers.test.py b/test/helpers.test.py index db8222dce..8dbc27c28 100755 --- a/test/helpers.test.py +++ b/test/helpers.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/history.test.py b/test/history.test.py index 27faedfb6..04f46b005 100755 --- a/test/history.test.py +++ b/test/history.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/hooks.env.test.py b/test/hooks.env.test.py index 41562bf2d..ea9d91593 100755 --- a/test/hooks.env.test.py +++ b/test/hooks.env.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/hooks.on-add.test.py b/test/hooks.on-add.test.py index 66fdf2b67..c63553752 100755 --- a/test/hooks.on-add.test.py +++ b/test/hooks.on-add.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/hooks.on-exit.test.py b/test/hooks.on-exit.test.py index 9ba44b0a8..fe7e05b30 100755 --- a/test/hooks.on-exit.test.py +++ b/test/hooks.on-exit.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/hooks.on-launch.test.py b/test/hooks.on-launch.test.py index f6e2aa9b2..b89098d2e 100755 --- a/test/hooks.on-launch.test.py +++ b/test/hooks.on-launch.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/hooks.on-modify.test.py b/test/hooks.on-modify.test.py index 0bf7bf061..2f8fea56e 100755 --- a/test/hooks.on-modify.test.py +++ b/test/hooks.on-modify.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/hyphenate.test.py b/test/hyphenate.test.py index 424daa18d..33aa05a91 100755 --- a/test/hyphenate.test.py +++ b/test/hyphenate.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/ids.test.py b/test/ids.test.py index 4531f4d51..cf810056f 100755 --- a/test/ids.test.py +++ b/test/ids.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/import.test.py b/test/import.test.py index 3bf6118b5..8fcc9366a 100755 --- a/test/import.test.py +++ b/test/import.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/info.test.py b/test/info.test.py index 42194757b..89679b8bd 100755 --- a/test/info.test.py +++ b/test/info.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/limit.test.py b/test/limit.test.py index cf7849e8b..79baa0af0 100755 --- a/test/limit.test.py +++ b/test/limit.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/list.all.projects.test.py b/test/list.all.projects.test.py index ef49c41d4..380118862 100755 --- a/test/list.all.projects.test.py +++ b/test/list.all.projects.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/log.test.py b/test/log.test.py index 6737ba5e7..2a145023a 100755 --- a/test/log.test.py +++ b/test/log.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/logo.test.py b/test/logo.test.py index 6cd1e8233..1cc80aa35 100755 --- a/test/logo.test.py +++ b/test/logo.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/math.test.py b/test/math.test.py index d071a33f7..62c05d41d 100755 --- a/test/math.test.py +++ b/test/math.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/modify.test.py b/test/modify.test.py index c5a64b846..f61bfdad9 100755 --- a/test/modify.test.py +++ b/test/modify.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/nag.test.py b/test/nag.test.py index 9edbe29fc..0c16e0b1d 100755 --- a/test/nag.test.py +++ b/test/nag.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/news.test.py b/test/news.test.py index f4ff93c19..e2caf8a2a 100755 --- a/test/news.test.py +++ b/test/news.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2024, Adrian Sadłocha. diff --git a/test/obfuscate.test.py b/test/obfuscate.test.py index 386e2a21a..a4e401258 100755 --- a/test/obfuscate.test.py +++ b/test/obfuscate.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/oldest.test.py b/test/oldest.test.py index 527de4a7d..ac840ba7e 100755 --- a/test/oldest.test.py +++ b/test/oldest.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/operators.test.py b/test/operators.test.py index aeede1905..9e121acdb 100755 --- a/test/operators.test.py +++ b/test/operators.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/overdue.test.py b/test/overdue.test.py index 48e399af4..cec542267 100755 --- a/test/overdue.test.py +++ b/test/overdue.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/partial.test.py b/test/partial.test.py index 7ef957d45..8467e1ce2 100755 --- a/test/partial.test.py +++ b/test/partial.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/prepend.test.py b/test/prepend.test.py index cc1e1f231..97d57892a 100755 --- a/test/prepend.test.py +++ b/test/prepend.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/pri_sort.test.py b/test/pri_sort.test.py index e17ca9b46..6e74a0519 100755 --- a/test/pri_sort.test.py +++ b/test/pri_sort.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/project.test.py b/test/project.test.py index c7cfbbdf5..02d2d2880 100755 --- a/test/project.test.py +++ b/test/project.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/purge.test.py b/test/purge.test.py index 1d9609995..ec6491fee 100755 --- a/test/purge.test.py +++ b/test/purge.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2024, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/quotes.test.py b/test/quotes.test.py index e35921ac6..95b057231 100755 --- a/test/quotes.test.py +++ b/test/quotes.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/rc.override.test.py b/test/rc.override.test.py index fc11e0277..55e748ac8 100755 --- a/test/rc.override.test.py +++ b/test/rc.override.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/recurrence.test.py b/test/recurrence.test.py index 56e4e1b24..d65a35c63 100755 --- a/test/recurrence.test.py +++ b/test/recurrence.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/reports.test.py b/test/reports.test.py index 051421c05..4a18c3bb5 100755 --- a/test/reports.test.py +++ b/test/reports.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/search.test.py b/test/search.test.py index 7cf460ce2..51474208e 100755 --- a/test/search.test.py +++ b/test/search.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/sequence.test.py b/test/sequence.test.py index ef14a5b19..add63b362 100755 --- a/test/sequence.test.py +++ b/test/sequence.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/shell.test.py b/test/shell.test.py index f219be770..8c1853b18 100755 --- a/test/shell.test.py +++ b/test/shell.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/show.test.py b/test/show.test.py index 12d03a13c..11a091bf8 100755 --- a/test/show.test.py +++ b/test/show.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/sorting.test.py b/test/sorting.test.py index 144369756..67799e0a4 100755 --- a/test/sorting.test.py +++ b/test/sorting.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/special.test.py b/test/special.test.py index 59575a043..d0dab40dc 100755 --- a/test/special.test.py +++ b/test/special.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/start.test.py b/test/start.test.py index e8ea3d469..e3a1db57b 100755 --- a/test/start.test.py +++ b/test/start.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/stats.test.py b/test/stats.test.py index 8d41e7ea8..54d5da70b 100755 --- a/test/stats.test.py +++ b/test/stats.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/substitute.test.py b/test/substitute.test.py index 04860e759..475d1eb5d 100755 --- a/test/substitute.test.py +++ b/test/substitute.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/sugar.test.py b/test/sugar.test.py index 3c4227077..326ae4f03 100755 --- a/test/sugar.test.py +++ b/test/sugar.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/summary.test.py b/test/summary.test.py index 5f6445f39..d83666439 100755 --- a/test/summary.test.py +++ b/test/summary.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/tag.test.py b/test/tag.test.py index a00df163d..ba517d39e 100755 --- a/test/tag.test.py +++ b/test/tag.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/taskrc.test.py b/test/taskrc.test.py index 985f1c5d7..4f88a1632 100755 --- a/test/taskrc.test.py +++ b/test/taskrc.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/template.test.py b/test/template.test.py index 568f3ede5..303795c90 100755 --- a/test/template.test.py +++ b/test/template.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/test_hooks/on-modify-for-template-badexit.py b/test/test_hooks/on-modify-for-template-badexit.py index 6527ebfb3..32537561e 100644 --- a/test/test_hooks/on-modify-for-template-badexit.py +++ b/test/test_hooks/on-modify-for-template-badexit.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- import sys import json diff --git a/test/test_hooks/on-modify-for-template.py b/test/test_hooks/on-modify-for-template.py index aeea80f2a..c3b01193e 100644 --- a/test/test_hooks/on-modify-for-template.py +++ b/test/test_hooks/on-modify-for-template.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- import sys import json diff --git a/test/timesheet.test.py b/test/timesheet.test.py index 2d0493401..83a48a253 100755 --- a/test/timesheet.test.py +++ b/test/timesheet.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/tw-1379.test.py b/test/tw-1379.test.py index edb6d5304..bc6f2aaaa 100755 --- a/test/tw-1379.test.py +++ b/test/tw-1379.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/tw-1837.test.py b/test/tw-1837.test.py index cd51f4d8b..7078545a6 100755 --- a/test/tw-1837.test.py +++ b/test/tw-1837.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/tw-20.test.py b/test/tw-20.test.py index 4c0556759..f06fafe8b 100755 --- a/test/tw-20.test.py +++ b/test/tw-20.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/tw-2575.test.py b/test/tw-2575.test.py index 246cf45d7..cd9c75dd1 100755 --- a/test/tw-2575.test.py +++ b/test/tw-2575.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- import sys import os diff --git a/test/tw-262.test.py b/test/tw-262.test.py index 1ddff5179..c3a0ff006 100755 --- a/test/tw-262.test.py +++ b/test/tw-262.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/tw-295.test.py b/test/tw-295.test.py index 15136b288..b46a892c2 100755 --- a/test/tw-295.test.py +++ b/test/tw-295.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/uda.test.py b/test/uda.test.py index b219e964c..1c9901e4f 100755 --- a/test/uda.test.py +++ b/test/uda.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/uda_orphan.test.py b/test/uda_orphan.test.py index edd624630..edceaafab 100755 --- a/test/uda_orphan.test.py +++ b/test/uda_orphan.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/uda_report.test.py b/test/uda_report.test.py index 0b54ae15c..427b20660 100755 --- a/test/uda_report.test.py +++ b/test/uda_report.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/uda_sort.test.py b/test/uda_sort.test.py index 922100d72..a8274672e 100755 --- a/test/uda_sort.test.py +++ b/test/uda_sort.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/undo.test.py b/test/undo.test.py index a2ef50847..b50acb0cc 100755 --- a/test/undo.test.py +++ b/test/undo.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/unicode.test.py b/test/unicode.test.py index 2850d9565..d18cc0239 100755 --- a/test/unicode.test.py +++ b/test/unicode.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/unique.test.py b/test/unique.test.py index 7dcc706bd..efa9d12eb 100755 --- a/test/unique.test.py +++ b/test/unique.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/upgrade.test.py b/test/upgrade.test.py index f9df76eb3..021591a36 100755 --- a/test/upgrade.test.py +++ b/test/upgrade.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/urgency.test.py b/test/urgency.test.py index 75c23707d..6ab72f087 100755 --- a/test/urgency.test.py +++ b/test/urgency.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/urgency_inherit.test.py b/test/urgency_inherit.test.py index c446355be..ac38aa6a9 100755 --- a/test/urgency_inherit.test.py +++ b/test/urgency_inherit.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/uuid.test.py b/test/uuid.test.py index 48bd0be12..ca42036b7 100755 --- a/test/uuid.test.py +++ b/test/uuid.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/verbose.test.py b/test/verbose.test.py index 7677d8550..b38ae25de 100755 --- a/test/verbose.test.py +++ b/test/verbose.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/version.test.py b/test/version.test.py index 747eacac0..3d74bd1c7 100755 --- a/test/version.test.py +++ b/test/version.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. diff --git a/test/wait.test.py b/test/wait.test.py index a85dd5de9..a88f5dc76 100755 --- a/test/wait.test.py +++ b/test/wait.test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- ############################################################################### # # Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez. From 9de339e0873aae8b183e5b104c9ce7522dc9c0cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:49:13 -0400 Subject: [PATCH 085/242] Bump docker/login-action from 3.2.0 to 3.3.0 (#3569) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v3.2.0...v3.3.0) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index e7e552e87..042504a24 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -32,7 +32,7 @@ jobs: uses: sigstore/cosign-installer@v3.5.0 - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3.2.0 + uses: docker/login-action@v3.3.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} From 234fac40c6d0c8bedfd9c62f279a5a6648301260 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:50:15 -0400 Subject: [PATCH 086/242] Bump docker/build-push-action from 6.4.1 to 6.5.0 (#3570) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.4.1 to 6.5.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.4.1...v6.5.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 042504a24..9414d0408 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -40,7 +40,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.4.1 + uses: docker/build-push-action@v6.5.0 with: context: . file: "./docker/task.dockerfile" From 5406772b669d3339a0a95fd01341e86dbc8da2f0 Mon Sep 17 00:00:00 2001 From: Felix Schurk Date: Thu, 11 Jul 2024 15:18:40 +0200 Subject: [PATCH 087/242] add clang-format initial version --- .clang-format | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..17177ad0a --- /dev/null +++ b/.clang-format @@ -0,0 +1,5 @@ +--- +Language: Cpp +BasedOnStyle: Google +ColumnLimit: 100 +... From a40ead9c6019b2243c6ebaba45f145cf7557ae9e Mon Sep 17 00:00:00 2001 From: Felix Schurk Date: Wed, 24 Jul 2024 16:57:58 +0200 Subject: [PATCH 088/242] add pre-commit-config.yaml file Includes default tools, as well as clang-format and black. --- .pre-commit-config.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..e17239bcc --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,19 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files +- repo: https://github.com/pre-commit/mirrors-clang-format + rev: v18.1.8 + hooks: + - id: clang-format + types_or: [c++, c] +- repo: https://github.com/psf/black + rev: 24.4.2 + hooks: + - id: black From dfc356679684d7d82a6979bbfc6b96cb78e5a717 Mon Sep 17 00:00:00 2001 From: Felix Schurk Date: Wed, 24 Jul 2024 17:00:34 +0200 Subject: [PATCH 089/242] add .cache from clang-format to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4ac379c09..bc63804f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ cmake.h commit.h +.cache *~ .*.swp Session.vim From 954d3f5058388bff03c4402111c8a975699dcc58 Mon Sep 17 00:00:00 2001 From: Felix Schurk Date: Wed, 24 Jul 2024 18:20:43 +0200 Subject: [PATCH 090/242] add blank line between cmake.h header include to prevent sorting * add required comment in the line below cmake.h include header --- src/CLI2.cpp | 2 ++ src/Context.cpp | 2 ++ src/DOM.cpp | 2 ++ src/Eval.cpp | 2 ++ src/Filter.cpp | 2 ++ src/Hooks.cpp | 2 ++ src/Lexer.cpp | 2 ++ src/TDB2.cpp | 2 ++ src/Task.cpp | 2 ++ src/Variant.cpp | 2 ++ src/Version.cpp | 2 ++ src/ViewTask.cpp | 4 +++- src/calc.cpp | 2 ++ src/columns/ColDepends.cpp | 2 ++ src/columns/ColDescription.cpp | 2 ++ src/columns/ColDue.cpp | 2 ++ src/columns/ColEnd.cpp | 2 ++ src/columns/ColEntry.cpp | 2 ++ src/columns/ColID.cpp | 2 ++ src/columns/ColIMask.cpp | 2 ++ src/columns/ColLast.cpp | 2 ++ src/columns/ColMask.cpp | 2 ++ src/columns/ColModified.cpp | 2 ++ src/columns/ColParent.cpp | 2 ++ src/columns/ColProject.cpp | 2 ++ src/columns/ColRType.cpp | 2 ++ src/columns/ColRecur.cpp | 2 ++ src/columns/ColScheduled.cpp | 2 ++ src/columns/ColStart.cpp | 2 ++ src/columns/ColStatus.cpp | 2 ++ src/columns/ColTags.cpp | 2 ++ src/columns/ColTemplate.cpp | 2 ++ src/columns/ColTypeDate.cpp | 4 +++- src/columns/ColTypeDuration.cpp | 2 ++ src/columns/ColTypeNumeric.cpp | 2 ++ src/columns/ColTypeString.cpp | 2 ++ src/columns/ColUDA.cpp | 2 ++ src/columns/ColUUID.cpp | 2 ++ src/columns/ColUntil.cpp | 2 ++ src/columns/ColUrgency.cpp | 2 ++ src/columns/ColWait.cpp | 2 ++ src/columns/Column.cpp | 2 ++ src/commands/CmdAdd.cpp | 2 ++ src/commands/CmdAliases.cpp | 2 ++ src/commands/CmdAnnotate.cpp | 2 ++ src/commands/CmdAppend.cpp | 2 ++ src/commands/CmdAttributes.cpp | 2 ++ src/commands/CmdBurndown.cpp | 2 ++ src/commands/CmdCalendar.cpp | 2 ++ src/commands/CmdColor.cpp | 2 ++ src/commands/CmdColumns.cpp | 2 ++ src/commands/CmdCommands.cpp | 2 ++ src/commands/CmdConfig.cpp | 2 ++ src/commands/CmdContext.cpp | 2 ++ src/commands/CmdCount.cpp | 2 ++ src/commands/CmdCustom.cpp | 2 ++ src/commands/CmdDelete.cpp | 4 +++- src/commands/CmdDenotate.cpp | 2 ++ src/commands/CmdDiagnostics.cpp | 2 ++ src/commands/CmdDone.cpp | 6 ++++-- src/commands/CmdDuplicate.cpp | 2 ++ src/commands/CmdEdit.cpp | 2 ++ src/commands/CmdExec.cpp | 2 ++ src/commands/CmdExport.cpp | 2 ++ src/commands/CmdGet.cpp | 2 ++ src/commands/CmdHelp.cpp | 2 ++ src/commands/CmdHistory.cpp | 2 ++ src/commands/CmdIDs.cpp | 2 ++ src/commands/CmdImport.cpp | 2 ++ src/commands/CmdInfo.cpp | 2 ++ src/commands/CmdLog.cpp | 2 ++ src/commands/CmdLogo.cpp | 2 ++ src/commands/CmdModify.cpp | 2 ++ src/commands/CmdNews.cpp | 4 +++- src/commands/CmdPrepend.cpp | 2 ++ src/commands/CmdProjects.cpp | 2 ++ src/commands/CmdPurge.cpp | 2 ++ src/commands/CmdReports.cpp | 2 ++ src/commands/CmdShow.cpp | 4 +++- src/commands/CmdStart.cpp | 2 ++ src/commands/CmdStats.cpp | 2 ++ src/commands/CmdStop.cpp | 2 ++ src/commands/CmdSummary.cpp | 2 ++ src/commands/CmdSync.cpp | 2 ++ src/commands/CmdTags.cpp | 2 ++ src/commands/CmdTimesheet.cpp | 2 ++ src/commands/CmdUDAs.cpp | 2 ++ src/commands/CmdUndo.cpp | 2 ++ src/commands/CmdUnique.cpp | 2 ++ src/commands/CmdUrgency.cpp | 2 ++ src/commands/CmdVersion.cpp | 2 ++ src/commands/Command.cpp | 2 ++ src/dependency.cpp | 2 ++ src/feedback.cpp | 2 ++ src/legacy.cpp | 2 ++ src/main.cpp | 2 ++ src/nag.cpp | 2 ++ src/recur.cpp | 2 ++ src/rules.cpp | 2 ++ src/sort.cpp | 8 +++++--- src/tc/Replica.cpp | 2 ++ src/tc/Server.cpp | 2 ++ src/tc/Task.cpp | 2 ++ src/tc/WorkingSet.cpp | 2 ++ src/tc/util.cpp | 2 ++ src/util.cpp | 4 +++- src/util.h | 2 ++ test/col.test.cpp | 2 ++ test/dom.test.cpp | 2 ++ test/eval.test.cpp | 2 ++ test/lexer.test.cpp | 2 ++ test/t.test.cpp | 2 ++ test/tc.test.cpp | 4 +++- test/tdb2.test.cpp | 2 ++ test/tw-2689.test.cpp | 2 ++ test/util.test.cpp | 2 ++ test/variant_add.test.cpp | 2 ++ test/variant_and.test.cpp | 2 ++ test/variant_cast.test.cpp | 2 ++ test/variant_divide.test.cpp | 2 ++ test/variant_equal.test.cpp | 2 ++ test/variant_exp.test.cpp | 2 ++ test/variant_gt.test.cpp | 2 ++ test/variant_gte.test.cpp | 2 ++ test/variant_inequal.test.cpp | 2 ++ test/variant_lt.test.cpp | 2 ++ test/variant_lte.test.cpp | 2 ++ test/variant_match.test.cpp | 2 ++ test/variant_math.test.cpp | 2 ++ test/variant_modulo.test.cpp | 2 ++ test/variant_multiply.test.cpp | 2 ++ test/variant_nomatch.test.cpp | 2 ++ test/variant_not.test.cpp | 2 ++ test/variant_or.test.cpp | 2 ++ test/variant_partial.test.cpp | 2 ++ test/variant_subtract.test.cpp | 2 ++ test/variant_xor.test.cpp | 2 ++ test/view.test.cpp | 2 ++ 138 files changed, 288 insertions(+), 12 deletions(-) diff --git a/src/CLI2.cpp b/src/CLI2.cpp index 3fd286ecd..eca04fabe 100644 --- a/src/CLI2.cpp +++ b/src/CLI2.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/Context.cpp b/src/Context.cpp index 0f66aebba..2004d26ce 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/DOM.cpp b/src/DOM.cpp index 4b6f901dc..e2eea1b68 100644 --- a/src/DOM.cpp +++ b/src/DOM.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/Eval.cpp b/src/Eval.cpp index 5319a0804..9d2748aa3 100644 --- a/src/Eval.cpp +++ b/src/Eval.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/Filter.cpp b/src/Filter.cpp index 4c900e917..e6ee0f550 100644 --- a/src/Filter.cpp +++ b/src/Filter.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/Hooks.cpp b/src/Hooks.cpp index 12c8d1756..c0fdc056d 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include // If is included, put it after , because it includes diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 801d6b491..d11a8f335 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 5bfb9133b..185f86e29 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/Task.cpp b/src/Task.cpp index 36d690e3d..d483fdd52 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/Variant.cpp b/src/Variant.cpp index 84c0a645e..3d34f7327 100644 --- a/src/Variant.cpp +++ b/src/Variant.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/Version.cpp b/src/Version.cpp index bbce61253..8d79c6816 100644 --- a/src/Version.cpp +++ b/src/Version.cpp @@ -26,6 +26,8 @@ #include #include +// cmake.h include header must come first + #include #include #include diff --git a/src/ViewTask.cpp b/src/ViewTask.cpp index e3adf8826..791316002 100644 --- a/src/ViewTask.cpp +++ b/src/ViewTask.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include @@ -188,7 +190,7 @@ std::string ViewTask::render (std::vector & data, std::vector & seque // Calculate final column widths. int overage = _width - sum_minimal - all_extra; - Context::getContext ().debug (format ("ViewTask::render min={1} ideal={2} overage={3} width={4}", + Context::getContext ().debug (format ("ViewTask::render min={1} ideal={2} overage={3} width={4}", sum_minimal + all_extra, sum_ideal + all_extra, overage, diff --git a/src/calc.cpp b/src/calc.cpp index e6dd9ef22..af852d64b 100644 --- a/src/calc.cpp +++ b/src/calc.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColDepends.cpp b/src/columns/ColDepends.cpp index 4b69e4d39..1bd01a72b 100644 --- a/src/columns/ColDepends.cpp +++ b/src/columns/ColDepends.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColDescription.cpp b/src/columns/ColDescription.cpp index 86ef52524..b36661ff7 100644 --- a/src/columns/ColDescription.cpp +++ b/src/columns/ColDescription.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColDue.cpp b/src/columns/ColDue.cpp index 3801af6d8..c1aee8ed3 100644 --- a/src/columns/ColDue.cpp +++ b/src/columns/ColDue.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColEnd.cpp b/src/columns/ColEnd.cpp index 0f38d85ff..baddb8670 100644 --- a/src/columns/ColEnd.cpp +++ b/src/columns/ColEnd.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColEntry.cpp b/src/columns/ColEntry.cpp index 0d3397d02..8068d710a 100644 --- a/src/columns/ColEntry.cpp +++ b/src/columns/ColEntry.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColID.cpp b/src/columns/ColID.cpp index a6b8c2588..9f66406e8 100644 --- a/src/columns/ColID.cpp +++ b/src/columns/ColID.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColIMask.cpp b/src/columns/ColIMask.cpp index ac49e4250..d4c79e249 100644 --- a/src/columns/ColIMask.cpp +++ b/src/columns/ColIMask.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include diff --git a/src/columns/ColLast.cpp b/src/columns/ColLast.cpp index ca7b1c0be..abeaff93d 100644 --- a/src/columns/ColLast.cpp +++ b/src/columns/ColLast.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include diff --git a/src/columns/ColMask.cpp b/src/columns/ColMask.cpp index b1bb13870..3be3418ca 100644 --- a/src/columns/ColMask.cpp +++ b/src/columns/ColMask.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include diff --git a/src/columns/ColModified.cpp b/src/columns/ColModified.cpp index fa7d96564..658e4b80b 100644 --- a/src/columns/ColModified.cpp +++ b/src/columns/ColModified.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColParent.cpp b/src/columns/ColParent.cpp index 60e5ac091..dfd63d4d6 100644 --- a/src/columns/ColParent.cpp +++ b/src/columns/ColParent.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include diff --git a/src/columns/ColProject.cpp b/src/columns/ColProject.cpp index a5890f1bb..e9444b00c 100644 --- a/src/columns/ColProject.cpp +++ b/src/columns/ColProject.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColRType.cpp b/src/columns/ColRType.cpp index fb2a0e7a8..bbe62b2c8 100644 --- a/src/columns/ColRType.cpp +++ b/src/columns/ColRType.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColRecur.cpp b/src/columns/ColRecur.cpp index 844dcb5a5..319705a18 100644 --- a/src/columns/ColRecur.cpp +++ b/src/columns/ColRecur.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColScheduled.cpp b/src/columns/ColScheduled.cpp index e04cb2517..e20ae46ba 100644 --- a/src/columns/ColScheduled.cpp +++ b/src/columns/ColScheduled.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColStart.cpp b/src/columns/ColStart.cpp index 2c7f7bc35..d9ee252a4 100644 --- a/src/columns/ColStart.cpp +++ b/src/columns/ColStart.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColStatus.cpp b/src/columns/ColStatus.cpp index 02d3ccff6..b2b5e6137 100644 --- a/src/columns/ColStatus.cpp +++ b/src/columns/ColStatus.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColTags.cpp b/src/columns/ColTags.cpp index e5d30ab57..8f69d71e8 100644 --- a/src/columns/ColTags.cpp +++ b/src/columns/ColTags.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColTemplate.cpp b/src/columns/ColTemplate.cpp index 055df7e6e..584d08d42 100644 --- a/src/columns/ColTemplate.cpp +++ b/src/columns/ColTemplate.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include diff --git a/src/columns/ColTypeDate.cpp b/src/columns/ColTypeDate.cpp index 310606c29..7a882acb6 100644 --- a/src/columns/ColTypeDate.cpp +++ b/src/columns/ColTypeDate.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include @@ -43,7 +45,7 @@ ColumnTypeDate::ColumnTypeDate () _label = ""; _styles = {"formatted", "julian", - "epoch", + "epoch", "iso", "age", "relative", diff --git a/src/columns/ColTypeDuration.cpp b/src/columns/ColTypeDuration.cpp index 942d69b16..8a2182e58 100644 --- a/src/columns/ColTypeDuration.cpp +++ b/src/columns/ColTypeDuration.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColTypeNumeric.cpp b/src/columns/ColTypeNumeric.cpp index 737e991e8..5f4dd2d4f 100644 --- a/src/columns/ColTypeNumeric.cpp +++ b/src/columns/ColTypeNumeric.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColTypeString.cpp b/src/columns/ColTypeString.cpp index 2d93ca9fc..cf71a42dd 100644 --- a/src/columns/ColTypeString.cpp +++ b/src/columns/ColTypeString.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColUDA.cpp b/src/columns/ColUDA.cpp index 57dd628bb..95244562c 100644 --- a/src/columns/ColUDA.cpp +++ b/src/columns/ColUDA.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/columns/ColUUID.cpp b/src/columns/ColUUID.cpp index 34fbe2a67..ac7822dc0 100644 --- a/src/columns/ColUUID.cpp +++ b/src/columns/ColUUID.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include diff --git a/src/columns/ColUntil.cpp b/src/columns/ColUntil.cpp index d6f74fafe..fada3aea0 100644 --- a/src/columns/ColUntil.cpp +++ b/src/columns/ColUntil.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColUrgency.cpp b/src/columns/ColUrgency.cpp index f1c89720b..cc19b03d3 100644 --- a/src/columns/ColUrgency.cpp +++ b/src/columns/ColUrgency.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include diff --git a/src/columns/ColWait.cpp b/src/columns/ColWait.cpp index e0286eadf..cd7b1633b 100644 --- a/src/columns/ColWait.cpp +++ b/src/columns/ColWait.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/Column.cpp b/src/columns/Column.cpp index 402b4ca3f..5620f6c65 100644 --- a/src/columns/Column.cpp +++ b/src/columns/Column.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdAdd.cpp b/src/commands/CmdAdd.cpp index 21d5475a9..749c53499 100644 --- a/src/commands/CmdAdd.cpp +++ b/src/commands/CmdAdd.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdAliases.cpp b/src/commands/CmdAliases.cpp index e6e1b8e5e..b6b6074c4 100644 --- a/src/commands/CmdAliases.cpp +++ b/src/commands/CmdAliases.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdAnnotate.cpp b/src/commands/CmdAnnotate.cpp index 4a3a17a83..45827aeab 100644 --- a/src/commands/CmdAnnotate.cpp +++ b/src/commands/CmdAnnotate.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdAppend.cpp b/src/commands/CmdAppend.cpp index 1ce75447b..3cfd820db 100644 --- a/src/commands/CmdAppend.cpp +++ b/src/commands/CmdAppend.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdAttributes.cpp b/src/commands/CmdAttributes.cpp index 4088d6391..1ae4d69f0 100644 --- a/src/commands/CmdAttributes.cpp +++ b/src/commands/CmdAttributes.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdBurndown.cpp b/src/commands/CmdBurndown.cpp index be9a252be..2a3006504 100644 --- a/src/commands/CmdBurndown.cpp +++ b/src/commands/CmdBurndown.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdCalendar.cpp b/src/commands/CmdCalendar.cpp index 1a956a605..179ea8a6c 100644 --- a/src/commands/CmdCalendar.cpp +++ b/src/commands/CmdCalendar.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdColor.cpp b/src/commands/CmdColor.cpp index 0721edc1e..8ce3c576a 100644 --- a/src/commands/CmdColor.cpp +++ b/src/commands/CmdColor.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdColumns.cpp b/src/commands/CmdColumns.cpp index d85b23fda..6502882df 100644 --- a/src/commands/CmdColumns.cpp +++ b/src/commands/CmdColumns.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdCommands.cpp b/src/commands/CmdCommands.cpp index b7a91ee2e..6382d5d52 100644 --- a/src/commands/CmdCommands.cpp +++ b/src/commands/CmdCommands.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdConfig.cpp b/src/commands/CmdConfig.cpp index 6f5eab75a..cc845948e 100644 --- a/src/commands/CmdConfig.cpp +++ b/src/commands/CmdConfig.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index f8e6c3e2d..b010b62f9 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdCount.cpp b/src/commands/CmdCount.cpp index 14c6f442b..f577136ba 100644 --- a/src/commands/CmdCount.cpp +++ b/src/commands/CmdCount.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index 378cdc11e..7266b2187 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdDelete.cpp b/src/commands/CmdDelete.cpp index 8bedab285..b27690c81 100644 --- a/src/commands/CmdDelete.cpp +++ b/src/commands/CmdDelete.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include @@ -72,7 +74,7 @@ int CmdDelete::execute (std::string&) // Accumulated project change notifications. std::map projectChanges; - if(filtered.size() > 1) { + if(filtered.size() > 1) { feedback_affected("This command will alter {1} tasks.", filtered.size()); } for (auto& task : filtered) diff --git a/src/commands/CmdDenotate.cpp b/src/commands/CmdDenotate.cpp index ac73f940b..1b3a56916 100644 --- a/src/commands/CmdDenotate.cpp +++ b/src/commands/CmdDenotate.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdDiagnostics.cpp b/src/commands/CmdDiagnostics.cpp index 625dbcd26..7901524cf 100644 --- a/src/commands/CmdDiagnostics.cpp +++ b/src/commands/CmdDiagnostics.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdDone.cpp b/src/commands/CmdDone.cpp index f579364ac..e6b8af922 100644 --- a/src/commands/CmdDone.cpp +++ b/src/commands/CmdDone.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include @@ -129,9 +131,9 @@ int CmdDone::execute (std::string&) rc = 1; } } - + nag (modified); - + // Now list the project changes. for (const auto& change : projectChanges) if (change.first != "") diff --git a/src/commands/CmdDuplicate.cpp b/src/commands/CmdDuplicate.cpp index 46f1070e3..386a301e7 100644 --- a/src/commands/CmdDuplicate.cpp +++ b/src/commands/CmdDuplicate.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdEdit.cpp b/src/commands/CmdEdit.cpp index ca91527b0..7e8c93023 100644 --- a/src/commands/CmdEdit.cpp +++ b/src/commands/CmdEdit.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdExec.cpp b/src/commands/CmdExec.cpp index 111c77798..3c200d6ba 100644 --- a/src/commands/CmdExec.cpp +++ b/src/commands/CmdExec.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdExport.cpp b/src/commands/CmdExport.cpp index e758c9078..dcbd26206 100644 --- a/src/commands/CmdExport.cpp +++ b/src/commands/CmdExport.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdGet.cpp b/src/commands/CmdGet.cpp index 38aae7c35..0c2eb7173 100644 --- a/src/commands/CmdGet.cpp +++ b/src/commands/CmdGet.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdHelp.cpp b/src/commands/CmdHelp.cpp index f755735a7..72c2ea51a 100644 --- a/src/commands/CmdHelp.cpp +++ b/src/commands/CmdHelp.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdHistory.cpp b/src/commands/CmdHistory.cpp index 8b8f7ab83..5f895b8e8 100644 --- a/src/commands/CmdHistory.cpp +++ b/src/commands/CmdHistory.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdIDs.cpp b/src/commands/CmdIDs.cpp index 62d6739e0..3e52aa42b 100644 --- a/src/commands/CmdIDs.cpp +++ b/src/commands/CmdIDs.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdImport.cpp b/src/commands/CmdImport.cpp index 77d6f0ce1..19cef20fc 100644 --- a/src/commands/CmdImport.cpp +++ b/src/commands/CmdImport.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp index f0a1e993c..3a2771a6d 100644 --- a/src/commands/CmdInfo.cpp +++ b/src/commands/CmdInfo.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdLog.cpp b/src/commands/CmdLog.cpp index 91043e195..c58032e57 100644 --- a/src/commands/CmdLog.cpp +++ b/src/commands/CmdLog.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdLogo.cpp b/src/commands/CmdLogo.cpp index a910f7e70..c6d3138d8 100644 --- a/src/commands/CmdLogo.cpp +++ b/src/commands/CmdLogo.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdModify.cpp b/src/commands/CmdModify.cpp index cb407367d..4a16a9427 100644 --- a/src/commands/CmdModify.cpp +++ b/src/commands/CmdModify.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 798c4f43f..419372095 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include @@ -39,7 +41,7 @@ #include /* Adding a new version: - * + * * - Add a new `versionX_Y_Z` method to `NewsItem`, and add news items for the new * release. * - Call the new method in `NewsItem.all()`. Calls should be in version order. diff --git a/src/commands/CmdPrepend.cpp b/src/commands/CmdPrepend.cpp index 0e59d5a30..25a307c49 100644 --- a/src/commands/CmdPrepend.cpp +++ b/src/commands/CmdPrepend.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdProjects.cpp b/src/commands/CmdProjects.cpp index 2c5dea7d4..97d668f80 100644 --- a/src/commands/CmdProjects.cpp +++ b/src/commands/CmdProjects.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdPurge.cpp b/src/commands/CmdPurge.cpp index 09550cdcd..cee795820 100644 --- a/src/commands/CmdPurge.cpp +++ b/src/commands/CmdPurge.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdReports.cpp b/src/commands/CmdReports.cpp index c881f38d0..e8bfa80dc 100644 --- a/src/commands/CmdReports.cpp +++ b/src/commands/CmdReports.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index 53fb6ac03..4bb797d71 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include @@ -284,7 +286,7 @@ int CmdShow::execute (std::string& output) std::string section; - // Look for the first plausible argument which could be a pattern + // Look for the first plausible argument which could be a pattern if (words.size ()) section = words[0]; diff --git a/src/commands/CmdStart.cpp b/src/commands/CmdStart.cpp index e63edd1cb..1566e1bfb 100644 --- a/src/commands/CmdStart.cpp +++ b/src/commands/CmdStart.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdStats.cpp b/src/commands/CmdStats.cpp index 668b1cf0d..be658bc5f 100644 --- a/src/commands/CmdStats.cpp +++ b/src/commands/CmdStats.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdStop.cpp b/src/commands/CmdStop.cpp index f9d1cec6d..a46b5840d 100644 --- a/src/commands/CmdStop.cpp +++ b/src/commands/CmdStop.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdSummary.cpp b/src/commands/CmdSummary.cpp index 4a23384ba..5564a8829 100644 --- a/src/commands/CmdSummary.cpp +++ b/src/commands/CmdSummary.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index 836d6ebe0..3a6dc8144 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdTags.cpp b/src/commands/CmdTags.cpp index fcf58b631..9d43d4040 100644 --- a/src/commands/CmdTags.cpp +++ b/src/commands/CmdTags.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdTimesheet.cpp b/src/commands/CmdTimesheet.cpp index eee64f007..63eddf328 100644 --- a/src/commands/CmdTimesheet.cpp +++ b/src/commands/CmdTimesheet.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdUDAs.cpp b/src/commands/CmdUDAs.cpp index d10badcd9..55fa33e6f 100644 --- a/src/commands/CmdUDAs.cpp +++ b/src/commands/CmdUDAs.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdUndo.cpp b/src/commands/CmdUndo.cpp index d51d54f97..cb3ec26c4 100644 --- a/src/commands/CmdUndo.cpp +++ b/src/commands/CmdUndo.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include diff --git a/src/commands/CmdUnique.cpp b/src/commands/CmdUnique.cpp index cda54f196..ca67b3075 100644 --- a/src/commands/CmdUnique.cpp +++ b/src/commands/CmdUnique.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdUrgency.cpp b/src/commands/CmdUrgency.cpp index 55487e507..6260d8d2c 100644 --- a/src/commands/CmdUrgency.cpp +++ b/src/commands/CmdUrgency.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/CmdVersion.cpp b/src/commands/CmdVersion.cpp index efdf90d6d..1042071d6 100644 --- a/src/commands/CmdVersion.cpp +++ b/src/commands/CmdVersion.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/commands/Command.cpp b/src/commands/Command.cpp index 15b311aae..ba028b4a9 100644 --- a/src/commands/Command.cpp +++ b/src/commands/Command.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/dependency.cpp b/src/dependency.cpp index 154859e32..ab17c3d08 100644 --- a/src/dependency.cpp +++ b/src/dependency.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/feedback.cpp b/src/feedback.cpp index 131be6627..4905b06d0 100644 --- a/src/feedback.cpp +++ b/src/feedback.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/legacy.cpp b/src/legacy.cpp index 7cf9f9321..965e1d227 100644 --- a/src/legacy.cpp +++ b/src/legacy.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/main.cpp b/src/main.cpp index 07ac2fd22..d360876d0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/nag.cpp b/src/nag.cpp index f95c5201b..55085a8b2 100644 --- a/src/nag.cpp +++ b/src/nag.cpp @@ -26,6 +26,8 @@ #include #include +// cmake.h include header must come first + #include #include #include diff --git a/src/recur.cpp b/src/recur.cpp index 524c912d3..463534ecf 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/rules.cpp b/src/rules.cpp index 760858487..4edf81d5f 100644 --- a/src/rules.cpp +++ b/src/rules.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/src/sort.cpp b/src/sort.cpp index ac5c3d418..25f690fe4 100644 --- a/src/sort.cpp +++ b/src/sort.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include @@ -76,12 +78,12 @@ void sort_projects ( { parent_pos = std::find_if (sorted.begin (), sorted.end (), [&parent](const std::pair & item) { return item.first == parent; }); - + // if parent does not exist yet: insert into sorted view if (parent_pos == sorted.end ()) sorted.emplace_back (parent, 1); } - + // insert new element below latest parent sorted.insert ((parent_pos == sorted.end ()) ? parent_pos : ++parent_pos, project); } @@ -100,7 +102,7 @@ void sort_projects ( std::map allProjectsInt; for (auto& p : allProjects) allProjectsInt[p.first] = (int) p.second; - + sort_projects (sorted, allProjectsInt); } diff --git a/src/tc/Replica.cpp b/src/tc/Replica.cpp index f33a617dc..e1a2fb1ce 100644 --- a/src/tc/Replica.cpp +++ b/src/tc/Replica.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include "tc/Replica.h" #include "tc/Task.h" diff --git a/src/tc/Server.cpp b/src/tc/Server.cpp index fe4717afd..5e43d1c2e 100644 --- a/src/tc/Server.cpp +++ b/src/tc/Server.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include "tc/Server.h" #include "tc/util.h" diff --git a/src/tc/Task.cpp b/src/tc/Task.cpp index 69a354a9f..7b3a4e217 100644 --- a/src/tc/Task.cpp +++ b/src/tc/Task.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include "tc/Task.h" #include "tc/util.h" diff --git a/src/tc/WorkingSet.cpp b/src/tc/WorkingSet.cpp index 4f116aaa5..b089f347c 100644 --- a/src/tc/WorkingSet.cpp +++ b/src/tc/WorkingSet.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include "tc/WorkingSet.h" #include "tc/Task.h" diff --git a/src/tc/util.cpp b/src/tc/util.cpp index 567c4a05b..318f9ed0f 100644 --- a/src/tc/util.cpp +++ b/src/tc/util.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include "tc/Replica.h" diff --git a/src/util.cpp b/src/util.cpp index 02a97c44c..da6eb4f93 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include // If is included, put it after , because it includes @@ -67,7 +69,7 @@ static const char* newline = "\n"; static const char* noline = ""; -//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// static void signal_handler (int s) { if (s == SIGINT) diff --git a/src/util.h b/src/util.h index f900e989e..62e8223b0 100644 --- a/src/util.h +++ b/src/util.h @@ -28,6 +28,8 @@ #define INCLUDED_UTIL #include +// cmake.h include header must come first + #include #include #include diff --git a/test/col.test.cpp b/test/col.test.cpp index a579d52b8..c4be0ea4f 100644 --- a/test/col.test.cpp +++ b/test/col.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/dom.test.cpp b/test/dom.test.cpp index 214955cda..34a815476 100644 --- a/test/dom.test.cpp +++ b/test/dom.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/eval.test.cpp b/test/eval.test.cpp index 85ed424c4..f5969bdb5 100644 --- a/test/eval.test.cpp +++ b/test/eval.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/lexer.test.cpp b/test/lexer.test.cpp index 8ebce5d4c..43a8af3c0 100644 --- a/test/lexer.test.cpp +++ b/test/lexer.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/t.test.cpp b/test/t.test.cpp index 63739c6da..e0e35c4c0 100644 --- a/test/t.test.cpp +++ b/test/t.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/tc.test.cpp b/test/tc.test.cpp index 1d4c7e519..724105404 100644 --- a/test/tc.test.cpp +++ b/test/tc.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include @@ -84,7 +86,7 @@ int main (int, char**) t.is ((int)tasks.size(), 1, "all_tasks returns one task"); //// Task - + task = std::move(tasks[0]); t.is (task.get_uuid(), uuid, "returned task has correct uuid"); diff --git a/test/tdb2.test.cpp b/test/tdb2.test.cpp index 059663349..03b1d9f84 100644 --- a/test/tdb2.test.cpp +++ b/test/tdb2.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/tw-2689.test.cpp b/test/tw-2689.test.cpp index 3b67f1f4e..e18fdb021 100644 --- a/test/tw-2689.test.cpp +++ b/test/tw-2689.test.cpp @@ -26,6 +26,8 @@ #include #include +// cmake.h include header must come first + #include #include #include diff --git a/test/util.test.cpp b/test/util.test.cpp index 74901dfd5..36276a38b 100644 --- a/test/util.test.cpp +++ b/test/util.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_add.test.cpp b/test/variant_add.test.cpp index 56dc8ab46..8c6f22e9f 100644 --- a/test/variant_add.test.cpp +++ b/test/variant_add.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_and.test.cpp b/test/variant_and.test.cpp index 9963556e8..237bbd02d 100644 --- a/test/variant_and.test.cpp +++ b/test/variant_and.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_cast.test.cpp b/test/variant_cast.test.cpp index 8eb2c63cc..ac6c246ca 100644 --- a/test/variant_cast.test.cpp +++ b/test/variant_cast.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_divide.test.cpp b/test/variant_divide.test.cpp index 70a895a4a..676c25870 100644 --- a/test/variant_divide.test.cpp +++ b/test/variant_divide.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_equal.test.cpp b/test/variant_equal.test.cpp index b89d2e782..5b8c6c530 100644 --- a/test/variant_equal.test.cpp +++ b/test/variant_equal.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_exp.test.cpp b/test/variant_exp.test.cpp index b8c9d227c..ac7c1e085 100644 --- a/test/variant_exp.test.cpp +++ b/test/variant_exp.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_gt.test.cpp b/test/variant_gt.test.cpp index 69dd44742..86d2b6317 100644 --- a/test/variant_gt.test.cpp +++ b/test/variant_gt.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_gte.test.cpp b/test/variant_gte.test.cpp index 093358f0e..708a598e1 100644 --- a/test/variant_gte.test.cpp +++ b/test/variant_gte.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_inequal.test.cpp b/test/variant_inequal.test.cpp index 74c91b965..348b7b9a7 100644 --- a/test/variant_inequal.test.cpp +++ b/test/variant_inequal.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_lt.test.cpp b/test/variant_lt.test.cpp index 56394f4c9..897168c44 100644 --- a/test/variant_lt.test.cpp +++ b/test/variant_lt.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_lte.test.cpp b/test/variant_lte.test.cpp index b218ecdb9..30eda6398 100644 --- a/test/variant_lte.test.cpp +++ b/test/variant_lte.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_match.test.cpp b/test/variant_match.test.cpp index 5b39e62dc..97a7f6432 100644 --- a/test/variant_match.test.cpp +++ b/test/variant_match.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_math.test.cpp b/test/variant_math.test.cpp index b8a7b3a21..f247d4c69 100644 --- a/test/variant_math.test.cpp +++ b/test/variant_math.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_modulo.test.cpp b/test/variant_modulo.test.cpp index 75a9d2ad2..1f0e53c52 100644 --- a/test/variant_modulo.test.cpp +++ b/test/variant_modulo.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_multiply.test.cpp b/test/variant_multiply.test.cpp index 88f84682a..6dcb20159 100644 --- a/test/variant_multiply.test.cpp +++ b/test/variant_multiply.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_nomatch.test.cpp b/test/variant_nomatch.test.cpp index a811c558e..0f069f9d9 100644 --- a/test/variant_nomatch.test.cpp +++ b/test/variant_nomatch.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_not.test.cpp b/test/variant_not.test.cpp index 4337745d0..aad898cc3 100644 --- a/test/variant_not.test.cpp +++ b/test/variant_not.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_or.test.cpp b/test/variant_or.test.cpp index 8b097b9e9..010406d7f 100644 --- a/test/variant_or.test.cpp +++ b/test/variant_or.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_partial.test.cpp b/test/variant_partial.test.cpp index a545979c0..66b6385cf 100644 --- a/test/variant_partial.test.cpp +++ b/test/variant_partial.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_subtract.test.cpp b/test/variant_subtract.test.cpp index 331073e38..e545d4422 100644 --- a/test/variant_subtract.test.cpp +++ b/test/variant_subtract.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/variant_xor.test.cpp b/test/variant_xor.test.cpp index 54a4f6a0f..cd32ee24f 100644 --- a/test/variant_xor.test.cpp +++ b/test/variant_xor.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include diff --git a/test/view.test.cpp b/test/view.test.cpp index c65045c5d..59f668298 100644 --- a/test/view.test.cpp +++ b/test/view.test.cpp @@ -25,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////// #include +// cmake.h include header must come first + #include #include #include From 665aeeef6126e584f5e95e7567126f5490e87834 Mon Sep 17 00:00:00 2001 From: Felix Schurk Date: Wed, 24 Jul 2024 19:56:55 +0200 Subject: [PATCH 091/242] reflect updated code-style and workflow in documentation --- doc/devel/contrib/coding_style.md | 32 ++++++++++--------------------- doc/devel/contrib/development.md | 1 + 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/doc/devel/contrib/coding_style.md b/doc/devel/contrib/coding_style.md index 2077bc6b3..7f30d65fb 100644 --- a/doc/devel/contrib/coding_style.md +++ b/doc/devel/contrib/coding_style.md @@ -1,30 +1,18 @@ # Coding Style -The coding style used for the Taskwarrior, Taskserver, and other codebases is deliberately kept simple and a little vague. -This is because there are many languages involved (C++, C, Python, sh, bash, HTML, troff and more), and specіfying those would be a major effort that detracts from the main focus which is improving the software. +The coding style used for the Taskwarrior is based on the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html), with small modifications in the line length. -Instead, the general guideline is simply this: +# Automatic formatting -Make all changes and additions such that they blend in perfectly with the surrounding code, so it looks like only one person worked on the source, and that person is rigidly consistent. +In order to have consistancy and automatic formatting [pre-commit](https://pre-commit.com) is used to apply [clang-format](https://clang.llvm.org/docs/ClangFormat.html) and [black](https://github.com/psf/black) are used. +In order to set them up locally please run: +```python +pip install pre-commit +pre-commit install +``` -To be a little more explicit: - -## C++ - -- All functionality in C++17 is allowed. - -- Indent C++ code using two spaces, no tabs - -- Surround operators and expression terms with a space. - This includes a space between a function name and its list of arguments. - -- No cuddled braces - -- Class names are capitalized, variable names are not - -## Python - -Follow [PEP8](https://www.python.org/dev/peps/pep-0008/) as much as possible. +For more information refer to the [quick-start of pre-commit](https://pre-commit.com/#quick-start). +The setup is also included in the CI, hence if one can not install it automatically then the CI will take care for it in the PR. ## Rust diff --git a/doc/devel/contrib/development.md b/doc/devel/contrib/development.md index a5c904c5e..0301eea07 100644 --- a/doc/devel/contrib/development.md +++ b/doc/devel/contrib/development.md @@ -9,6 +9,7 @@ ## Install Optional Dependencies: * python 3 (for running the test suite) + * pre-commit (for applying formatting changes locally) * clangd or ccls (for C++ integration in many editors) * rust-analyzer (for Rust integration in many editors) From 93356b39c3086fdf8dd41d7357bb1c115ff69cb1 Mon Sep 17 00:00:00 2001 From: Felix Schurk Date: Mon, 29 Jul 2024 22:34:51 +0200 Subject: [PATCH 092/242] add initial bulk run from pre-commit over all files --- .github/workflows/release-check.yaml | 1 - ChangeLog | 3 +- LICENSE | 1 - cmake.h.in | 1 - doc/devel/rfcs/dom.md | 4 +- doc/devel/rfcs/task.md | 1 - doc/man/task-sync.5.in | 6 +- doc/rc/bubblegum-256.theme | 1 - doc/rc/dark-16.theme | 1 - doc/rc/dark-256.theme | 1 - doc/rc/dark-blue-256.theme | 1 - doc/rc/dark-gray-256.theme | 1 - doc/rc/dark-gray-blue-256.theme | 1 - doc/rc/dark-red-256.theme | 1 - doc/rc/dark-violets-256.theme | 1 - doc/rc/dark-yellow-green.theme | 1 - doc/rc/light-16.theme | 1 - doc/rc/light-256.theme | 3 +- doc/rc/no-color.theme | 1 - doc/rc/refresh | 1 - doc/rc/solarized-dark-256.theme | 1 - doc/rc/solarized-light-256.theme | 1 - misc/themes/README | 1 - misc/themes/setup | 1 - performance/CMakeLists.txt | 2 - performance/compare_runs.py | 25 +- performance/run_perf | 1 - performance/sample-text.txt | 2 +- scripts/CMakeLists.txt | 1 - scripts/add-ons/update-holidays.pl | 1 - scripts/hooks/README | 1 - scripts/hooks/on-exit.shadow-file | 1 - scripts/hooks/on-launch | 1 - scripts/vim/ftdetect/task.vim | 2 +- scripts/zsh/_task | 2 +- src/CLI2.cpp | 2031 +++++------ src/CLI2.h | 158 +- src/Context.cpp | 1594 ++++----- src/Context.h | 148 +- src/DOM.cpp | 545 ++- src/DOM.h | 69 +- src/Eval.cpp | 813 ++--- src/Eval.h | 71 +- src/Filter.cpp | 249 +- src/Filter.h | 33 +- src/Hooks.cpp | 533 ++- src/Hooks.h | 56 +- src/Lexer.cpp | 1303 +++---- src/Lexer.h | 161 +- src/TDB2.cpp | 375 +- src/TDB2.h | 76 +- src/Task.cpp | 2493 ++++++------- src/Task.h | 256 +- src/Variant.cpp | 3186 +++++++++-------- src/Variant.h | 130 +- src/Version.cpp | 18 +- src/Version.h | 6 +- src/ViewTask.cpp | 294 +- src/ViewTask.h | 101 +- src/calc.cpp | 93 +- src/columns/ColDepends.cpp | 193 +- src/columns/ColDepends.h | 17 +- src/columns/ColDescription.cpp | 221 +- src/columns/ColDescription.h | 13 +- src/columns/ColDue.cpp | 15 +- src/columns/ColDue.h | 9 +- src/columns/ColEnd.cpp | 5 +- src/columns/ColEnd.h | 7 +- src/columns/ColEntry.cpp | 16 +- src/columns/ColEntry.h | 9 +- src/columns/ColID.cpp | 49 +- src/columns/ColID.h | 13 +- src/columns/ColIMask.cpp | 29 +- src/columns/ColIMask.h | 13 +- src/columns/ColLast.cpp | 29 +- src/columns/ColLast.h | 13 +- src/columns/ColMask.cpp | 29 +- src/columns/ColMask.h | 13 +- src/columns/ColModified.cpp | 5 +- src/columns/ColModified.h | 7 +- src/columns/ColParent.cpp | 42 +- src/columns/ColParent.h | 13 +- src/columns/ColProject.cpp | 125 +- src/columns/ColProject.h | 15 +- src/columns/ColRType.cpp | 58 +- src/columns/ColRType.h | 17 +- src/columns/ColRecur.cpp | 89 +- src/columns/ColRecur.h | 17 +- src/columns/ColScheduled.cpp | 13 +- src/columns/ColScheduled.h | 9 +- src/columns/ColStart.cpp | 50 +- src/columns/ColStart.h | 13 +- src/columns/ColStatus.cpp | 92 +- src/columns/ColStatus.h | 15 +- src/columns/ColTags.cpp | 151 +- src/columns/ColTags.h | 17 +- src/columns/ColTemplate.cpp | 42 +- src/columns/ColTemplate.h | 13 +- src/columns/ColTypeDate.cpp | 216 +- src/columns/ColTypeDate.h | 22 +- src/columns/ColTypeDuration.cpp | 44 +- src/columns/ColTypeDuration.h | 14 +- src/columns/ColTypeNumeric.cpp | 45 +- src/columns/ColTypeNumeric.h | 14 +- src/columns/ColTypeString.cpp | 63 +- src/columns/ColTypeString.h | 14 +- src/columns/ColUDA.cpp | 354 +- src/columns/ColUDA.h | 74 +- src/columns/ColUUID.cpp | 36 +- src/columns/ColUUID.h | 13 +- src/columns/ColUntil.cpp | 5 +- src/columns/ColUntil.h | 7 +- src/columns/ColUrgency.cpp | 31 +- src/columns/ColUrgency.h | 13 +- src/columns/ColWait.cpp | 5 +- src/columns/ColWait.h | 7 +- src/columns/Column.cpp | 369 +- src/columns/Column.h | 76 +- src/commands/CmdAdd.cpp | 60 +- src/commands/CmdAdd.h | 12 +- src/commands/CmdAliases.cpp | 31 +- src/commands/CmdAliases.h | 12 +- src/commands/CmdAnnotate.cpp | 105 +- src/commands/CmdAnnotate.h | 12 +- src/commands/CmdAppend.cpp | 105 +- src/commands/CmdAppend.h | 12 +- src/commands/CmdAttributes.cpp | 40 +- src/commands/CmdAttributes.h | 12 +- src/commands/CmdBurndown.cpp | 948 +++-- src/commands/CmdBurndown.h | 30 +- src/commands/CmdCalc.cpp | 46 +- src/commands/CmdCalc.h | 12 +- src/commands/CmdCalendar.cpp | 609 ++-- src/commands/CmdCalendar.h | 20 +- src/commands/CmdColor.cpp | 245 +- src/commands/CmdColor.h | 12 +- src/commands/CmdColumns.cpp | 143 +- src/commands/CmdColumns.h | 21 +- src/commands/CmdCommands.cpp | 200 +- src/commands/CmdCommands.h | 30 +- src/commands/CmdConfig.cpp | 188 +- src/commands/CmdConfig.h | 25 +- src/commands/CmdContext.cpp | 358 +- src/commands/CmdContext.h | 41 +- src/commands/CmdCount.cpp | 39 +- src/commands/CmdCount.h | 12 +- src/commands/CmdCustom.cpp | 264 +- src/commands/CmdCustom.h | 20 +- src/commands/CmdDelete.cpp | 177 +- src/commands/CmdDelete.h | 12 +- src/commands/CmdDenotate.cpp | 137 +- src/commands/CmdDenotate.h | 12 +- src/commands/CmdDiagnostics.cpp | 269 +- src/commands/CmdDiagnostics.h | 12 +- src/commands/CmdDone.cpp | 114 +- src/commands/CmdDone.h | 12 +- src/commands/CmdDuplicate.cpp | 128 +- src/commands/CmdDuplicate.h | 12 +- src/commands/CmdEdit.cpp | 907 ++--- src/commands/CmdEdit.h | 30 +- src/commands/CmdExec.cpp | 38 +- src/commands/CmdExec.h | 12 +- src/commands/CmdExport.cpp | 131 +- src/commands/CmdExport.h | 17 +- src/commands/CmdGet.cpp | 68 +- src/commands/CmdGet.h | 12 +- src/commands/CmdHelp.cpp | 332 +- src/commands/CmdHelp.h | 16 +- src/commands/CmdHistory.cpp | 711 ++-- src/commands/CmdHistory.h | 44 +- src/commands/CmdIDs.cpp | 290 +- src/commands/CmdIDs.h | 61 +- src/commands/CmdImport.cpp | 163 +- src/commands/CmdImport.h | 18 +- src/commands/CmdInfo.cpp | 617 ++-- src/commands/CmdInfo.h | 16 +- src/commands/CmdLog.cpp | 44 +- src/commands/CmdLog.h | 12 +- src/commands/CmdLogo.cpp | 120 +- src/commands/CmdLogo.h | 12 +- src/commands/CmdModify.cpp | 203 +- src/commands/CmdModify.h | 26 +- src/commands/CmdNews.cpp | 745 ++-- src/commands/CmdNews.h | 39 +- src/commands/CmdPrepend.cpp | 105 +- src/commands/CmdPrepend.h | 12 +- src/commands/CmdProjects.cpp | 166 +- src/commands/CmdProjects.h | 21 +- src/commands/CmdPurge.cpp | 143 +- src/commands/CmdPurge.h | 23 +- src/commands/CmdReports.cpp | 91 +- src/commands/CmdReports.h | 12 +- src/commands/CmdShow.cpp | 511 ++- src/commands/CmdShow.h | 21 +- src/commands/CmdStart.cpp | 109 +- src/commands/CmdStart.h | 12 +- src/commands/CmdStats.cpp | 328 +- src/commands/CmdStats.h | 12 +- src/commands/CmdStop.cpp | 99 +- src/commands/CmdStop.h | 12 +- src/commands/CmdSummary.cpp | 200 +- src/commands/CmdSummary.h | 12 +- src/commands/CmdSync.cpp | 75 +- src/commands/CmdSync.h | 12 +- src/commands/CmdTags.cpp | 214 +- src/commands/CmdTags.h | 21 +- src/commands/CmdTimesheet.cpp | 180 +- src/commands/CmdTimesheet.h | 14 +- src/commands/CmdUDAs.cpp | 202 +- src/commands/CmdUDAs.h | 21 +- src/commands/CmdUndo.cpp | 26 +- src/commands/CmdUndo.h | 12 +- src/commands/CmdUnique.cpp | 70 +- src/commands/CmdUnique.h | 12 +- src/commands/CmdUrgency.cpp | 50 +- src/commands/CmdUrgency.h | 12 +- src/commands/CmdVersion.cpp | 97 +- src/commands/CmdVersion.h | 21 +- src/commands/Command.cpp | 440 +-- src/commands/Command.h | 77 +- src/dependency.cpp | 115 +- src/feedback.cpp | 274 +- src/legacy.cpp | 98 +- src/lex.cpp | 17 +- src/main.cpp | 41 +- src/main.h | 87 +- src/nag.cpp | 24 +- src/recur.cpp | 403 +-- src/rules.cpp | 306 +- src/sort.cpp | 299 +- src/tc/Replica.cpp | 196 +- src/tc/Replica.h | 153 +- src/tc/Server.cpp | 81 +- src/tc/Server.h | 72 +- src/tc/Task.cpp | 106 +- src/tc/Task.h | 160 +- src/tc/WorkingSet.cpp | 53 +- src/tc/WorkingSet.h | 61 +- src/tc/lib/taskchampion.h | 114 +- src/tc/util.cpp | 34 +- src/tc/util.h | 23 +- src/util.cpp | 258 +- src/util.h | 31 +- test/README.md | 2 +- test/abbreviation.test.py | 9 +- test/add.test.py | 32 +- test/alias.test.py | 77 +- test/annotate.test.py | 104 +- test/append.test.py | 2 + test/args.test.py | 20 +- test/basetest/CMakeLists.txt | 2 +- test/basetest/exceptions.py | 26 +- test/basetest/hooks.py | 176 +- test/basetest/meta.py | 2 + test/basetest/task.py | 69 +- test/basetest/testing.py | 6 +- test/basetest/utils.py | 88 +- test/bash_completion.test.py | 24 +- test/blocked.test.py | 2 + test/bulk.test.py | 6 +- test/burndown.test.py | 2 + test/calc.test.py | 17 +- test/calendar.test.py | 49 +- test/caseless.test.py | 26 +- test/col.test.cpp | 48 +- test/color.cmd.test.py | 11 +- test/color.rules.test.py | 239 +- test/columns.test.py | 153 +- test/commands.test.py | 10 +- test/completed.test.py | 8 +- test/configuration.test.py | 6 +- test/confirmation.test.py | 8 +- test/context.test.py | 308 +- test/count.test.py | 2 + test/custom.config.test.py | 12 +- test/custom.recur_ind.test.py | 14 +- test/custom.tag_ind.test.py | 8 +- test/custom.test.py | 17 +- test/date.iso.test.py | 2 + test/dateformat.test.py | 69 +- test/datesort.test.py | 16 +- test/datetime-negative.test.py | 220 +- test/debug.test.py | 2 + test/default.test.py | 18 +- test/delete.test.py | 6 +- test/denotate.test.py | 6 +- test/dependencies.test.py | 61 +- test/diag.test.py | 8 +- test/diag_color.test.py | 2 + test/dom.test.cpp | 77 +- test/dom2.test.py | 187 +- test/due.test.py | 2 + test/duplicate.test.py | 11 +- test/edit.test.py | 6 +- test/encoding.test.py | 2 + test/enpassant.test.py | 39 +- test/eval.test.cpp | 168 +- test/exec.test.py | 3 + test/export.test.py | 87 +- test/feature.559.test.py | 6 +- test/feature.default.project.test.py | 24 +- test/feature.print.empty.columns.test.py | 3 +- test/feature.recurrence.test.py | 2 + test/feedback.test.py | 2 + test/filter.test.py | 257 +- test/fontunderline.test.py | 34 +- test/format.test.py | 32 +- test/gc.test.py | 2 + test/helpers.test.py | 11 +- test/history.test.py | 5 + test/hooks.env.test.py | 26 +- test/hooks.on-add.test.py | 28 +- test/hooks.on-exit.test.py | 13 +- test/hooks.on-launch.test.py | 11 +- test/hooks.on-modify.test.py | 26 +- test/hyphenate.test.py | 13 +- test/ids.test.py | 21 +- test/import.test.py | 25 +- test/info.test.py | 12 +- test/lexer.test.cpp | 1261 ++++--- test/limit.test.py | 2 + test/list.all.projects.test.py | 3 + test/log.test.py | 4 +- test/logo.test.py | 2 + test/math.test.py | 25 +- test/modify.test.py | 5 + test/nag.test.py | 10 +- test/news.test.py | 1 + test/obfuscate.test.py | 2 + test/oldest.test.py | 2 + test/operators.test.py | 2 + test/overdue.test.py | 2 + test/partial.test.py | 10 +- test/prepend.test.py | 2 + test/pri_sort.test.py | 2 + test/project.test.py | 75 +- test/purge.test.py | 9 +- test/quotes.test.py | 26 +- test/rc.override.test.py | 4 +- test/recurrence.test.py | 185 +- test/reports.test.py | 2 + test/scripts/test_macos.sh | 2 +- test/search.test.py | 87 +- test/sequence.test.py | 11 +- test/shell.test.py | 8 +- test/show.test.py | 6 +- test/simpletap/CMakeLists.txt | 2 +- test/simpletap/__init__.py | 45 +- test/sorting.test.py | 238 +- test/special.test.py | 17 +- test/start.test.py | 10 +- test/stats.test.py | 2 + test/substitute.test.py | 2 + test/sugar.test.py | 8 +- test/summary.test.py | 12 +- test/t.test.cpp | 150 +- test/tag.test.py | 41 +- test/taskrc.test.py | 2 + test/tc.test.cpp | 81 +- test/tdb2.test.cpp | 118 +- test/template.test.py | 31 +- test/test.cpp | 481 +-- test/test.h | 53 +- test/test_hooks/on-exit-misbehave2 | 1 - test/test_hooks/on-launch-bad | 1 - test/test_hooks/on-launch-good | 1 - test/test_hooks/on-launch-good-env | 1 - test/test_hooks/on-launch-misbehave1 | 1 - test/test_hooks/on-launch-misbehave2 | 1 - .../on-modify-for-template-badexit.py | 2 +- test/test_hooks/on-modify-for-template.py | 2 +- test/timesheet.test.py | 15 +- test/tw-1379.test.py | 5 +- test/tw-1837.test.py | 4 + test/tw-20.test.py | 8 +- test/tw-2575.test.py | 103 +- test/tw-262.test.py | 9 +- test/tw-2689.test.cpp | 89 +- test/tw-295.test.py | 11 +- test/tw-3527.test.py | 2 +- test/uda.test.py | 34 +- test/uda_orphan.test.py | 8 +- test/uda_report.test.py | 2 + test/uda_sort.test.py | 111 +- test/undo.test.py | 38 +- test/unicode.test.py | 23 +- test/unique.test.py | 14 +- test/upgrade.test.py | 2 + test/urgency.test.py | 136 +- test/urgency_inherit.test.py | 2 + test/util.test.cpp | 78 +- test/uuid.test.py | 13 +- test/variant_add.test.cpp | 198 +- test/variant_and.test.cpp | 178 +- test/variant_cast.test.cpp | 340 +- test/variant_divide.test.cpp | 278 +- test/variant_equal.test.cpp | 166 +- test/variant_exp.test.cpp | 306 +- test/variant_gt.test.cpp | 166 +- test/variant_gte.test.cpp | 166 +- test/variant_inequal.test.cpp | 166 +- test/variant_lt.test.cpp | 166 +- test/variant_lte.test.cpp | 166 +- test/variant_match.test.cpp | 392 +- test/variant_math.test.cpp | 16 +- test/variant_modulo.test.cpp | 294 +- test/variant_multiply.test.cpp | 240 +- test/variant_nomatch.test.cpp | 392 +- test/variant_not.test.cpp | 66 +- test/variant_or.test.cpp | 178 +- test/variant_partial.test.cpp | 254 +- test/variant_subtract.test.cpp | 234 +- test/variant_xor.test.cpp | 250 +- test/verbose.test.py | 18 +- test/version.test.py | 17 +- test/view.test.cpp | 180 +- test/wait.test.py | 24 +- xtask/Cargo.toml | 2 +- 418 files changed, 21354 insertions(+), 23858 deletions(-) diff --git a/.github/workflows/release-check.yaml b/.github/workflows/release-check.yaml index e9cf424ba..7ec39eb1d 100644 --- a/.github/workflows/release-check.yaml +++ b/.github/workflows/release-check.yaml @@ -25,4 +25,3 @@ jobs: cd task-*.*.* && cmake -S. -Bbuild && cmake --build build --target task_executable - diff --git a/ChangeLog b/ChangeLog index 3f7d99266..6a8b78bb5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -44,7 +44,7 @@ - Akash Shanmugaraj - Andrew Savchenko - - Dathan Bennett + - Dathan Bennett - Dustin Mitchell - dbr/Ben - Felix Schurk @@ -2785,4 +2785,3 @@ regular usage to determine which features were needed or unnecessary.] - Usage. ------ start ----------------------------------- - diff --git a/LICENSE b/LICENSE index 89dcf89c2..96d1910f0 100644 --- a/LICENSE +++ b/LICENSE @@ -21,4 +21,3 @@ 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. - diff --git a/cmake.h.in b/cmake.h.in index 6586a7f03..ec294bf99 100644 --- a/cmake.h.in +++ b/cmake.h.in @@ -58,4 +58,3 @@ /* Undefine this to eliminate the execute command */ #define HAVE_EXECUTE 1 - diff --git a/doc/devel/rfcs/dom.md b/doc/devel/rfcs/dom.md index ccc00515b..2a9a76586 100644 --- a/doc/devel/rfcs/dom.md +++ b/doc/devel/rfcs/dom.md @@ -227,14 +227,14 @@ An example is to make two reports share the same description: $ task config -- report.ls.description rc.report.list.description -This sets the description for the `ls` report to be a reference to the description of the `list` report. +This sets the description for the `ls` report to be a reference to the description of the `list` report. This reference is not evaluated when the entry is written, but is evaluated every time the value is read, thus providing late-bound behavior. Then if the description of the `list` report changes, so does that of the `ls` report automatically. ## Implementation Details -These notes list a series of anticipated changes to the codebase. +These notes list a series of anticipated changes to the codebase. - The `src/columns/Col*` objects will implement type-specific and attribute-specific DOM support. DOM reference lookup will defer to the column objects first. diff --git a/doc/devel/rfcs/task.md b/doc/devel/rfcs/task.md index a1ab4927f..312b8ee72 100644 --- a/doc/devel/rfcs/task.md +++ b/doc/devel/rfcs/task.md @@ -466,4 +466,3 @@ Given that the configuration is not present in the JSON format of a task, any fi This means that if a task contains a UDA, unless the meaning of it is understood, it MUST be preserved. UDAs may have one of four types: string, numeric, date and duration. - diff --git a/doc/man/task-sync.5.in b/doc/man/task-sync.5.in index 9e1cdb320..18f8a8189 100644 --- a/doc/man/task-sync.5.in +++ b/doc/man/task-sync.5.in @@ -101,7 +101,7 @@ Then configure Taskwarrior with: $ task config sync.gcp.bucket .fi -However you can bring your own service account credentials if your +However you can bring your own service account credentials if your `application-default` is already being used by some other application To begin, navigate to the "IAM and Admin" section in the Navigation Menu, then select "Roles." @@ -112,8 +112,8 @@ Provide an appropriate name and description for the new role. Add permissions to your new role using the filter "Service:storage" (not the "Filter permissions by role" input box). Select the following permissions: - - storage.buckets.create - - storage.buckets.get + - storage.buckets.create + - storage.buckets.get - storage.buckets.update - storage.objects.create - storage.objects.delete diff --git a/doc/rc/bubblegum-256.theme b/doc/rc/bubblegum-256.theme index 2bb005611..ac7b87e73 100644 --- a/doc/rc/bubblegum-256.theme +++ b/doc/rc/bubblegum-256.theme @@ -96,4 +96,3 @@ color.sync.rejected=rgb103 # Command: undo color.undo.before=rgb103 color.undo.after=rgb305 - diff --git a/doc/rc/dark-16.theme b/doc/rc/dark-16.theme index 6ac6505d7..c7778848e 100644 --- a/doc/rc/dark-16.theme +++ b/doc/rc/dark-16.theme @@ -98,4 +98,3 @@ color.sync.rejected=red # Command: undo color.undo.after=green color.undo.before=red - diff --git a/doc/rc/dark-256.theme b/doc/rc/dark-256.theme index 4398fa96a..17fcb8fe6 100644 --- a/doc/rc/dark-256.theme +++ b/doc/rc/dark-256.theme @@ -95,4 +95,3 @@ color.sync.rejected=color9 # Command: undo color.undo.after=color2 color.undo.before=color1 - diff --git a/doc/rc/dark-blue-256.theme b/doc/rc/dark-blue-256.theme index 91b1c6ca4..ec974ce7e 100644 --- a/doc/rc/dark-blue-256.theme +++ b/doc/rc/dark-blue-256.theme @@ -95,4 +95,3 @@ color.sync.rejected=rgb004 # Command: undo color.undo.after=rgb035 color.undo.before=rgb013 - diff --git a/doc/rc/dark-gray-256.theme b/doc/rc/dark-gray-256.theme index f169abaa4..b6fdb343e 100644 --- a/doc/rc/dark-gray-256.theme +++ b/doc/rc/dark-gray-256.theme @@ -95,4 +95,3 @@ color.sync.rejected=gray5 on gray23 # Command: undo color.undo.before=white on black color.undo.after=black on white - diff --git a/doc/rc/dark-gray-blue-256.theme b/doc/rc/dark-gray-blue-256.theme index 97bb242b4..8b65e615d 100644 --- a/doc/rc/dark-gray-blue-256.theme +++ b/doc/rc/dark-gray-blue-256.theme @@ -95,4 +95,3 @@ color.sync.rejected=gray23 # Command: undo color.undo.before=rgb013 color.undo.after=rgb035 - diff --git a/doc/rc/dark-red-256.theme b/doc/rc/dark-red-256.theme index 414e1077f..8c655d2ae 100644 --- a/doc/rc/dark-red-256.theme +++ b/doc/rc/dark-red-256.theme @@ -95,4 +95,3 @@ color.sync.rejected=rgb200 # Command: undo color.undo.after=rgb511 color.undo.before=rgb200 - diff --git a/doc/rc/dark-violets-256.theme b/doc/rc/dark-violets-256.theme index 0cd56e6df..395777cc6 100644 --- a/doc/rc/dark-violets-256.theme +++ b/doc/rc/dark-violets-256.theme @@ -95,4 +95,3 @@ color.sync.rejected=rgb103 # Command: undo color.undo.before=rgb103 color.undo.after=rgb305 - diff --git a/doc/rc/dark-yellow-green.theme b/doc/rc/dark-yellow-green.theme index da7429a46..4a9dcbc00 100644 --- a/doc/rc/dark-yellow-green.theme +++ b/doc/rc/dark-yellow-green.theme @@ -95,4 +95,3 @@ color.sync.rejected=rgb110 # Command: undo color.undo.before=rgb021 color.undo.after=rgb042 - diff --git a/doc/rc/light-16.theme b/doc/rc/light-16.theme index 885c7367d..c310dd301 100644 --- a/doc/rc/light-16.theme +++ b/doc/rc/light-16.theme @@ -95,4 +95,3 @@ color.sync.rejected=red # Command: undo color.undo.before=yellow color.undo.after=green - diff --git a/doc/rc/light-256.theme b/doc/rc/light-256.theme index e8eb896f3..8044efbb0 100644 --- a/doc/rc/light-256.theme +++ b/doc/rc/light-256.theme @@ -65,7 +65,7 @@ color.due.today=on rgb353 color.overdue=on rgb544 # Report: burndown -color.burndown.pending=on rgb411 +color.burndown.pending=on rgb411 color.burndown.started=on rgb550 color.burndown.done=on rgb151 @@ -95,4 +95,3 @@ color.sync.rejected=red # Command: undo color.undo.before=yellow color.undo.after=green - diff --git a/doc/rc/no-color.theme b/doc/rc/no-color.theme index af085c04d..ae387e5ba 100644 --- a/doc/rc/no-color.theme +++ b/doc/rc/no-color.theme @@ -98,4 +98,3 @@ color.sync.rejected= # Command: undo color.undo.after= color.undo.before= - diff --git a/doc/rc/refresh b/doc/rc/refresh index 361dc85fa..78ce19f45 100755 --- a/doc/rc/refresh +++ b/doc/rc/refresh @@ -6,4 +6,3 @@ do echo $locale ../../scripts/add-ons/update-holidays.pl --locale $locale --file holidays.${locale}.rc done - diff --git a/doc/rc/solarized-dark-256.theme b/doc/rc/solarized-dark-256.theme index b8551d971..cd61504c3 100644 --- a/doc/rc/solarized-dark-256.theme +++ b/doc/rc/solarized-dark-256.theme @@ -112,4 +112,3 @@ color.sync.rejected=color13 # Command: undo color.undo.after=color2 color.undo.before=color1 - diff --git a/doc/rc/solarized-light-256.theme b/doc/rc/solarized-light-256.theme index 55c524e01..9cdd56453 100644 --- a/doc/rc/solarized-light-256.theme +++ b/doc/rc/solarized-light-256.theme @@ -112,4 +112,3 @@ color.sync.rejected=color13 # Command: undo color.undo.after=color2 color.undo.before=color1 - diff --git a/misc/themes/README b/misc/themes/README index 37a373e58..6417a68c3 100644 --- a/misc/themes/README +++ b/misc/themes/README @@ -25,4 +25,3 @@ Using a solarized light terminal, run the following: Note that for the solarized themes, the terminal color palette needs to be set to specific colors. - diff --git a/misc/themes/setup b/misc/themes/setup index 869ba67dc..e2882e8b1 100755 --- a/misc/themes/setup +++ b/misc/themes/setup @@ -30,4 +30,3 @@ task rc:x add Deleted_1 task rc:x 14 mod depends:13 task rc:x 15 delete - diff --git a/performance/CMakeLists.txt b/performance/CMakeLists.txt index 293c5b942..a8fff9921 100644 --- a/performance/CMakeLists.txt +++ b/performance/CMakeLists.txt @@ -7,5 +7,3 @@ configure_file(run_perf run_perf) add_custom_target (performance ${CMAKE_BINARY_DIR}/performance/run_perf DEPENDS task_executable WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/performance) - - diff --git a/performance/compare_runs.py b/performance/compare_runs.py index 834332a18..c4b86ea7c 100755 --- a/performance/compare_runs.py +++ b/performance/compare_runs.py @@ -29,10 +29,13 @@ def parse_perf(input): tests[command] = [] # Parse concatenated run_perf output - for i in re.findall("^ - task %s\\.\\.\\.\n" - "Perf task ([^ ]+) ([^ ]+) ([^ ]+) (.+)$" - % command, input, re.MULTILINE): - info = i[0:3] + ({k:v for k, v in (i.split(":") for i in i[-1].split())},) + for i in re.findall( + "^ - task %s\\.\\.\\.\n" + "Perf task ([^ ]+) ([^ ]+) ([^ ]+) (.+)$" % command, + input, + re.MULTILINE, + ): + info = i[0:3] + ({k: v for k, v in (i.split(":") for i in i[-1].split())},) pt = TaskPerf(*info) tests[command].append(pt) return tests @@ -61,8 +64,14 @@ with open(sys.argv[2], "r") as fh: tests_cur = parse_perf(fh.read()) best_cur = get_best(tests_cur) -print("Previous: %s (%s)" % (tests_prev[COMMANDS[0]][0].version, tests_prev[COMMANDS[0]][0].commit)) -print("Current: %s (%s)" % (tests_cur[COMMANDS[0]][0].version, tests_cur[COMMANDS[0]][0].commit)) +print( + "Previous: %s (%s)" + % (tests_prev[COMMANDS[0]][0].version, tests_prev[COMMANDS[0]][0].commit) +) +print( + "Current: %s (%s)" + % (tests_cur[COMMANDS[0]][0].version, tests_cur[COMMANDS[0]][0].commit) +) for test in COMMANDS: print("# %s:" % test) @@ -76,7 +85,9 @@ for test in COMMANDS: else: percentage = "0%" - pad = max(map(len, (k, best_prev[test][k], best_cur[test][k], diff, percentage))) + pad = max( + map(len, (k, best_prev[test][k], best_cur[test][k], diff, percentage)) + ) out[0] += " %s" % k.rjust(pad) out[1] += " %s" % best_prev[test][k].rjust(pad) out[2] += " %s" % best_cur[test][k].rjust(pad) diff --git a/performance/run_perf b/performance/run_perf index 8d925efb9..829fe1723 100755 --- a/performance/run_perf +++ b/performance/run_perf @@ -50,4 +50,3 @@ $TASK rc.debug:1 rc:perf.rc import ${CMAKE_SOURCE_DIR}/performance/export.json 2 echo 'End' exit 0 - diff --git a/performance/sample-text.txt b/performance/sample-text.txt index 5b6e09ba9..acc17fb25 100644 --- a/performance/sample-text.txt +++ b/performance/sample-text.txt @@ -3963,7 +3963,7 @@ GLOUCESTER Give me the letter, sir. EDMUND I shall offend, either to detain or give it. The contents, as in part I understand them, are to blame. GLOUCESTER Lets see, lets see. EDMUND I hope, for my brothers justification, he wrote this but as an essay or taste of my virtue. -GLOUCESTER This policy and reverence of age makes the world bitter to the best of our times, keeps our fortunes from us till our oldness cannot relish them. I begin to find an idle and fond bondage in the oppression of aged tyranny, who sways, not as it hath power, but as it is suffered. Come to me, that of this I may speak more. If our father would sleep till I waked him, you should enjoy half his revenue for ever, and live the beloved of your brother, +GLOUCESTER This policy and reverence of age makes the world bitter to the best of our times, keeps our fortunes from us till our oldness cannot relish them. I begin to find an idle and fond bondage in the oppression of aged tyranny, who sways, not as it hath power, but as it is suffered. Come to me, that of this I may speak more. If our father would sleep till I waked him, you should enjoy half his revenue for ever, and live the beloved of your brother, EDMUND It was not brought me, my lord, theres the cunning of it, I found it thrown in at the casement of my closet. GLOUCESTER You know the character to be your brothers? EDMUND If the matter were good, my lord, I durst swear it were his, but, in respect of that, I would fain think it were not. diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index c0f40fe3e..3d06cedaf 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -8,4 +8,3 @@ install (DIRECTORY add-ons FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - diff --git a/scripts/add-ons/update-holidays.pl b/scripts/add-ons/update-holidays.pl index 7849cbe69..e3360040e 100755 --- a/scripts/add-ons/update-holidays.pl +++ b/scripts/add-ons/update-holidays.pl @@ -221,4 +221,3 @@ if (open my $fh, '>:utf8', $file) exit 0; ################################################################################ - diff --git a/scripts/hooks/README b/scripts/hooks/README index ecfea194e..ecb36e579 100644 --- a/scripts/hooks/README +++ b/scripts/hooks/README @@ -24,4 +24,3 @@ Expected Permissions Interface Each hook script has a unique interface. This is documented in the example scripts here. - diff --git a/scripts/hooks/on-exit.shadow-file b/scripts/hooks/on-exit.shadow-file index 3434c83f5..bf8d94d5d 100755 --- a/scripts/hooks/on-exit.shadow-file +++ b/scripts/hooks/on-exit.shadow-file @@ -32,4 +32,3 @@ fi echo Shadow file $SHADOW_FILE updated. exit 0 - diff --git a/scripts/hooks/on-launch b/scripts/hooks/on-launch index 6f8739374..3a1bcc9fb 100755 --- a/scripts/hooks/on-launch +++ b/scripts/hooks/on-launch @@ -14,4 +14,3 @@ echo 'on-launch' # - 0: JSON ignored, non-JSON is feedback. # - non-0: JSON ignored, non-JSON is error. exit 0 - diff --git a/scripts/vim/ftdetect/task.vim b/scripts/vim/ftdetect/task.vim index ab3acd9c6..952c825d8 100644 --- a/scripts/vim/ftdetect/task.vim +++ b/scripts/vim/ftdetect/task.vim @@ -1,4 +1,4 @@ -" Vim support file to detect Taskwarrior data and configuration files and +" Vim support file to detect Taskwarrior data and configuration files and " single task edits " " Maintainer: John Florian diff --git a/scripts/zsh/_task b/scripts/zsh/_task index 45fb7fd87..013e2065d 100644 --- a/scripts/zsh/_task +++ b/scripts/zsh/_task @@ -289,4 +289,4 @@ _task_default() { } _arguments -s -S \ - "*::task default:_task_default" \ No newline at end of file + "*::task default:_task_default" diff --git a/src/CLI2.cpp b/src/CLI2.cpp index eca04fabe..f951b3c9e 100644 --- a/src/CLI2.cpp +++ b/src/CLI2.cpp @@ -28,18 +28,18 @@ // cmake.h include header must come first #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include #include +#include +#include // Overridden by rc.abbreviation.minimum. int CLI2::minimumMatchLength = 3; @@ -48,298 +48,253 @@ int CLI2::minimumMatchLength = 3; static int safetyValveDefault = 10; //////////////////////////////////////////////////////////////////////////////// -A2::A2 (const std::string& raw, Lexer::Type lextype) -{ +A2::A2(const std::string& raw, Lexer::Type lextype) { _lextype = lextype; - attribute ("raw", raw); + attribute("raw", raw); } //////////////////////////////////////////////////////////////////////////////// -A2::A2 (const A2& other) = default; +A2::A2(const A2& other) = default; //////////////////////////////////////////////////////////////////////////////// -A2& A2::operator= (const A2& other) = default; +A2& A2::operator=(const A2& other) = default; //////////////////////////////////////////////////////////////////////////////// -bool A2::hasTag (const std::string& tag) const -{ - return std::find (_tags.begin (), _tags.end (), tag) != _tags.end (); +bool A2::hasTag(const std::string& tag) const { + return std::find(_tags.begin(), _tags.end(), tag) != _tags.end(); } //////////////////////////////////////////////////////////////////////////////// -void A2::tag (const std::string& tag) -{ - if (! hasTag (tag)) - _tags.push_back (tag); +void A2::tag(const std::string& tag) { + if (!hasTag(tag)) _tags.push_back(tag); } //////////////////////////////////////////////////////////////////////////////// -void A2::unTag (const std::string& tag) -{ - for (auto i = _tags.begin (); i != _tags.end (); ++i) - if (*i == tag) - { - _tags.erase (i); +void A2::unTag(const std::string& tag) { + for (auto i = _tags.begin(); i != _tags.end(); ++i) + if (*i == tag) { + _tags.erase(i); break; } } //////////////////////////////////////////////////////////////////////////////// // Accessor for attributes. -void A2::attribute (const std::string& name, const std::string& value) -{ +void A2::attribute(const std::string& name, const std::string& value) { _attributes[name] = value; - if (name == "raw") - decompose (); + if (name == "raw") decompose(); } //////////////////////////////////////////////////////////////////////////////// // Accessor for attributes. -const std::string A2::attribute (const std::string& name) const -{ +const std::string A2::attribute(const std::string& name) const { // Prevent autovivification. - auto i = _attributes.find (name); - if (i != _attributes.end ()) - return i->second; + auto i = _attributes.find(name); + if (i != _attributes.end()) return i->second; return ""; } //////////////////////////////////////////////////////////////////////////////// -const std::string A2::getToken () const -{ - auto i = _attributes.find ("canonical"); - if (i == _attributes.end ()) - i = _attributes.find ("raw"); +const std::string A2::getToken() const { + auto i = _attributes.find("canonical"); + if (i == _attributes.end()) i = _attributes.find("raw"); return i->second; } //////////////////////////////////////////////////////////////////////////////// -void A2::decompose () -{ - if (_lextype == Lexer::Type::tag) - { +void A2::decompose() { + if (_lextype == Lexer::Type::tag) { std::string raw = _attributes["raw"]; - attribute ("name", raw.substr (1)); - attribute ("sign", raw.substr (0, 1)); + attribute("name", raw.substr(1)); + attribute("sign", raw.substr(0, 1)); } - else if (_lextype == Lexer::Type::substitution) - { - //if (Directory (raw).exists ()) - // return; + else if (_lextype == Lexer::Type::substitution) { + // if (Directory (raw).exists ()) + // return; std::string from; std::string to; std::string flags; - if (Lexer::decomposeSubstitution (_attributes["raw"], from, to, flags)) - { - attribute ("from", from); - attribute ("to", to); - attribute ("flags", flags); + if (Lexer::decomposeSubstitution(_attributes["raw"], from, to, flags)) { + attribute("from", from); + attribute("to", to); + attribute("flags", flags); } } - else if (_lextype == Lexer::Type::pair) - { + else if (_lextype == Lexer::Type::pair) { std::string name; std::string mod; std::string sep; std::string value; - if (Lexer::decomposePair (_attributes["raw"], name, mod, sep, value)) - { - attribute ("name", name); - attribute ("modifier", mod); - attribute ("separator", sep); - attribute ("value", value); + if (Lexer::decomposePair(_attributes["raw"], name, mod, sep, value)) { + attribute("name", name); + attribute("modifier", mod); + attribute("separator", sep); + attribute("value", value); - if (name == "rc") - { + if (name == "rc") { if (mod != "") - tag ("CONFIG"); + tag("CONFIG"); else - tag ("RC"); + tag("RC"); } } } - else if (_lextype == Lexer::Type::pattern) - { - //if (Directory (raw).exists ()) - // return; + else if (_lextype == Lexer::Type::pattern) { + // if (Directory (raw).exists ()) + // return; std::string pattern; std::string flags; - if (Lexer::decomposePattern (_attributes["raw"], pattern, flags)) - { - attribute ("pattern", pattern); - attribute ("flags", flags); + if (Lexer::decomposePattern(_attributes["raw"], pattern, flags)) { + attribute("pattern", pattern); + attribute("flags", flags); } } } //////////////////////////////////////////////////////////////////////////////// -const std::string A2::dump () const -{ - auto output = Lexer::typeToString (_lextype); +const std::string A2::dump() const { + auto output = Lexer::typeToString(_lextype); // Dump attributes. std::string atts; - for (const auto& a : _attributes) - atts += a.first + "='\033[33m" + a.second + "\033[0m' "; + for (const auto& a : _attributes) atts += a.first + "='\033[33m" + a.second + "\033[0m' "; // Dump tags. std::string tags; - for (const auto& tag : _tags) - { - if (tag == "BINARY") tags += "\033[1;37;44m" + tag + "\033[0m "; - else if (tag == "CMD") tags += "\033[1;37;46m" + tag + "\033[0m "; - else if (tag == "FILTER") tags += "\033[1;37;42m" + tag + "\033[0m "; - else if (tag == "MODIFICATION") tags += "\033[1;37;43m" + tag + "\033[0m "; - else if (tag == "MISCELLANEOUS") tags += "\033[1;37;45m" + tag + "\033[0m "; - else if (tag == "RC") tags += "\033[1;37;41m" + tag + "\033[0m "; - else if (tag == "CONFIG") tags += "\033[1;37;101m" + tag + "\033[0m "; - else if (tag == "?") tags += "\033[38;5;255;48;5;232m" + tag + "\033[0m "; - else tags += "\033[32m" + tag + "\033[0m "; + for (const auto& tag : _tags) { + if (tag == "BINARY") + tags += "\033[1;37;44m" + tag + "\033[0m "; + else if (tag == "CMD") + tags += "\033[1;37;46m" + tag + "\033[0m "; + else if (tag == "FILTER") + tags += "\033[1;37;42m" + tag + "\033[0m "; + else if (tag == "MODIFICATION") + tags += "\033[1;37;43m" + tag + "\033[0m "; + else if (tag == "MISCELLANEOUS") + tags += "\033[1;37;45m" + tag + "\033[0m "; + else if (tag == "RC") + tags += "\033[1;37;41m" + tag + "\033[0m "; + else if (tag == "CONFIG") + tags += "\033[1;37;101m" + tag + "\033[0m "; + else if (tag == "?") + tags += "\033[38;5;255;48;5;232m" + tag + "\033[0m "; + else + tags += "\033[32m" + tag + "\033[0m "; } return output + ' ' + atts + tags; } //////////////////////////////////////////////////////////////////////////////// -static -const char* getValue (int argc, const char** argv, std::string arg) -{ - const auto is_arg = [&] (std::string s) - { - return s.size () > arg.size () + 1 - && (s[arg.size ()] == ':' || s[arg.size ()] == '=') - && s.compare (0, arg.size (), arg) == 0; +static const char* getValue(int argc, const char** argv, std::string arg) { + const auto is_arg = [&](std::string s) { + return s.size() > arg.size() + 1 && (s[arg.size()] == ':' || s[arg.size()] == '=') && + s.compare(0, arg.size(), arg) == 0; }; // find last argument before -- - auto last = std::make_reverse_iterator (argv); - auto first = std::make_reverse_iterator ( - std::find (argv, argv + argc, std::string ("--"))); - auto it = std::find_if (first, last, is_arg); - if (it == last) - return nullptr; + auto last = std::make_reverse_iterator(argv); + auto first = std::make_reverse_iterator(std::find(argv, argv + argc, std::string("--"))); + auto it = std::find_if(first, last, is_arg); + if (it == last) return nullptr; // return the string after : or = - return *it + arg.size () + 1; + return *it + arg.size() + 1; } //////////////////////////////////////////////////////////////////////////////// // Static method. -bool CLI2::getOverride (int argc, const char** argv, File& rc) -{ - const char* value = getValue (argc, argv, "rc"); - if (value == nullptr) - return false; - rc = File (value); +bool CLI2::getOverride(int argc, const char** argv, File& rc) { + const char* value = getValue(argc, argv, "rc"); + if (value == nullptr) return false; + rc = File(value); return true; } //////////////////////////////////////////////////////////////////////////////// // Look for CONFIG data.location and initialize a Path object. // Static method. -bool CLI2::getDataLocation (int argc, const char** argv, Path& data) -{ - const char* value = getValue (argc, argv, "rc.data.location"); - if (value == nullptr) - { - std::string location = Context::getContext ().config.get ("data.location"); - if (location != "") - data = location; +bool CLI2::getDataLocation(int argc, const char** argv, Path& data) { + const char* value = getValue(argc, argv, "rc.data.location"); + if (value == nullptr) { + std::string location = Context::getContext().config.get("data.location"); + if (location != "") data = location; return false; } - data = Directory (value); + data = Directory(value); return true; } //////////////////////////////////////////////////////////////////////////////// // Static method. -void CLI2::applyOverrides (int argc, const char** argv) -{ - auto& context = Context::getContext (); - auto last = std::find (argv, argv + argc, std::string ("--")); - auto is_override = [] (const std::string& s) - { - return s.compare (0, 3, "rc.") == 0; - }; - auto get_sep = [&] (const std::string& s) - { - if (is_override (s)) - return s.find_first_of (":=", 3); +void CLI2::applyOverrides(int argc, const char** argv) { + auto& context = Context::getContext(); + auto last = std::find(argv, argv + argc, std::string("--")); + auto is_override = [](const std::string& s) { return s.compare(0, 3, "rc.") == 0; }; + auto get_sep = [&](const std::string& s) { + if (is_override(s)) return s.find_first_of(":=", 3); return std::string::npos; }; - auto override_settings = [&] (std::string raw) - { - auto sep = get_sep (raw); - if (sep == std::string::npos) - return; - std::string name = raw.substr (3, sep - 3); - std::string value = raw.substr (sep + 1); - context.config.set (name, value); + auto override_settings = [&](std::string raw) { + auto sep = get_sep(raw); + if (sep == std::string::npos) return; + std::string name = raw.substr(3, sep - 3); + std::string value = raw.substr(sep + 1); + context.config.set(name, value); }; - auto display_overrides = [&] (std::string raw) - { - if (is_override (raw)) - context.footnote (format ("Configuration override {1}", raw)); + auto display_overrides = [&](std::string raw) { + if (is_override(raw)) context.footnote(format("Configuration override {1}", raw)); }; - std::for_each (argv, last, override_settings); - if (context.verbose ("override")) - std::for_each (argv, last, display_overrides); + std::for_each(argv, last, override_settings); + if (context.verbose("override")) std::for_each(argv, last, display_overrides); } //////////////////////////////////////////////////////////////////////////////// -void CLI2::alias (const std::string& name, const std::string& value) -{ - _aliases[name] = value; -} +void CLI2::alias(const std::string& name, const std::string& value) { _aliases[name] = value; } //////////////////////////////////////////////////////////////////////////////// -void CLI2::entity (const std::string& category, const std::string& name) -{ +void CLI2::entity(const std::string& category, const std::string& name) { // Walk the list of entities for category. - auto c = _entities.equal_range (category); + auto c = _entities.equal_range(category); for (auto e = c.first; e != c.second; ++e) - if (e->second == name) - return; + if (e->second == name) return; // The category/name pair was not found, therefore add it. - _entities.emplace (category, name); + _entities.emplace(category, name); } //////////////////////////////////////////////////////////////////////////////// // Capture a single argument. -void CLI2::add (const std::string& argument) -{ - A2 arg (Lexer::trim (argument), Lexer::Type::word); - arg.tag ("ORIGINAL"); - _original_args.push_back (arg); +void CLI2::add(const std::string& argument) { + A2 arg(Lexer::trim(argument), Lexer::Type::word); + arg.tag("ORIGINAL"); + _original_args.push_back(arg); // Adding a new argument invalidates prior analysis. - _args.clear (); + _args.clear(); } //////////////////////////////////////////////////////////////////////////////// // Capture a set of arguments, inserted immediately after arguments // after the binary.. -void CLI2::add (const std::vector & arguments, int offset /* = 0 */) -{ - std::vector replacement {_original_args.begin(), _original_args.begin() + offset + 1}; +void CLI2::add(const std::vector& arguments, int offset /* = 0 */) { + std::vector replacement{_original_args.begin(), _original_args.begin() + offset + 1}; - for (const auto& arg : arguments) - replacement.emplace_back (arg, Lexer::Type::word); + for (const auto& arg : arguments) replacement.emplace_back(arg, Lexer::Type::word); - for (unsigned int i = 1 + offset; i < _original_args.size (); ++i) - replacement.push_back (_original_args[i]); + for (unsigned int i = 1 + offset; i < _original_args.size(); ++i) + replacement.push_back(_original_args[i]); _original_args = replacement; // Adding a new argument invalidates prior analysis. - _args.clear (); + _args.clear(); } //////////////////////////////////////////////////////////////////////////////// @@ -349,31 +304,25 @@ void CLI2::add (const std::vector & arguments, int offset /* = 0 */ // The binary name is 'task', but if the binary is reported as 'cal' or // 'calendar' then it was invoked via symbolic link, in which case capture the // first argument as 'calendar'. -void CLI2::handleArg0 () -{ +void CLI2::handleArg0() { // Capture arg0 separately, because it is the command that was run, and could // need special handling. - auto raw = _original_args[0].attribute ("raw"); - A2 a (raw, Lexer::Type::word); - a.tag ("BINARY"); + auto raw = _original_args[0].attribute("raw"); + A2 a(raw, Lexer::Type::word); + a.tag("BINARY"); std::string basename = "task"; - auto slash = raw.rfind ('/'); - if (slash != std::string::npos) - basename = raw.substr (slash + 1); + auto slash = raw.rfind('/'); + if (slash != std::string::npos) basename = raw.substr(slash + 1); - a.attribute ("basename", basename); - if (basename == "cal" || - basename == "calendar") - { - _args.push_back (a); + a.attribute("basename", basename); + if (basename == "cal" || basename == "calendar") { + _args.push_back(a); - A2 cal ("calendar", Lexer::Type::word); - _args.push_back (cal); - } - else - { - _args.push_back (a); + A2 cal("calendar", Lexer::Type::word); + _args.push_back(cal); + } else { + _args.push_back(a); } } @@ -383,61 +332,53 @@ void CLI2::handleArg0 () // // As a side effect, tags all arguments after a terminator ('--') with // TERMINATED. -void CLI2::lexArguments () -{ +void CLI2::lexArguments() { // Note: Starts iterating at index 1, because ::handleArg0 has already // processed it. bool terminated = false; - for (unsigned int i = 1; i < _original_args.size (); ++i) - { - bool quoted = Lexer::wasQuoted (_original_args[i].attribute ("raw")); + for (unsigned int i = 1; i < _original_args.size(); ++i) { + bool quoted = Lexer::wasQuoted(_original_args[i].attribute("raw")); // Process single-token arguments. std::string lexeme; Lexer::Type type; - Lexer lex (_original_args[i].attribute ("raw")); - if (lex.token (lexeme, type) && - (lex.isEOS () || // Token goes to EOS - (quoted && type == Lexer::Type::pair))) // Quoted pairs automatically go to EOS + Lexer lex(_original_args[i].attribute("raw")); + if (lex.token(lexeme, type) && + (lex.isEOS() || // Token goes to EOS + (quoted && type == Lexer::Type::pair))) // Quoted pairs automatically go to EOS { - if (! terminated && type == Lexer::Type::separator) + if (!terminated && type == Lexer::Type::separator) terminated = true; else if (terminated) type = Lexer::Type::word; - A2 a (_original_args[i].attribute ("raw"), type); - if (terminated) - a.tag ("TERMINATED"); - if (quoted) - a.tag ("QUOTED"); + A2 a(_original_args[i].attribute("raw"), type); + if (terminated) a.tag("TERMINATED"); + if (quoted) a.tag("QUOTED"); - if (_original_args[i].hasTag ("ORIGINAL")) - a.tag ("ORIGINAL"); + if (_original_args[i].hasTag("ORIGINAL")) a.tag("ORIGINAL"); - _args.push_back (a); + _args.push_back(a); } // Process multiple-token arguments. - else - { + else { const std::string quote = "'"; // Escape unescaped single quotes std::string escaped = ""; // For performance reasons. The escaped string is as long as the original. - escaped.reserve (_original_args[i].attribute ("raw").size ()); + escaped.reserve(_original_args[i].attribute("raw").size()); std::string::size_type cursor = 0; bool nextEscaped = false; - while (int num = utf8_next_char (_original_args[i].attribute ("raw"), cursor)) - { - std::string character = utf8_character (num); + while (int num = utf8_next_char(_original_args[i].attribute("raw"), cursor)) { + std::string character = utf8_character(num); if (!nextEscaped && (character == "\\")) nextEscaped = true; else { - if (character == quote && !nextEscaped) - escaped += "\\"; + if (character == quote && !nextEscaped) escaped += "\\"; nextEscaped = false; } escaped += character; @@ -445,166 +386,141 @@ void CLI2::lexArguments () cursor = 0; std::string word; - if (Lexer::readWord (quote + escaped + quote, quote, cursor, word)) - { - Lexer::dequote (word); - A2 unknown (word, Lexer::Type::word); - if (lex.wasQuoted (_original_args[i].attribute ("raw"))) - unknown.tag ("QUOTED"); + if (Lexer::readWord(quote + escaped + quote, quote, cursor, word)) { + Lexer::dequote(word); + A2 unknown(word, Lexer::Type::word); + if (lex.wasQuoted(_original_args[i].attribute("raw"))) unknown.tag("QUOTED"); - if (_original_args[i].hasTag ("ORIGINAL")) - unknown.tag ("ORIGINAL"); + if (_original_args[i].hasTag("ORIGINAL")) unknown.tag("ORIGINAL"); - _args.push_back (unknown); + _args.push_back(unknown); } // This branch may have no use-case. - else - { - A2 unknown (_original_args[i].attribute ("raw"), Lexer::Type::word); - unknown.tag ("UNKNOWN"); + else { + A2 unknown(_original_args[i].attribute("raw"), Lexer::Type::word); + unknown.tag("UNKNOWN"); - if (lex.wasQuoted (_original_args[i].attribute ("raw"))) - unknown.tag ("QUOTED"); + if (lex.wasQuoted(_original_args[i].attribute("raw"))) unknown.tag("QUOTED"); - if (_original_args[i].hasTag ("ORIGINAL")) - unknown.tag ("ORIGINAL"); + if (_original_args[i].hasTag("ORIGINAL")) unknown.tag("ORIGINAL"); - _args.push_back (unknown); + _args.push_back(unknown); } } } - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::analyze lexArguments")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::analyze lexArguments")); } //////////////////////////////////////////////////////////////////////////////// // [1] Scan all args for the 'add' and 'log' commands, and demote any // Lexer::Type::Tag args with sign '-' to Lexer::Type::word. // [2] Convert any pseudo args name:value into config settings, and erase. -void CLI2::demotion () -{ +void CLI2::demotion() { bool changes = false; - std::vector replacement; + std::vector replacement; std::string canonical; - for (auto& a : _args) - { - if (a._lextype == Lexer::Type::tag && - a.attribute ("sign") == "-") - { - std::string command = getCommand (); - if (command == "add" || - command == "log") - { + for (auto& a : _args) { + if (a._lextype == Lexer::Type::tag && a.attribute("sign") == "-") { + std::string command = getCommand(); + if (command == "add" || command == "log") { a._lextype = Lexer::Type::word; changes = true; } } else if (a._lextype == Lexer::Type::pair && - canonicalize (canonical, "pseudo", a.attribute ("name"))) - { - Context::getContext ().config.set (canonical, a.attribute ("value")); + canonicalize(canonical, "pseudo", a.attribute("name"))) { + Context::getContext().config.set(canonical, a.attribute("value")); changes = true; // Equivalent to erasing 'a'. continue; } - replacement.push_back (a); + replacement.push_back(a); } - if (changes && - Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::analyze demotion")); + if (changes && Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::analyze demotion")); } //////////////////////////////////////////////////////////////////////////////// // Intended to be called after ::add() to perform the final analysis. -void CLI2::analyze () -{ - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::analyze")); +void CLI2::analyze() { + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::analyze")); // Process _original_args. - _args.clear (); - handleArg0 (); - lexArguments (); + _args.clear(); + handleArg0(); + lexArguments(); // Process _args. - aliasExpansion (); - if (! findCommand ()) - { - defaultCommand (); - if (! findCommand ()) - throw std::string ("You must specify a command or a task to modify."); + aliasExpansion(); + if (!findCommand()) { + defaultCommand(); + if (!findCommand()) throw std::string("You must specify a command or a task to modify."); } - demotion (); - canonicalizeNames (); + demotion(); + canonicalizeNames(); // Determine arg types: FILTER, MODIFICATION, MISCELLANEOUS. - categorizeArgs (); - parenthesizeOriginalFilter (); + categorizeArgs(); + parenthesizeOriginalFilter(); // Cache frequently looked up items - _command = getCommand (); + _command = getCommand(); } //////////////////////////////////////////////////////////////////////////////// // Process raw filter string. // Insert filter arguments (wrapped in parentheses) immediatelly after the binary. -void CLI2::addFilter (const std::string& arg) -{ - if (arg.length ()) - { - std::vector filter; - filter.push_back ("("); +void CLI2::addFilter(const std::string& arg) { + if (arg.length()) { + std::vector filter; + filter.push_back("("); std::string lexeme; Lexer::Type type; - Lexer lex (arg); + Lexer lex(arg); - while (lex.token (lexeme, type)) - filter.push_back (lexeme); + while (lex.token(lexeme, type)) filter.push_back(lexeme); - filter.push_back (")"); - add (filter); - analyze (); + filter.push_back(")"); + add(filter); + analyze(); } } //////////////////////////////////////////////////////////////////////////////// // Process raw modification string. // Insert modification arguments immediatelly after the command (i.e. 'add') -void CLI2::addModifications (const std::string& arg) -{ - if (arg.length ()) - { - std::vector mods; +void CLI2::addModifications(const std::string& arg) { + if (arg.length()) { + std::vector mods; std::string lexeme; Lexer::Type type; - Lexer lex (arg); + Lexer lex(arg); - while (lex.token (lexeme, type)) - mods.push_back (lexeme); + while (lex.token(lexeme, type)) mods.push_back(lexeme); // Determine at which argument index does the task modification command // reside unsigned int cmdIndex = 0; - for (; cmdIndex < _args.size(); ++cmdIndex) - { + for (; cmdIndex < _args.size(); ++cmdIndex) { // Command found, stop iterating. - if (_args[cmdIndex].hasTag ("CMD")) - break; + if (_args[cmdIndex].hasTag("CMD")) break; } // Insert modifications after the command. - add (mods, cmdIndex); - analyze (); + add(mods, cmdIndex); + analyze(); } } @@ -612,35 +528,30 @@ void CLI2::addModifications (const std::string& arg) // There are situations where a context filter is applied. This method // determines whether one applies, and if so, applies it. Disqualifiers include: // - filter contains ID or UUID -void CLI2::addContext (bool readable, bool writeable) -{ +void CLI2::addContext(bool readable, bool writeable) { // Recursion block. - if (_context_added) - return; + if (_context_added) return; // Detect if any context is set, and bail out if not std::string contextString; if (readable) // Empty string is treated as "currently selected context" - contextString = Context::getContext ().getTaskContext("read", ""); + contextString = Context::getContext().getTaskContext("read", ""); else if (writeable) - contextString = Context::getContext ().getTaskContext("write", ""); + contextString = Context::getContext().getTaskContext("write", ""); else return; // If context is empty, bail out too - if (contextString.empty ()) - return; + if (contextString.empty()) return; // For readable contexts: Detect if UUID or ID is set, and bail out if (readable) - for (auto& a : _args) - { - if (a._lextype == Lexer::Type::uuid || - a._lextype == Lexer::Type::number || - a._lextype == Lexer::Type::set) - { - Context::getContext ().debug (format ("UUID/ID argument found '{1}', not applying context.", a.attribute ("raw"))); + for (auto& a : _args) { + if (a._lextype == Lexer::Type::uuid || a._lextype == Lexer::Type::number || + a._lextype == Lexer::Type::set) { + Context::getContext().debug( + format("UUID/ID argument found '{1}', not applying context.", a.attribute("raw"))); return; } } @@ -649,75 +560,63 @@ void CLI2::addContext (bool readable, bool writeable) // block now, since addFilter calls analyze(), which calls addContext(). _context_added = true; if (readable) - addFilter (contextString); + addFilter(contextString); else if (writeable) - addModifications (contextString); + addModifications(contextString); // Inform the user about the application of context - if (Context::getContext ().verbose ("context")) - Context::getContext ().footnote (format ( - "Context '{1}' set. Use 'task context none' to remove.", - Context::getContext ().config.get ("context") - )); + if (Context::getContext().verbose("context")) + Context::getContext().footnote(format("Context '{1}' set. Use 'task context none' to remove.", + Context::getContext().config.get("context"))); } //////////////////////////////////////////////////////////////////////////////// // Parse the command line, identifiying filter components, expanding syntactic // sugar as necessary. -void CLI2::prepareFilter () -{ +void CLI2::prepareFilter() { // Clear and re-populate. - _id_ranges.clear (); - _uuid_list.clear (); + _id_ranges.clear(); + _uuid_list.clear(); _context_added = false; // Remove all the syntactic sugar for FILTERs. - lexFilterArgs (); - findIDs (); - findUUIDs (); - insertIDExpr (); - desugarFilterPlainArgs (); - findStrayModifications (); - desugarFilterTags (); - desugarFilterAttributes (); - desugarFilterPatterns (); - insertJunctions (); // Deliberately after all desugar calls. + lexFilterArgs(); + findIDs(); + findUUIDs(); + insertIDExpr(); + desugarFilterPlainArgs(); + findStrayModifications(); + desugarFilterTags(); + desugarFilterAttributes(); + desugarFilterPatterns(); + insertJunctions(); // Deliberately after all desugar calls. - if (Context::getContext ().verbose ("filter")) - { + if (Context::getContext().verbose("filter")) { std::string combined; - for (const auto& a : _args) - { - if (a.hasTag ("FILTER")) - { - if (combined != "") - combined += ' '; + for (const auto& a : _args) { + if (a.hasTag("FILTER")) { + if (combined != "") combined += ' '; - combined += a.attribute ("raw"); + combined += a.attribute("raw"); } } - if (combined.size ()) - Context::getContext ().footnote (std::string ("Filter: ") + combined); + if (combined.size()) Context::getContext().footnote(std::string("Filter: ") + combined); } } //////////////////////////////////////////////////////////////////////////////// // Return all the MISCELLANEOUS args as strings. -const std::vector CLI2::getWords () -{ - std::vector words; +const std::vector CLI2::getWords() { + std::vector words; for (const auto& a : _args) - if (a.hasTag ("MISCELLANEOUS")) - words.push_back (a.attribute ("raw")); + if (a.hasTag("MISCELLANEOUS")) words.push_back(a.attribute("raw")); - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - { - Color colorOrigArgs ("gray10 on gray4"); + if (Context::getContext().config.getInteger("debug.parser") >= 2) { + Color colorOrigArgs("gray10 on gray4"); std::string message = " "; - for (const auto& word : words) - message += colorOrigArgs.colorize (word) + ' '; - Context::getContext ().debug ("CLI2::getWords" + message); + for (const auto& word : words) message += colorOrigArgs.colorize(word) + ' '; + Context::getContext().debug("CLI2::getWords" + message); } return words; @@ -725,54 +624,45 @@ const std::vector CLI2::getWords () //////////////////////////////////////////////////////////////////////////////// // Return all the MISCELLANEOUS args. -const std::vector CLI2::getMiscellaneous () -{ - std::vector misc; +const std::vector CLI2::getMiscellaneous() { + std::vector misc; for (const auto& a : _args) - if (a.hasTag ("MISCELLANEOUS")) - misc.push_back (a); + if (a.hasTag("MISCELLANEOUS")) misc.push_back(a); return misc; } //////////////////////////////////////////////////////////////////////////////// // Search for 'value' in _entities category, return canonicalized value. -bool CLI2::canonicalize ( - std::string& canonicalized, - const std::string& category, - const std::string& value) -{ +bool CLI2::canonicalize(std::string& canonicalized, const std::string& category, + const std::string& value) { // Utilize a cache mapping of (category, value) -> canonicalized value. // This cache does not need to be invalidated, because entities are defined // only once per initialization of the Context object. - int cache_key = 31 * std::hash{} (category) + std::hash{} (value); - auto cache_result = _canonical_cache.find (cache_key); - if (cache_result != _canonical_cache.end()) - { + int cache_key = 31 * std::hash{}(category) + std::hash{}(value); + auto cache_result = _canonical_cache.find(cache_key); + if (cache_result != _canonical_cache.end()) { canonicalized = cache_result->second; return true; } // Extract a list of entities for category. - std::vector options; - auto c = _entities.equal_range (category); - for (auto e = c.first; e != c.second; ++e) - { + std::vector options; + auto c = _entities.equal_range(category); + for (auto e = c.first; e != c.second; ++e) { // Shortcut: if an exact match is found, success. - if (value == e->second) - { + if (value == e->second) { canonicalized = value; _canonical_cache[cache_key] = value; return true; } - options.push_back (e->second); + options.push_back(e->second); } // Match against the options, throw away results. - std::vector matches; - if (autoComplete (value, options, matches, minimumMatchLength) == 1) - { + std::vector matches; + if (autoComplete(value, options, matches, minimumMatchLength) == 1) { canonicalized = matches[0]; _canonical_cache[cache_key] = matches[0]; return true; @@ -782,186 +672,144 @@ bool CLI2::canonicalize ( } //////////////////////////////////////////////////////////////////////////////// -std::string CLI2::getBinary () const -{ - if (_args.size ()) - return _args[0].attribute ("raw"); +std::string CLI2::getBinary() const { + if (_args.size()) return _args[0].attribute("raw"); return ""; } //////////////////////////////////////////////////////////////////////////////// -std::string CLI2::getCommand (bool canonical) const -{ +std::string CLI2::getCommand(bool canonical) const { // Shortcut if analysis has been finalized - if (_command != "") - return _command; + if (_command != "") return _command; for (const auto& a : _args) - if (a.hasTag ("CMD")) - return a.attribute (canonical ? "canonical" : "raw"); + if (a.hasTag("CMD")) return a.attribute(canonical ? "canonical" : "raw"); return ""; } //////////////////////////////////////////////////////////////////////////////// -const std::string CLI2::dump (const std::string& title) const -{ +const std::string CLI2::dump(const std::string& title) const { std::stringstream out; out << "\033[1m" << title << "\033[0m\n" << " _original_args\n "; - Color colorArgs ("gray10 on gray4"); - Color colorFilter ("black on rgb311"); - for (auto i = _original_args.begin (); i != _original_args.end (); ++i) - { - if (i != _original_args.begin ()) - out << ' '; + Color colorArgs("gray10 on gray4"); + Color colorFilter("black on rgb311"); + for (auto i = _original_args.begin(); i != _original_args.end(); ++i) { + if (i != _original_args.begin()) out << ' '; - if (i->hasTag ("ORIGINAL")) - out << colorArgs.colorize (i->attribute ("raw")); + if (i->hasTag("ORIGINAL")) + out << colorArgs.colorize(i->attribute("raw")); else - out << colorFilter.colorize (i->attribute ("raw")); + out << colorFilter.colorize(i->attribute("raw")); } out << '\n'; - if (_args.size ()) - { + if (_args.size()) { out << " _args\n"; - for (const auto& a : _args) - out << " " << a.dump () << '\n'; + for (const auto& a : _args) out << " " << a.dump() << '\n'; } - if (_id_ranges.size ()) - { + if (_id_ranges.size()) { out << " _id_ranges\n "; - for (const auto& range : _id_ranges) - { + for (const auto& range : _id_ranges) { if (range.first != range.second) - out << colorArgs.colorize (range.first + "-" + range.second) << ' '; + out << colorArgs.colorize(range.first + "-" + range.second) << ' '; else - out << colorArgs.colorize (range.first) << ' '; + out << colorArgs.colorize(range.first) << ' '; } out << '\n'; } - if (_uuid_list.size ()) - { + if (_uuid_list.size()) { out << " _uuid_list\n "; - for (const auto& uuid : _uuid_list) - out << colorArgs.colorize (uuid) << ' '; + for (const auto& uuid : _uuid_list) out << colorArgs.colorize(uuid) << ' '; out << '\n'; } - return out.str (); + return out.str(); } //////////////////////////////////////////////////////////////////////////////// // If any aliases are found in un-TERMINATED arguments, replace the alias with // a set of Lexed tokens from the configuration. -void CLI2::aliasExpansion () -{ +void CLI2::aliasExpansion() { bool changes = false; bool action; int counter = 0; - do - { + do { action = false; - std::vector reconstructed; + std::vector reconstructed; std::string raw; - for (const auto& i : _args) - { - raw = i.attribute ("raw"); - if (i.hasTag ("TERMINATED")) - { - reconstructed.push_back (i); - } - else if (_aliases.find (raw) != _aliases.end ()) - { + for (const auto& i : _args) { + raw = i.attribute("raw"); + if (i.hasTag("TERMINATED")) { + reconstructed.push_back(i); + } else if (_aliases.find(raw) != _aliases.end()) { std::string lexeme; Lexer::Type type; - Lexer lex (_aliases[raw]); - while (lex.token (lexeme, type)) - reconstructed.emplace_back (lexeme, type); + Lexer lex(_aliases[raw]); + while (lex.token(lexeme, type)) reconstructed.emplace_back(lexeme, type); action = true; changes = true; - } - else - { - reconstructed.push_back (i); + } else { + reconstructed.push_back(i); } } _args = reconstructed; - std::vector reconstructedOriginals; + std::vector reconstructedOriginals; bool terminated = false; - for (const auto& i : _original_args) - { - if (i.attribute ("raw") == "--") - terminated = true; + for (const auto& i : _original_args) { + if (i.attribute("raw") == "--") terminated = true; - if (terminated) - { - reconstructedOriginals.push_back (i); - } - else if (_aliases.find (i.attribute ("raw")) != _aliases.end ()) - { + if (terminated) { + reconstructedOriginals.push_back(i); + } else if (_aliases.find(i.attribute("raw")) != _aliases.end()) { std::string lexeme; Lexer::Type type; - Lexer lex (_aliases[i.attribute ("raw")]); - while (lex.token (lexeme, type)) - reconstructedOriginals.emplace_back (lexeme, type); + Lexer lex(_aliases[i.attribute("raw")]); + while (lex.token(lexeme, type)) reconstructedOriginals.emplace_back(lexeme, type); action = true; changes = true; - } - else - { - reconstructedOriginals.push_back (i); + } else { + reconstructedOriginals.push_back(i); } } _original_args = reconstructedOriginals; - } - while (action && counter++ < safetyValveDefault); + } while (action && counter++ < safetyValveDefault); if (counter >= safetyValveDefault) - Context::getContext ().debug (format ("Nested alias limit of {1} reached.", safetyValveDefault)); + Context::getContext().debug(format("Nested alias limit of {1} reached.", safetyValveDefault)); - if (changes && - Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::analyze aliasExpansion")); + if (changes && Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::analyze aliasExpansion")); } //////////////////////////////////////////////////////////////////////////////// // Scan all arguments and canonicalize names that need it. -void CLI2::canonicalizeNames () -{ +void CLI2::canonicalizeNames() { bool changes = false; - for (auto& a : _args) - { - if (a._lextype == Lexer::Type::pair) - { - std::string raw = a.attribute ("raw"); - if (raw.substr (0, 3) != "rc:" && - raw.substr (0, 3) != "rc.") - { - std::string name = a.attribute ("name"); + for (auto& a : _args) { + if (a._lextype == Lexer::Type::pair) { + std::string raw = a.attribute("raw"); + if (raw.substr(0, 3) != "rc:" && raw.substr(0, 3) != "rc.") { + std::string name = a.attribute("name"); std::string canonical; - if (canonicalize (canonical, "pseudo", name) || - canonicalize (canonical, "attribute", name)) - { - a.attribute ("canonical", canonical); - } - else - { + if (canonicalize(canonical, "pseudo", name) || canonicalize(canonical, "attribute", name)) { + a.attribute("canonical", canonical); + } else { a._lextype = Lexer::Type::word; } @@ -970,18 +818,16 @@ void CLI2::canonicalizeNames () } } - if (changes && - Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::analyze canonicalizeNames")); + if (changes && Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::analyze canonicalizeNames")); } //////////////////////////////////////////////////////////////////////////////// // Categorize FILTER, MODIFICATION and MISCELLANEOUS args, based on CMD DNA. -void CLI2::categorizeArgs () -{ +void CLI2::categorizeArgs() { // Context is only applied for commands that request it. - std::string command = getCommand (); - Command* cmd = Context::getContext ().commands[command]; + std::string command = getCommand(); + Command* cmd = Context::getContext().commands[command]; // Determine if the command uses Context. CmdCustom and CmdTimesheet need to // be handled separately, as they override the parent Command::use_context @@ -990,35 +836,28 @@ void CLI2::categorizeArgs () // All Command classes overriding uses_context () getter need to be specified // here. bool uses_context; - if (dynamic_cast (cmd)) - uses_context = (dynamic_cast (cmd))->uses_context (); - else if (dynamic_cast (cmd)) - uses_context = (dynamic_cast (cmd))->uses_context (); + if (dynamic_cast(cmd)) + uses_context = (dynamic_cast(cmd))->uses_context(); + else if (dynamic_cast(cmd)) + uses_context = (dynamic_cast(cmd))->uses_context(); else if (cmd) - uses_context = cmd->uses_context (); + uses_context = cmd->uses_context(); // Apply the context, if applicable - if (cmd && uses_context) - addContext (cmd->accepts_filter (), cmd->accepts_modifications ()); + if (cmd && uses_context) addContext(cmd->accepts_filter(), cmd->accepts_modifications()); bool changes = false; bool afterCommand = false; - for (auto& a : _args) - { - if (a._lextype == Lexer::Type::separator) - continue; + for (auto& a : _args) { + if (a._lextype == Lexer::Type::separator) continue; // Record that the command has been found, it affects behavior. - if (a.hasTag ("CMD")) - { + if (a.hasTag("CMD")) { afterCommand = true; } // Skip admin args. - else if (a.hasTag ("BINARY") || - a.hasTag ("RC") || - a.hasTag ("CONFIG")) - { + else if (a.hasTag("BINARY") || a.hasTag("RC") || a.hasTag("CONFIG")) { // NOP. } @@ -1033,83 +872,51 @@ void CLI2::categorizeArgs () // Fi Mo -- task [Fi] [Mo] // Fi Mo Mi Internally inconsistent // - else if (cmd && - ! cmd->accepts_filter () && - ! cmd->accepts_modifications () && - ! cmd->accepts_miscellaneous ()) - { + else if (cmd && !cmd->accepts_filter() && !cmd->accepts_modifications() && + !cmd->accepts_miscellaneous()) { // No commands were expected --> error. - throw format ("The '{1}' command does not allow '{2}'.", command, a.attribute ("raw")); - } - else if (cmd && - ! cmd->accepts_filter () && - ! cmd->accepts_modifications () && - cmd->accepts_miscellaneous ()) - { - a.tag ("MISCELLANEOUS"); + throw format("The '{1}' command does not allow '{2}'.", command, a.attribute("raw")); + } else if (cmd && !cmd->accepts_filter() && !cmd->accepts_modifications() && + cmd->accepts_miscellaneous()) { + a.tag("MISCELLANEOUS"); changes = true; - } - else if (cmd && - ! cmd->accepts_filter () && - cmd->accepts_modifications () && - ! cmd->accepts_miscellaneous ()) - { - a.tag ("MODIFICATION"); + } else if (cmd && !cmd->accepts_filter() && cmd->accepts_modifications() && + !cmd->accepts_miscellaneous()) { + a.tag("MODIFICATION"); changes = true; - } - else if (cmd && - ! cmd->accepts_filter () && - cmd->accepts_modifications () && - cmd->accepts_miscellaneous ()) - { + } else if (cmd && !cmd->accepts_filter() && cmd->accepts_modifications() && + cmd->accepts_miscellaneous()) { // Error: internally inconsistent. - throw std::string ("Unknown error. Please report."); - } - else if (cmd && - cmd->accepts_filter () && - ! cmd->accepts_modifications () && - ! cmd->accepts_miscellaneous ()) - { - a.tag ("FILTER"); + throw std::string("Unknown error. Please report."); + } else if (cmd && cmd->accepts_filter() && !cmd->accepts_modifications() && + !cmd->accepts_miscellaneous()) { + a.tag("FILTER"); changes = true; - } - else if (cmd && - cmd->accepts_filter () && - ! cmd->accepts_modifications () && - cmd->accepts_miscellaneous ()) - { + } else if (cmd && cmd->accepts_filter() && !cmd->accepts_modifications() && + cmd->accepts_miscellaneous()) { if (!afterCommand) - a.tag ("FILTER"); + a.tag("FILTER"); else - a.tag ("MISCELLANEOUS"); + a.tag("MISCELLANEOUS"); changes = true; - } - else if (cmd && - cmd->accepts_filter () && - cmd->accepts_modifications () && - ! cmd->accepts_miscellaneous ()) - { + } else if (cmd && cmd->accepts_filter() && cmd->accepts_modifications() && + !cmd->accepts_miscellaneous()) { if (!afterCommand) - a.tag ("FILTER"); + a.tag("FILTER"); else - a.tag ("MODIFICATION"); + a.tag("MODIFICATION"); changes = true; - } - else if (cmd && - cmd->accepts_filter () && - cmd->accepts_modifications () && - cmd->accepts_miscellaneous ()) - { + } else if (cmd && cmd->accepts_filter() && cmd->accepts_modifications() && + cmd->accepts_miscellaneous()) { // Error: internally inconsistent. - throw std::string ("Unknown error. Please report."); + throw std::string("Unknown error. Please report."); } } - if (changes && - Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::analyze categorizeArgs")); + if (changes && Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::analyze categorizeArgs")); } //////////////////////////////////////////////////////////////////////////////// @@ -1132,53 +939,43 @@ void CLI2::categorizeArgs () // task ( status:pending ) and ( +home or +work ) list // // the query is correct. -void CLI2::parenthesizeOriginalFilter () -{ +void CLI2::parenthesizeOriginalFilter() { // Locate the first and last ORIGINAL FILTER args. unsigned int firstOriginalFilter = 0; unsigned int lastOriginalFilter = 0; - for (unsigned int i = 1; i < _args.size (); ++i) - { - if (_args[i].hasTag ("FILTER") && - _args[i].hasTag ("ORIGINAL")) - { - if (firstOriginalFilter == 0) - firstOriginalFilter = i; + for (unsigned int i = 1; i < _args.size(); ++i) { + if (_args[i].hasTag("FILTER") && _args[i].hasTag("ORIGINAL")) { + if (firstOriginalFilter == 0) firstOriginalFilter = i; lastOriginalFilter = i; } } // If found, parenthesize the arg list accordingly. - if (firstOriginalFilter && - lastOriginalFilter) - { - std::vector reconstructed; - for (unsigned int i = 0; i < _args.size (); ++i) - { - if (i == firstOriginalFilter) - { - A2 openParen ("(", Lexer::Type::op); - openParen.tag ("ORIGINAL"); - openParen.tag ("FILTER"); - reconstructed.push_back (openParen); + if (firstOriginalFilter && lastOriginalFilter) { + std::vector reconstructed; + for (unsigned int i = 0; i < _args.size(); ++i) { + if (i == firstOriginalFilter) { + A2 openParen("(", Lexer::Type::op); + openParen.tag("ORIGINAL"); + openParen.tag("FILTER"); + reconstructed.push_back(openParen); } - reconstructed.push_back (_args[i]); + reconstructed.push_back(_args[i]); - if (i == lastOriginalFilter) - { - A2 closeParen (")", Lexer::Type::op); - closeParen.tag ("ORIGINAL"); - closeParen.tag ("FILTER"); - reconstructed.push_back (closeParen); + if (i == lastOriginalFilter) { + A2 closeParen(")", Lexer::Type::op); + closeParen.tag("ORIGINAL"); + closeParen.tag("FILTER"); + reconstructed.push_back(closeParen); } } _args = reconstructed; - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::analyze parenthesizeOriginalFilter")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::analyze parenthesizeOriginalFilter")); } } @@ -1186,11 +983,9 @@ void CLI2::parenthesizeOriginalFilter () // Scan all arguments and if any are an exact match for a command name, then // tag as CMD. If an argument is an exact match for an attribute, despite being // an inexact match for a command, then it is not a command. -bool CLI2::findCommand () -{ - for (auto& a : _args) - { - std::string raw = a.attribute ("raw"); +bool CLI2::findCommand() { + for (auto& a : _args) { + std::string raw = a.attribute("raw"); std::string canonical; // If the arg canonicalized to a 'cmd', but is also not an exact match @@ -1198,28 +993,28 @@ bool CLI2::findCommand () // task project=foo list // ^cmd ^cmd // ^attribute - if (exactMatch ("cmd", raw)) + if (exactMatch("cmd", raw)) canonical = raw; - else if (exactMatch ("attribute", raw)) + else if (exactMatch("attribute", raw)) continue; - else if (! canonicalize (canonical, "cmd", raw)) + else if (!canonicalize(canonical, "cmd", raw)) continue; - a.attribute ("canonical", canonical); - a.tag ("CMD"); + a.attribute("canonical", canonical); + a.tag("CMD"); // Apply command DNA as tags. - Command* command = Context::getContext ().commands[canonical]; - if (command->read_only ()) a.tag ("READONLY"); - if (command->displays_id ()) a.tag ("SHOWSID"); - if (command->needs_gc ()) a.tag ("RUNSGC"); - if (command->uses_context ()) a.tag ("USESCONTEXT"); - if (command->accepts_filter ()) a.tag ("ALLOWSFILTER"); - if (command->accepts_modifications ()) a.tag ("ALLOWSMODIFICATIONS"); - if (command->accepts_miscellaneous ()) a.tag ("ALLOWSMISC"); + Command* command = Context::getContext().commands[canonical]; + if (command->read_only()) a.tag("READONLY"); + if (command->displays_id()) a.tag("SHOWSID"); + if (command->needs_gc()) a.tag("RUNSGC"); + if (command->uses_context()) a.tag("USESCONTEXT"); + if (command->accepts_filter()) a.tag("ALLOWSFILTER"); + if (command->accepts_modifications()) a.tag("ALLOWSMODIFICATIONS"); + if (command->accepts_miscellaneous()) a.tag("ALLOWSMISC"); - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::analyze findCommand")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::analyze findCommand")); // Stop and indicate command found. return true; @@ -1231,15 +1026,11 @@ bool CLI2::findCommand () //////////////////////////////////////////////////////////////////////////////// // Search for exact 'value' in _entities category. -bool CLI2::exactMatch ( - const std::string& category, - const std::string& value) const -{ +bool CLI2::exactMatch(const std::string& category, const std::string& value) const { // Extract a list of entities for category. - auto c = _entities.equal_range (category); + auto c = _entities.equal_range(category); for (auto e = c.first; e != c.second; ++e) - if (value == e->second) - return true; + if (value == e->second) return true; return false; } @@ -1247,90 +1038,74 @@ bool CLI2::exactMatch ( //////////////////////////////////////////////////////////////////////////////// // +tag --> tags _hastag_ tag // -tag --> tags _notag_ tag -void CLI2::desugarFilterTags () -{ +void CLI2::desugarFilterTags() { bool changes = false; - std::vector reconstructed; - for (const auto& a : _args) - { - if (a._lextype == Lexer::Type::tag && - a.hasTag ("FILTER")) - { + std::vector reconstructed; + for (const auto& a : _args) { + if (a._lextype == Lexer::Type::tag && a.hasTag("FILTER")) { changes = true; - A2 left ("tags", Lexer::Type::dom); - left.tag ("FILTER"); - reconstructed.push_back (left); + A2 left("tags", Lexer::Type::dom); + left.tag("FILTER"); + reconstructed.push_back(left); - std::string raw = a.attribute ("raw"); + std::string raw = a.attribute("raw"); - A2 op (raw[0] == '+' ? "_hastag_" : "_notag_", Lexer::Type::op); - op.tag ("FILTER"); - reconstructed.push_back (op); + A2 op(raw[0] == '+' ? "_hastag_" : "_notag_", Lexer::Type::op); + op.tag("FILTER"); + reconstructed.push_back(op); - A2 right ("" + raw.substr (1) + "", Lexer::Type::string); - right.tag ("FILTER"); - reconstructed.push_back (right); - } - else - reconstructed.push_back (a); + A2 right("" + raw.substr(1) + "", Lexer::Type::string); + right.tag("FILTER"); + reconstructed.push_back(right); + } else + reconstructed.push_back(a); } - if (changes) - { + if (changes) { _args = reconstructed; - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::prepareFilter desugarFilterTags")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::prepareFilter desugarFilterTags")); } } //////////////////////////////////////////////////////////////////////////////// -void CLI2::findStrayModifications () -{ +void CLI2::findStrayModifications() { bool changes = false; - auto command = getCommand (); - if (command == "add" || - command == "log") - { - for (auto& a : _args) - { - if (a.hasTag ("FILTER")) - { - a.unTag ("FILTER"); - a.tag ("MODIFICATION"); + auto command = getCommand(); + if (command == "add" || command == "log") { + for (auto& a : _args) { + if (a.hasTag("FILTER")) { + a.unTag("FILTER"); + a.tag("MODIFICATION"); changes = true; } } } if (changes) - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::prepareFilter findStrayModifications")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::prepareFilter findStrayModifications")); } //////////////////////////////////////////////////////////////////////////////// // [.]:['"][]['"] --> name value -void CLI2::desugarFilterAttributes () -{ +void CLI2::desugarFilterAttributes() { bool changes = false; - std::vector reconstructed; - for (auto& a : _args) - { - if (a._lextype == Lexer::Type::pair && - a.hasTag ("FILTER")) - { - std::string raw = a.attribute ("raw"); - std::string name = a.attribute ("name"); - std::string mod = a.attribute ("modifier"); - std::string sep = a.attribute ("separator"); - std::string value = a.attribute ("value"); + std::vector reconstructed; + for (auto& a : _args) { + if (a._lextype == Lexer::Type::pair && a.hasTag("FILTER")) { + std::string raw = a.attribute("raw"); + std::string name = a.attribute("name"); + std::string mod = a.attribute("modifier"); + std::string sep = a.attribute("separator"); + std::string value = a.attribute("value"); // An unquoted string, while equivalent to an empty string, doesn't cause // an operand shortage in eval. - if (value == "") - value = "''"; + if (value == "") value = "''"; // Some values are expressions, which need to be lexed. The best way to // determine whether an expression is either a single value, or needs to @@ -1343,151 +1118,111 @@ void CLI2::desugarFilterAttributes () // 1d // ) // Use this sequence in place of a single value. - std::vector values = lexExpression (value); - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - { - Context::getContext ().debug ("CLI2::lexExpression " + name + ':' + value); - for (auto& v : values) - Context::getContext ().debug (" " + v.dump ()); - Context::getContext ().debug (" "); + std::vector values = lexExpression(value); + if (Context::getContext().config.getInteger("debug.parser") >= 2) { + Context::getContext().debug("CLI2::lexExpression " + name + ':' + value); + for (auto& v : values) Context::getContext().debug(" " + v.dump()); + Context::getContext().debug(" "); } bool found = false; std::string canonical; - if (canonicalize (canonical, "attribute", name)) - { + if (canonicalize(canonical, "attribute", name)) { // Certain attribute types do not suport math. // string --> no // numeric --> yes // date --> yes // duration --> yes bool evalSupported = true; - Column* col = Context::getContext ().columns[canonical]; - if (col && col->type () == "string") - evalSupported = false; + Column* col = Context::getContext().columns[canonical]; + if (col && col->type() == "string") evalSupported = false; - A2 lhs (name, Lexer::Type::dom); - lhs.tag ("FILTER"); - lhs.attribute ("canonical", canonical); - lhs.attribute ("modifier", mod); + A2 lhs(name, Lexer::Type::dom); + lhs.tag("FILTER"); + lhs.attribute("canonical", canonical); + lhs.attribute("modifier", mod); - A2 op ("", Lexer::Type::op); - op.tag ("FILTER"); + A2 op("", Lexer::Type::op); + op.tag("FILTER"); // Attribute types that do not support evaluation should be interpreted // as strings (currently this means that string attributes are not evaluated) - A2 rhs ("", evalSupported ? values[0]._lextype: Lexer::Type::string); - rhs.tag ("FILTER"); + A2 rhs("", evalSupported ? values[0]._lextype : Lexer::Type::string); + rhs.tag("FILTER"); // Special case for ':'. - if (mod == "") - { - op.attribute ("raw", "="); - rhs.attribute ("raw", value); - } - else if (mod == "before" || mod == "under" || mod == "below") - { - op.attribute ("raw", "<"); - rhs.attribute ("raw", value); - } - else if (mod == "after" || mod == "over" || mod == "above") - { - op.attribute ("raw", ">"); - rhs.attribute ("raw", value); - } - else if (mod == "by") - { - op.attribute ("raw", "<="); - rhs.attribute ("raw", value); - } - else if (mod == "none") - { - op.attribute ("raw", "=="); - rhs.attribute ("raw", "''"); - } - else if (mod == "any") - { - op.attribute ("raw", "!=="); - rhs.attribute ("raw", "''"); - } - else if (mod == "is" || mod == "equals") - { - op.attribute ("raw", "=="); - rhs.attribute ("raw", value); - } - else if (mod == "not") - { - op.attribute ("raw", "!="); - rhs.attribute ("raw", value); - } - else if (mod == "isnt") - { - op.attribute ("raw", "!=="); - rhs.attribute ("raw", value); - } - else if (mod == "has" || mod == "contains") - { - op.attribute ("raw", "~"); - rhs.attribute ("raw", value); - } - else if (mod == "hasnt") - { - op.attribute ("raw", "!~"); - rhs.attribute ("raw", value); - } - else if (mod == "startswith" || mod == "left") - { - op.attribute ("raw", "~"); - rhs.attribute ("raw", "^" + value); - } - else if (mod == "endswith" || mod == "right") - { - op.attribute ("raw", "~"); - rhs.attribute ("raw", value + "$"); - } - else if (mod == "word") - { - op.attribute ("raw", "~"); -#if defined (DARWIN) - rhs.attribute ("raw", value); -#elif defined (SOLARIS) - rhs.attribute ("raw", "\\<" + value + "\\>"); + if (mod == "") { + op.attribute("raw", "="); + rhs.attribute("raw", value); + } else if (mod == "before" || mod == "under" || mod == "below") { + op.attribute("raw", "<"); + rhs.attribute("raw", value); + } else if (mod == "after" || mod == "over" || mod == "above") { + op.attribute("raw", ">"); + rhs.attribute("raw", value); + } else if (mod == "by") { + op.attribute("raw", "<="); + rhs.attribute("raw", value); + } else if (mod == "none") { + op.attribute("raw", "=="); + rhs.attribute("raw", "''"); + } else if (mod == "any") { + op.attribute("raw", "!=="); + rhs.attribute("raw", "''"); + } else if (mod == "is" || mod == "equals") { + op.attribute("raw", "=="); + rhs.attribute("raw", value); + } else if (mod == "not") { + op.attribute("raw", "!="); + rhs.attribute("raw", value); + } else if (mod == "isnt") { + op.attribute("raw", "!=="); + rhs.attribute("raw", value); + } else if (mod == "has" || mod == "contains") { + op.attribute("raw", "~"); + rhs.attribute("raw", value); + } else if (mod == "hasnt") { + op.attribute("raw", "!~"); + rhs.attribute("raw", value); + } else if (mod == "startswith" || mod == "left") { + op.attribute("raw", "~"); + rhs.attribute("raw", "^" + value); + } else if (mod == "endswith" || mod == "right") { + op.attribute("raw", "~"); + rhs.attribute("raw", value + "$"); + } else if (mod == "word") { + op.attribute("raw", "~"); +#if defined(DARWIN) + rhs.attribute("raw", value); +#elif defined(SOLARIS) + rhs.attribute("raw", "\\<" + value + "\\>"); #else - rhs.attribute ("raw", "\\b" + value + "\\b"); + rhs.attribute("raw", "\\b" + value + "\\b"); #endif - } - else if (mod == "noword") - { - op.attribute ("raw", "!~"); -#if defined (DARWIN) - rhs.attribute ("raw", value); -#elif defined (SOLARIS) - rhs.attribute ("raw", "\\<" + value + "\\>"); + } else if (mod == "noword") { + op.attribute("raw", "!~"); +#if defined(DARWIN) + rhs.attribute("raw", value); +#elif defined(SOLARIS) + rhs.attribute("raw", "\\<" + value + "\\>"); #else - rhs.attribute ("raw", "\\b" + value + "\\b"); + rhs.attribute("raw", "\\b" + value + "\\b"); #endif - } - else - throw format ("Error: unrecognized attribute modifier '{1}'.", mod); + } else + throw format("Error: unrecognized attribute modifier '{1}'.", mod); - reconstructed.push_back (lhs); - reconstructed.push_back (op); + reconstructed.push_back(lhs); + reconstructed.push_back(op); // Do not modify this construct without full understanding. // Getting this wrong breaks a whole lot of filtering tests. - if (evalSupported) - { - for (auto& v : values) - reconstructed.push_back (v); - } - else if (Lexer::isDOM (rhs.attribute ("raw"))) - { + if (evalSupported) { + for (auto& v : values) reconstructed.push_back(v); + } else if (Lexer::isDOM(rhs.attribute("raw"))) { rhs._lextype = Lexer::Type::dom; - reconstructed.push_back (rhs); - } - else - { - reconstructed.push_back (rhs); + reconstructed.push_back(rhs); + } else { + reconstructed.push_back(rhs); } found = true; @@ -1496,66 +1231,58 @@ void CLI2::desugarFilterAttributes () // If the name does not canonicalize to either an attribute or a UDA // then it is not a recognized Lexer::Type::pair, so downgrade it to // Lexer::Type::word. - else - { + else { a._lextype = Lexer::Type::word; } if (found) changes = true; else - reconstructed.push_back (a); + reconstructed.push_back(a); } // Not a FILTER pair. else - reconstructed.push_back (a); + reconstructed.push_back(a); } - if (changes) - { + if (changes) { _args = reconstructed; - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::prepareFilter desugarFilterAttributes")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::prepareFilter desugarFilterAttributes")); } } //////////////////////////////////////////////////////////////////////////////// // /pattern/ --> description ~ 'pattern' -void CLI2::desugarFilterPatterns () -{ +void CLI2::desugarFilterPatterns() { bool changes = false; - std::vector reconstructed; - for (const auto& a : _args) - { - if (a._lextype == Lexer::Type::pattern && - a.hasTag ("FILTER")) - { + std::vector reconstructed; + for (const auto& a : _args) { + if (a._lextype == Lexer::Type::pattern && a.hasTag("FILTER")) { changes = true; - A2 lhs ("description", Lexer::Type::dom); - lhs.tag ("FILTER"); - reconstructed.push_back (lhs); + A2 lhs("description", Lexer::Type::dom); + lhs.tag("FILTER"); + reconstructed.push_back(lhs); - A2 op ("~", Lexer::Type::op); - op.tag ("FILTER"); - reconstructed.push_back (op); + A2 op("~", Lexer::Type::op); + op.tag("FILTER"); + reconstructed.push_back(op); - A2 rhs (a.attribute ("pattern"), Lexer::Type::string); - rhs.attribute ("flags", a.attribute ("flags")); - rhs.tag ("FILTER"); - reconstructed.push_back (rhs); - } - else - reconstructed.push_back (a); + A2 rhs(a.attribute("pattern"), Lexer::Type::string); + rhs.attribute("flags", a.attribute("flags")); + rhs.tag("FILTER"); + reconstructed.push_back(rhs); + } else + reconstructed.push_back(a); } - if (changes) - { + if (changes) { _args = reconstructed; - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::prepareFilter desugarFilterPatterns")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::prepareFilter desugarFilterPatterns")); } } @@ -1568,99 +1295,75 @@ void CLI2::desugarFilterPatterns () // a range: 5-10 // or a combination: 1,3,5-10 12 // -void CLI2::findIDs () -{ +void CLI2::findIDs() { bool changes = false; - if (Context::getContext ().config.getBoolean ("sugar")) - { + if (Context::getContext().config.getBoolean("sugar")) { bool previousFilterArgWasAnOperator = false; int filterCount = 0; - for (const auto& a : _args) - { - if (a.hasTag ("FILTER")) - { + for (const auto& a : _args) { + if (a.hasTag("FILTER")) { ++filterCount; - if (a._lextype == Lexer::Type::number) - { + if (a._lextype == Lexer::Type::number) { // Skip any number that was preceded by an operator. - if (! previousFilterArgWasAnOperator) - { + if (!previousFilterArgWasAnOperator) { changes = true; - std::string number = a.attribute ("raw"); - _id_ranges.emplace_back (number, number); + std::string number = a.attribute("raw"); + _id_ranges.emplace_back(number, number); } - } - else if (a._lextype == Lexer::Type::set) - { + } else if (a._lextype == Lexer::Type::set) { // Split the ID list into elements. - auto elements = split (a.attribute ("raw"), ','); + auto elements = split(a.attribute("raw"), ','); - for (auto& element : elements) - { + for (auto& element : elements) { changes = true; - auto hyphen = element.find ('-'); + auto hyphen = element.find('-'); if (hyphen != std::string::npos) - _id_ranges.emplace_back (element.substr (0, hyphen), element.substr (hyphen + 1)); + _id_ranges.emplace_back(element.substr(0, hyphen), element.substr(hyphen + 1)); else - _id_ranges.emplace_back (element, element); + _id_ranges.emplace_back(element, element); } } - std::string raw = a.attribute ("raw"); - previousFilterArgWasAnOperator = (a._lextype == Lexer::Type::op && - raw != "(" && - raw != ")") - ? true - : false; + std::string raw = a.attribute("raw"); + previousFilterArgWasAnOperator = + (a._lextype == Lexer::Type::op && raw != "(" && raw != ")") ? true : false; } } // If no IDs were found, and no filter was specified, look for number/set // listed as a MODIFICATION. - std::string command = getCommand (); + std::string command = getCommand(); - if (! _id_ranges.size () && - filterCount == 0 && - command != "add" && - command != "log") - { - for (auto& a : _args) - { - if (a.hasTag ("MODIFICATION")) - { - std::string raw = a.attribute ("raw"); + if (!_id_ranges.size() && filterCount == 0 && command != "add" && command != "log") { + for (auto& a : _args) { + if (a.hasTag("MODIFICATION")) { + std::string raw = a.attribute("raw"); // For a number to be an ID, it must not contain any sign or floating // point elements. - if (a._lextype == Lexer::Type::number && - raw.find ('.') == std::string::npos && - raw.find ('e') == std::string::npos && - raw.find ('-') == std::string::npos) - { + if (a._lextype == Lexer::Type::number && raw.find('.') == std::string::npos && + raw.find('e') == std::string::npos && raw.find('-') == std::string::npos) { changes = true; - a.unTag ("MODIFICATION"); - a.tag ("FILTER"); - _id_ranges.emplace_back (raw, raw); - } - else if (a._lextype == Lexer::Type::set) - { - a.unTag ("MODIFICATION"); - a.tag ("FILTER"); + a.unTag("MODIFICATION"); + a.tag("FILTER"); + _id_ranges.emplace_back(raw, raw); + } else if (a._lextype == Lexer::Type::set) { + a.unTag("MODIFICATION"); + a.tag("FILTER"); // Split the ID list into elements. - auto elements = split (raw, ','); + auto elements = split(raw, ','); - for (const auto& element : elements) - { + for (const auto& element : elements) { changes = true; - auto hyphen = element.find ('-'); + auto hyphen = element.find('-'); if (hyphen != std::string::npos) - _id_ranges.emplace_back (element.substr (0, hyphen), element.substr (hyphen + 1)); + _id_ranges.emplace_back(element.substr(0, hyphen), element.substr(hyphen + 1)); else - _id_ranges.emplace_back (element, element); + _id_ranges.emplace_back(element, element); } } } @@ -1669,117 +1372,89 @@ void CLI2::findIDs () } // Sugar-free. - else - { - std::vector reconstructed; - for (const auto& a : _args) - { - if (a.hasTag ("FILTER") && - a._lextype == Lexer::Type::number) - { + else { + std::vector reconstructed; + for (const auto& a : _args) { + if (a.hasTag("FILTER") && a._lextype == Lexer::Type::number) { changes = true; - A2 pair ("id:" + a.attribute ("raw"), Lexer::Type::pair); - pair.tag ("FILTER"); - pair.decompose (); - reconstructed.push_back (pair); - } - else - reconstructed.push_back (a); + A2 pair("id:" + a.attribute("raw"), Lexer::Type::pair); + pair.tag("FILTER"); + pair.decompose(); + reconstructed.push_back(pair); + } else + reconstructed.push_back(a); } - if (changes) - _args = reconstructed; + if (changes) _args = reconstructed; } if (changes) - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::prepareFilter findIDs")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::prepareFilter findIDs")); } //////////////////////////////////////////////////////////////////////////////// -void CLI2::findUUIDs () -{ +void CLI2::findUUIDs() { bool changes = false; - if (Context::getContext ().config.getBoolean ("sugar")) - { - for (const auto& a : _args) - { - if (a._lextype == Lexer::Type::uuid && - a.hasTag ("FILTER")) - { + if (Context::getContext().config.getBoolean("sugar")) { + for (const auto& a : _args) { + if (a._lextype == Lexer::Type::uuid && a.hasTag("FILTER")) { changes = true; - _uuid_list.push_back (a.attribute ("raw")); + _uuid_list.push_back(a.attribute("raw")); } } - if (! _uuid_list.size ()) - { - for (auto& a : _args) - { - if (a._lextype == Lexer::Type::uuid && - a.hasTag ("MODIFICATION")) - { + if (!_uuid_list.size()) { + for (auto& a : _args) { + if (a._lextype == Lexer::Type::uuid && a.hasTag("MODIFICATION")) { changes = true; - a.unTag ("MODIFICATION"); - a.tag ("FILTER"); - _uuid_list.push_back (a.attribute ("raw")); + a.unTag("MODIFICATION"); + a.tag("FILTER"); + _uuid_list.push_back(a.attribute("raw")); } } } } // Sugar-free. - else - { - std::vector reconstructed; - for (const auto& a : _args) - { - if (a.hasTag ("FILTER") && - a._lextype == Lexer::Type::uuid) - { + else { + std::vector reconstructed; + for (const auto& a : _args) { + if (a.hasTag("FILTER") && a._lextype == Lexer::Type::uuid) { changes = true; - A2 pair ("uuid:" + a.attribute ("raw"), Lexer::Type::pair); - pair.tag ("FILTER"); - pair.decompose (); - reconstructed.push_back (pair); - } - else - reconstructed.push_back (a); + A2 pair("uuid:" + a.attribute("raw"), Lexer::Type::pair); + pair.tag("FILTER"); + pair.decompose(); + reconstructed.push_back(pair); + } else + reconstructed.push_back(a); } - if (changes) - _args = reconstructed; + if (changes) _args = reconstructed; } if (changes) - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::prepareFilter findUUIDs")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::prepareFilter findUUIDs")); } //////////////////////////////////////////////////////////////////////////////// -void CLI2::insertIDExpr () -{ +void CLI2::insertIDExpr() { // Skip completely if no ID/UUID was found. This is because below, '(' and ')' // are inserted regardless of list size. - if (! _id_ranges.size () && - ! _uuid_list.size ()) - return; + if (!_id_ranges.size() && !_uuid_list.size()) return; // Find the *first* occurence of lexer type set/number/uuid, and replace it // with a synthesized expression. All other occurences are eaten. bool changes = false; bool foundID = false; - std::vector reconstructed; - for (const auto& a : _args) - { - if ((a._lextype == Lexer::Type::set || - a._lextype == Lexer::Type::number || + std::vector reconstructed; + for (const auto& a : _args) { + if ((a._lextype == Lexer::Type::set || a._lextype == Lexer::Type::number || a._lextype == Lexer::Type::uuid) && - a.hasTag ("FILTER")) - { - if (! foundID) - { + a.hasTag("FILTER")) { + if (!foundID) { foundID = true; changes = true; @@ -1799,146 +1474,136 @@ void CLI2::insertIDExpr () // ) // Building block operators. - A2 openParen ("(", Lexer::Type::op); openParen.tag ("FILTER"); - A2 closeParen (")", Lexer::Type::op); closeParen.tag ("FILTER"); - A2 opOr ("or", Lexer::Type::op); opOr.tag ("FILTER"); - A2 opAnd ("and", Lexer::Type::op); opAnd.tag ("FILTER"); - A2 opSimilar ("=", Lexer::Type::op); opSimilar.tag ("FILTER"); - A2 opEqual ("==", Lexer::Type::op); opEqual.tag ("FILTER"); - A2 opGTE (">=", Lexer::Type::op); opGTE.tag ("FILTER"); - A2 opLTE ("<=", Lexer::Type::op); opLTE.tag ("FILTER"); + A2 openParen("(", Lexer::Type::op); + openParen.tag("FILTER"); + A2 closeParen(")", Lexer::Type::op); + closeParen.tag("FILTER"); + A2 opOr("or", Lexer::Type::op); + opOr.tag("FILTER"); + A2 opAnd("and", Lexer::Type::op); + opAnd.tag("FILTER"); + A2 opSimilar("=", Lexer::Type::op); + opSimilar.tag("FILTER"); + A2 opEqual("==", Lexer::Type::op); + opEqual.tag("FILTER"); + A2 opGTE(">=", Lexer::Type::op); + opGTE.tag("FILTER"); + A2 opLTE("<=", Lexer::Type::op); + opLTE.tag("FILTER"); // Building block attributes. - A2 argID ("id", Lexer::Type::dom); - argID.tag ("FILTER"); + A2 argID("id", Lexer::Type::dom); + argID.tag("FILTER"); - A2 argUUID ("uuid", Lexer::Type::dom); - argUUID.tag ("FILTER"); + A2 argUUID("uuid", Lexer::Type::dom); + argUUID.tag("FILTER"); - reconstructed.push_back (openParen); + reconstructed.push_back(openParen); // Add all ID ranges. - for (auto r = _id_ranges.begin (); r != _id_ranges.end (); ++r) - { - if (r != _id_ranges.begin ()) - reconstructed.push_back (opOr); + for (auto r = _id_ranges.begin(); r != _id_ranges.end(); ++r) { + if (r != _id_ranges.begin()) reconstructed.push_back(opOr); - if (r->first == r->second) - { - reconstructed.push_back (openParen); - reconstructed.push_back (argID); - reconstructed.push_back (opEqual); + if (r->first == r->second) { + reconstructed.push_back(openParen); + reconstructed.push_back(argID); + reconstructed.push_back(opEqual); - A2 value (r->first, Lexer::Type::number); - value.tag ("FILTER"); - reconstructed.push_back (value); + A2 value(r->first, Lexer::Type::number); + value.tag("FILTER"); + reconstructed.push_back(value); - reconstructed.push_back (closeParen); - } - else - { + reconstructed.push_back(closeParen); + } else { bool ascending = true; - int low = strtol (r->first.c_str (), nullptr, 10); - int high = strtol (r->second.c_str (), nullptr, 10); + int low = strtol(r->first.c_str(), nullptr, 10); + int high = strtol(r->second.c_str(), nullptr, 10); if (low <= high) ascending = true; else ascending = false; - reconstructed.push_back (openParen); - reconstructed.push_back (argID); - reconstructed.push_back (opGTE); + reconstructed.push_back(openParen); + reconstructed.push_back(argID); + reconstructed.push_back(opGTE); - A2 startValue ((ascending ? r->first : r->second), Lexer::Type::number); - startValue.tag ("FILTER"); - reconstructed.push_back (startValue); + A2 startValue((ascending ? r->first : r->second), Lexer::Type::number); + startValue.tag("FILTER"); + reconstructed.push_back(startValue); - reconstructed.push_back (opAnd); - reconstructed.push_back (argID); - reconstructed.push_back (opLTE); + reconstructed.push_back(opAnd); + reconstructed.push_back(argID); + reconstructed.push_back(opLTE); - A2 endValue ((ascending ? r->second : r->first), Lexer::Type::number); - endValue.tag ("FILTER"); - reconstructed.push_back (endValue); + A2 endValue((ascending ? r->second : r->first), Lexer::Type::number); + endValue.tag("FILTER"); + reconstructed.push_back(endValue); - reconstructed.push_back (closeParen); + reconstructed.push_back(closeParen); } } // Combine the ID and UUID sections with 'or'. - if (_id_ranges.size () && - _uuid_list.size ()) - reconstructed.push_back (opOr); + if (_id_ranges.size() && _uuid_list.size()) reconstructed.push_back(opOr); // Add all UUID list items. - for (auto u = _uuid_list.begin (); u != _uuid_list.end (); ++u) - { - if (u != _uuid_list.begin ()) - reconstructed.push_back (opOr); + for (auto u = _uuid_list.begin(); u != _uuid_list.end(); ++u) { + if (u != _uuid_list.begin()) reconstructed.push_back(opOr); - reconstructed.push_back (openParen); - reconstructed.push_back (argUUID); - reconstructed.push_back (opSimilar); + reconstructed.push_back(openParen); + reconstructed.push_back(argUUID); + reconstructed.push_back(opSimilar); - A2 value (*u, Lexer::Type::string); - value.tag ("FILTER"); - reconstructed.push_back (value); + A2 value(*u, Lexer::Type::string); + value.tag("FILTER"); + reconstructed.push_back(value); - reconstructed.push_back (closeParen); + reconstructed.push_back(closeParen); } - reconstructed.push_back (closeParen); + reconstructed.push_back(closeParen); } // No 'else' because all set/number/uuid args but the first are removed. - } - else - reconstructed.push_back (a); + } else + reconstructed.push_back(a); } - if (changes) - { + if (changes) { _args = reconstructed; - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::prepareFilter insertIDExpr")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::prepareFilter insertIDExpr")); } } //////////////////////////////////////////////////////////////////////////////// // FILTER Lexer::Type::word args will become part of an expression, and so they // need to be Lexed. -void CLI2::lexFilterArgs () -{ +void CLI2::lexFilterArgs() { bool changes = false; - std::vector reconstructed; - for (const auto& a : _args) - { - if (a._lextype == Lexer::Type::word && - a.hasTag ("FILTER")) - { + std::vector reconstructed; + for (const auto& a : _args) { + if (a._lextype == Lexer::Type::word && a.hasTag("FILTER")) { changes = true; std::string lexeme; Lexer::Type type; - Lexer lex (a.attribute ("raw")); - while (lex.token (lexeme, type)) - { - A2 extra (lexeme, type); - extra.tag ("FILTER"); - reconstructed.push_back (extra); + Lexer lex(a.attribute("raw")); + while (lex.token(lexeme, type)) { + A2 extra(lexeme, type); + extra.tag("FILTER"); + reconstructed.push_back(extra); } - } - else - reconstructed.push_back (a); + } else + reconstructed.push_back(a); } - if (changes) - { + if (changes) { _args = reconstructed; - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::prepareFilter lexFilterArgs")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::prepareFilter lexFilterArgs")); } } @@ -1954,39 +1619,28 @@ void CLI2::lexFilterArgs () // Lexer::Type::identifier // Lexer::Type::date // -void CLI2::desugarFilterPlainArgs () -{ +void CLI2::desugarFilterPlainArgs() { // First walk the arg list looking for plain words that are not part of an // existing expression. auto prevprev = &_args[0]; auto prev = &_args[0]; - for (auto& a : _args) - { - auto raw = a.attribute ("raw"); - auto praw = prev->attribute ("raw"); - auto ppraw = prevprev->attribute ("raw"); + for (auto& a : _args) { + auto raw = a.attribute("raw"); + auto praw = prev->attribute("raw"); + auto ppraw = prevprev->attribute("raw"); - if ((prevprev->_lextype != Lexer::Type::op || // argX - ppraw == "(" || - ppraw == ")" || - ppraw == "and" || - ppraw == "or" || - ppraw == "xor") && + if ((prevprev->_lextype != Lexer::Type::op || // argX + ppraw == "(" || ppraw == ")" || ppraw == "and" || ppraw == "or" || ppraw == "xor") && (prev->_lextype == Lexer::Type::identifier || // candidate - prev->_lextype == Lexer::Type::date || // candidate - prev->_lextype == Lexer::Type::word) && // candidate + prev->_lextype == Lexer::Type::date || // candidate + prev->_lextype == Lexer::Type::word) && // candidate - prev->hasTag ("FILTER") && // candidate + prev->hasTag("FILTER") && // candidate - (a._lextype != Lexer::Type::op || // argY - raw == "(" || - raw == ")" || - raw == "and" || - raw == "or" || - raw == "xor")) - { - prev->tag ("PLAIN"); + (a._lextype != Lexer::Type::op || // argY + raw == "(" || raw == ")" || raw == "and" || raw == "or" || raw == "xor")) { + prev->tag("PLAIN"); } prevprev = prev; @@ -1994,61 +1648,53 @@ void CLI2::desugarFilterPlainArgs () } // Cover the case where the *last* argument is a plain arg. - auto& penultimate = _args[_args.size () - 2]; - auto praw = penultimate.attribute ("raw"); - auto& last = _args[_args.size () - 1]; - if ((penultimate._lextype != Lexer::Type::op || // argX - praw == "(" || - praw == ")" || - praw == "and" || - praw == "or" || - praw == "xor") && + auto& penultimate = _args[_args.size() - 2]; + auto praw = penultimate.attribute("raw"); + auto& last = _args[_args.size() - 1]; + if ((penultimate._lextype != Lexer::Type::op || // argX + praw == "(" || praw == ")" || praw == "and" || praw == "or" || praw == "xor") && - (last._lextype == Lexer::Type::identifier || // candidate - last._lextype == Lexer::Type::word) && // candidate + (last._lextype == Lexer::Type::identifier || // candidate + last._lextype == Lexer::Type::word) && // candidate - last.hasTag ("FILTER")) // candidate + last.hasTag("FILTER")) // candidate { - last.tag ("PLAIN"); + last.tag("PLAIN"); } // Walk the list again, upgrading PLAIN args. bool changes = false; - std::vector reconstructed; - for (const auto& a : _args) - { - if (a.hasTag ("PLAIN")) - { + std::vector reconstructed; + for (const auto& a : _args) { + if (a.hasTag("PLAIN")) { changes = true; - A2 lhs ("description", Lexer::Type::dom); - lhs.attribute ("canonical", "description"); - lhs.tag ("FILTER"); - lhs.tag ("PLAIN"); - reconstructed.push_back (lhs); + A2 lhs("description", Lexer::Type::dom); + lhs.attribute("canonical", "description"); + lhs.tag("FILTER"); + lhs.tag("PLAIN"); + reconstructed.push_back(lhs); - A2 op ("~", Lexer::Type::op); - op.tag ("FILTER"); - op.tag ("PLAIN"); - reconstructed.push_back (op); + A2 op("~", Lexer::Type::op); + op.tag("FILTER"); + op.tag("PLAIN"); + reconstructed.push_back(op); - std::string word = a.attribute ("raw"); - Lexer::dequote (word); - A2 rhs (word, Lexer::Type::string); - rhs.tag ("FILTER"); - rhs.tag ("PLAIN"); - reconstructed.push_back (rhs); - } - else - reconstructed.push_back (a); + std::string word = a.attribute("raw"); + Lexer::dequote(word); + A2 rhs(word, Lexer::Type::string); + rhs.tag("FILTER"); + rhs.tag("PLAIN"); + reconstructed.push_back(rhs); + } else + reconstructed.push_back(a); } - if (changes) - { + if (changes) { _args = reconstructed; - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::prepareFilter desugarFilterPlainArgs")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::prepareFilter desugarFilterPlainArgs")); } } @@ -2062,13 +1708,11 @@ void CLI2::desugarFilterPlainArgs () // ( status = pending ) ( project = Home ) // ^ // it -----| => false -bool CLI2::isEmptyParenExpression (std::vector::iterator it, bool forward /* = true */) const -{ +bool CLI2::isEmptyParenExpression(std::vector::iterator it, bool forward /* = true */) const { int open = 0; int closed = 0; - for (auto a = it; a != (forward ? _args.end (): _args.begin()); (forward ? ++a: --a)) - { + for (auto a = it; a != (forward ? _args.end() : _args.begin()); (forward ? ++a : --a)) { if (a->attribute("raw") == "(") open++; else if (a->attribute("raw") == ")") @@ -2078,8 +1722,7 @@ bool CLI2::isEmptyParenExpression (std::vector::iterator it, bool forward /* return false; // Getting balanced parentheses means we have an empty paren expression - if (open == closed && open != 0) - return true; + if (open == closed && open != 0) return true; } // Should not end here. @@ -2095,39 +1738,28 @@ bool CLI2::isEmptyParenExpression (std::vector::iterator it, bool forward /* // ) ( --> ) and ( // --> and // -void CLI2::insertJunctions () -{ +void CLI2::insertJunctions() { bool changes = false; - std::vector reconstructed; - auto prev = _args.begin (); + std::vector reconstructed; + auto prev = _args.begin(); - for (auto a = _args.begin (); a != _args.end (); ++a) - { - if (a->hasTag ("FILTER")) - { + for (auto a = _args.begin(); a != _args.end(); ++a) { + if (a->hasTag("FILTER")) { // The prev iterator should be the first FILTER arg. - if (prev == _args.begin ()) - prev = a; + if (prev == _args.begin()) prev = a; // Insert AND between terms. - else if (a != prev) - { - if ((prev->_lextype != Lexer::Type::op && - a->attribute ("raw") == "(" && - ! isEmptyParenExpression(a, true) ) || - (prev->attribute ("raw") == ")" && - a->_lextype != Lexer::Type::op && - ! isEmptyParenExpression(prev, false)) || - (prev->attribute ("raw") == ")" && - a->attribute ("raw") == "(" && - ! isEmptyParenExpression(a, true) && - ! isEmptyParenExpression(prev, false)) || - (prev->_lextype != Lexer::Type::op && - a->_lextype != Lexer::Type::op)) - { - A2 opOr ("and", Lexer::Type::op); - opOr.tag ("FILTER"); - reconstructed.push_back (opOr); + else if (a != prev) { + if ((prev->_lextype != Lexer::Type::op && a->attribute("raw") == "(" && + !isEmptyParenExpression(a, true)) || + (prev->attribute("raw") == ")" && a->_lextype != Lexer::Type::op && + !isEmptyParenExpression(prev, false)) || + (prev->attribute("raw") == ")" && a->attribute("raw") == "(" && + !isEmptyParenExpression(a, true) && !isEmptyParenExpression(prev, false)) || + (prev->_lextype != Lexer::Type::op && a->_lextype != Lexer::Type::op)) { + A2 opOr("and", Lexer::Type::op); + opOr.tag("FILTER"); + reconstructed.push_back(opOr); changes = true; } } @@ -2136,15 +1768,14 @@ void CLI2::insertJunctions () prev = a; } - reconstructed.push_back (*a); + reconstructed.push_back(*a); } - if (changes) - { + if (changes) { _args = reconstructed; - if (Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::prepareFilter insertJunctions")); + if (Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::prepareFilter insertJunctions")); } } @@ -2157,78 +1788,65 @@ void CLI2::insertJunctions () // 2. If no command was found, but an ID/UUID was found, then assume a command // of 'information'. // -void CLI2::defaultCommand () -{ +void CLI2::defaultCommand() { // Scan the top-level branches for evidence of ID, UUID, overrides and other // arguments. - bool changes = false; - bool found_command = false; - bool found_sequence = false; + bool changes = false; + bool found_command = false; + bool found_sequence = false; - for (const auto& a : _args) - { - std::string raw = a.attribute ("raw"); + for (const auto& a : _args) { + std::string raw = a.attribute("raw"); - if (a.hasTag ("CMD")) - found_command = true; + if (a.hasTag("CMD")) found_command = true; - if (a._lextype == Lexer::Type::uuid || - a._lextype == Lexer::Type::number) - found_sequence = true; + if (a._lextype == Lexer::Type::uuid || a._lextype == Lexer::Type::number) found_sequence = true; } // If no command was specified, then a command will be inserted. - if (! found_command) - { + if (!found_command) { // Default command. - if (! found_sequence) - { + if (!found_sequence) { // Apply overrides, if any. - std::string defaultCommand = Context::getContext ().config.get ("default.command"); - if (defaultCommand != "") - { + std::string defaultCommand = Context::getContext().config.get("default.command"); + if (defaultCommand != "") { // Modify _args, _original_args to be: // [ ...] [...] - std::vector reconstructedOriginals {_original_args[0]}; - std::vector reconstructed {_args[0]}; + std::vector reconstructedOriginals{_original_args[0]}; + std::vector reconstructed{_args[0]}; std::string lexeme; Lexer::Type type; - Lexer lex (defaultCommand); + Lexer lex(defaultCommand); - while (lex.token (lexeme, type)) - { - reconstructedOriginals.emplace_back (lexeme, type); + while (lex.token(lexeme, type)) { + reconstructedOriginals.emplace_back(lexeme, type); - A2 cmd (lexeme, type); - cmd.tag ("DEFAULT"); - reconstructed.push_back (cmd); + A2 cmd(lexeme, type); + cmd.tag("DEFAULT"); + reconstructed.push_back(cmd); } - for (unsigned int i = 1; i < _original_args.size (); ++i) - reconstructedOriginals.push_back (_original_args[i]); + for (unsigned int i = 1; i < _original_args.size(); ++i) + reconstructedOriginals.push_back(_original_args[i]); - for (unsigned int i = 1; i < _args.size (); ++i) - reconstructed.push_back (_args[i]); + for (unsigned int i = 1; i < _args.size(); ++i) reconstructed.push_back(_args[i]); _original_args = reconstructedOriginals; _args = reconstructed; changes = true; } - } - else - { - A2 info ("information", Lexer::Type::word); - info.tag ("ASSUMED"); - _args.push_back (info); + } else { + A2 info("information", Lexer::Type::word); + info.tag("ASSUMED"); + _args.push_back(info); changes = true; } } - if (changes && - Context::getContext ().config.getInteger ("debug.parser") >= 2) - Context::getContext ().debug (dump ("CLI2::analyze defaultCommand")); + if (changes && Context::getContext().config.getInteger("debug.parser") >= 2) + Context::getContext().debug(dump("CLI2::analyze defaultCommand")); } //////////////////////////////////////////////////////////////////////////////// @@ -2242,30 +1860,27 @@ void CLI2::defaultCommand () // + // 1d // ) -std::vector CLI2::lexExpression (const std::string& expression) -{ - std::vector lexed; +std::vector CLI2::lexExpression(const std::string& expression) { + std::vector lexed; std::string lexeme; Lexer::Type type; - Lexer lex (expression); - while (lex.token (lexeme, type)) - { - A2 token (lexeme, type); - token.tag ("FILTER"); - lexed.push_back (token); + Lexer lex(expression); + while (lex.token(lexeme, type)) { + A2 token(lexeme, type); + token.tag("FILTER"); + lexed.push_back(token); } // If there were multiple tokens, parenthesize, because this expression will // be used as a value. - if (lexed.size () > 1) - { - A2 openParen ("(", Lexer::Type::op); - openParen.tag ("FILTER"); - A2 closeParen (")", Lexer::Type::op); - closeParen.tag ("FILTER"); + if (lexed.size() > 1) { + A2 openParen("(", Lexer::Type::op); + openParen.tag("FILTER"); + A2 closeParen(")", Lexer::Type::op); + closeParen.tag("FILTER"); - lexed.insert (lexed.begin (), openParen); - lexed.push_back (closeParen); + lexed.insert(lexed.begin(), openParen); + lexed.push_back(closeParen); } return lexed; diff --git a/src/CLI2.h b/src/CLI2.h index 6bada618a..ec446d688 100644 --- a/src/CLI2.h +++ b/src/CLI2.h @@ -26,100 +26,98 @@ #ifndef INCLUDED_CLI2 #define INCLUDED_CLI2 -#include -#include -#include -#include -#include #include +#include + +#include +#include +#include +#include // Represents a single argument. -class A2 -{ -public: - A2 (const std::string&, Lexer::Type); - A2 (const A2&); - A2& operator= (const A2&); - bool hasTag (const std::string&) const; - void tag (const std::string&); - void unTag (const std::string&); - void attribute (const std::string&, const std::string&); - const std::string attribute (const std::string&) const; - const std::string getToken () const; - const std::string dump () const; - void decompose (); +class A2 { + public: + A2(const std::string&, Lexer::Type); + A2(const A2&); + A2& operator=(const A2&); + bool hasTag(const std::string&) const; + void tag(const std::string&); + void unTag(const std::string&); + void attribute(const std::string&, const std::string&); + const std::string attribute(const std::string&) const; + const std::string getToken() const; + const std::string dump() const; + void decompose(); -public: - Lexer::Type _lextype {Lexer::Type::word}; - std::vector _tags {}; - std::map _attributes {}; + public: + Lexer::Type _lextype{Lexer::Type::word}; + std::vector _tags{}; + std::map _attributes{}; }; // Represents the command line. -class CLI2 -{ -public: +class CLI2 { + public: static int minimumMatchLength; - static bool getOverride (int, const char**, File&); - static bool getDataLocation (int, const char**, Path&); - static void applyOverrides (int, const char**); + static bool getOverride(int, const char**, File&); + static bool getDataLocation(int, const char**, Path&); + static void applyOverrides(int, const char**); -public: - CLI2 () = default; - void alias (const std::string&, const std::string&); - void entity (const std::string&, const std::string&); + public: + CLI2() = default; + void alias(const std::string&, const std::string&); + void entity(const std::string&, const std::string&); - void add (const std::string&); - void add (const std::vector &, int offset = 0); - void analyze (); - void addFilter (const std::string& arg); - void addModifications (const std::string& arg); - void addContext (bool readable, bool writeable); - void prepareFilter (); - const std::vector getWords (); - const std::vector getMiscellaneous (); - bool canonicalize (std::string&, const std::string&, const std::string&); - std::string getBinary () const; - std::string getCommand (bool canonical = true) const; - const std::string dump (const std::string& title = "CLI2 Parser") const; + void add(const std::string&); + void add(const std::vector&, int offset = 0); + void analyze(); + void addFilter(const std::string& arg); + void addModifications(const std::string& arg); + void addContext(bool readable, bool writeable); + void prepareFilter(); + const std::vector getWords(); + const std::vector getMiscellaneous(); + bool canonicalize(std::string&, const std::string&, const std::string&); + std::string getBinary() const; + std::string getCommand(bool canonical = true) const; + const std::string dump(const std::string& title = "CLI2 Parser") const; -private: - void handleArg0 (); - void lexArguments (); - void demotion (); - void aliasExpansion (); - void canonicalizeNames (); - void categorizeArgs (); - void parenthesizeOriginalFilter (); - bool findCommand (); - bool exactMatch (const std::string&, const std::string&) const; - void desugarFilterTags (); - void findStrayModifications (); - void desugarFilterAttributes (); - void desugarFilterPatterns (); - void findIDs (); - void findUUIDs (); - void insertIDExpr (); - void lexFilterArgs (); - bool isEmptyParenExpression (std::vector::iterator it, bool forward = true) const; - void desugarFilterPlainArgs (); - void insertJunctions (); - void defaultCommand (); - std::vector lexExpression (const std::string&); + private: + void handleArg0(); + void lexArguments(); + void demotion(); + void aliasExpansion(); + void canonicalizeNames(); + void categorizeArgs(); + void parenthesizeOriginalFilter(); + bool findCommand(); + bool exactMatch(const std::string&, const std::string&) const; + void desugarFilterTags(); + void findStrayModifications(); + void desugarFilterAttributes(); + void desugarFilterPatterns(); + void findIDs(); + void findUUIDs(); + void insertIDExpr(); + void lexFilterArgs(); + bool isEmptyParenExpression(std::vector::iterator it, bool forward = true) const; + void desugarFilterPlainArgs(); + void insertJunctions(); + void defaultCommand(); + std::vector lexExpression(const std::string&); -public: - std::multimap _entities {}; - std::map _aliases {}; - std::unordered_map _canonical_cache {}; - std::vector _original_args {}; - std::vector _args {}; + public: + std::multimap _entities{}; + std::map _aliases{}; + std::unordered_map _canonical_cache{}; + std::vector _original_args{}; + std::vector _args{}; - std::vector > _id_ranges {}; - std::vector _uuid_list {}; - std::string _command {""}; - bool _context_added {false}; + std::vector> _id_ranges{}; + std::vector _uuid_list{}; + std::string _command{""}; + bool _context_added{false}; }; #endif - diff --git a/src/Context.cpp b/src/Context.cpp index 2004d26ce..363c1d640 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -28,25 +28,26 @@ // cmake.h include header must come first #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include + +#include +#include +#include +#include #include +#include #ifdef HAVE_COMMIT #include @@ -61,418 +62,451 @@ //////////////////////////////////////////////////////////////////////////////// // This string is parsed and used as default values for configuration. -// Note: New configuration options should be added to the vim syntax file in scripts/vim/syntax/taskrc.vim +// Note: New configuration options should be added to the vim syntax file in +// scripts/vim/syntax/taskrc.vim std::string configurationDefaults = - "# Taskwarrior program configuration file.\n" - "# For more documentation, see https://taskwarrior.org or try 'man task', 'man task-color',\n" - "# 'man task-sync' or 'man taskrc'\n" - "\n" - "# Here is an example of entries that use the default, override and blank values\n" - "# variable=foo -- By specifying a value, this overrides the default\n" - "# variable= -- By specifying no value, this means no default\n" - "# #variable=foo -- By commenting out the line, or deleting it, this uses the default\n" - "\n" - "# You can also refence environment variables:\n" - "# variable=$HOME/task\n" - "# variable=$VALUE\n" - "\n" - "# Use the command 'task show' to see all defaults and overrides\n" - "\n" - "# Files\n" - "data.location=~/.task\n" - "gc=1 # Garbage-collect data files - DO NOT CHANGE unless you are sure\n" - "exit.on.missing.db=0 # Whether to exit if ~/.task is not found\n" - "hooks=1 # Master control switch for hooks\n" - "\n" - "# Terminal\n" - "detection=1 # Detects terminal width\n" - "defaultwidth=80 # Without detection, assumed width\n" - "defaultheight=24 # Without detection, assumed height\n" - "avoidlastcolumn=0 # Fixes Cygwin width problem\n" - "hyphenate=1 # Hyphenates lines wrapped on non-word-breaks\n" - "#editor=vi # Preferred text editor\n" - "reserved.lines=1 # Assume a 1-line prompt\n" - "\n" - "# Miscellaneous\n" - "# verbose= # Comma-separated list. May contain any subset of:\n" - "# affected,blank,context,default,edit,filter,footnote,header,label,new-id,new-uuid,news,override,project,recur,special,sync\n" - "verbose=affected,blank,context,edit,header,footnote,label,new-id,news,project,special,sync,override,recur\n" - "confirmation=1 # Confirmation on delete, big changes\n" - "recurrence=1 # Enable recurrence\n" - "recurrence.confirmation=prompt # Confirmation for propagating changes among recurring tasks (yes/no/prompt)\n" - "allow.empty.filter=1 # An empty filter gets a warning and requires confirmation\n" - "indent.annotation=2 # Indent spaces for annotations\n" - "indent.report=0 # Indent spaces for whole report\n" - "row.padding=0 # Left and right padding for each row of report\n" - "column.padding=1 # Spaces between each column in a report\n" - "bulk=3 # 3 or more tasks considered a bulk change and is confirmed\n" - "nag=You have more urgent tasks. # Nag message to keep you honest\n" // TODO - "search.case.sensitive=1 # Setting to no allows case insensitive searches\n" - "active.indicator=* # What to show as an active task indicator\n" - "tag.indicator=+ # What to show as a tag indicator\n" - "dependency.indicator=D # What to show as a dependency indicator\n" - "recurrence.indicator=R # What to show as a task recurrence indicator\n" - "recurrence.limit=1 # Number of future recurring pending tasks\n" - "undo.style=side # Undo style - can be 'side', or 'diff'\n" - "regex=1 # Assume all search/filter strings are regexes\n" - "xterm.title=0 # Sets xterm title for some commands\n" - "expressions=infix # Prefer infix over postfix expressions\n" - "json.array=1 # Enclose JSON output in [ ]\n" - "abbreviation.minimum=2 # Shortest allowed abbreviation\n" - "news.version= # Latest version highlights read by the user\n" - "purge.on-sync=0 # Purge old tasks on sync\n" - "\n" - "# Dates\n" - "dateformat=Y-M-D # Preferred input and display date format\n" - "dateformat.holiday=YMD # Preferred input date format for holidays\n" - "dateformat.edit=Y-M-D H:N:S # Preferred display date format when editing\n" - "dateformat.info=Y-M-D H:N:S # Preferred display date format for information\n" - "dateformat.report= # Preferred display date format for reports\n" - "dateformat.annotation= # Preferred display date format for annotations\n" - "date.iso=1 # Enable ISO date support\n" - "weekstart=sunday # Sunday or Monday only\n" - "displayweeknumber=1 # Show week numbers on calendar\n" - "due=7 # Task is considered due in 7 days\n" - "\n" - "# Calendar controls\n" - "calendar.legend=1 # Display the legend on calendar\n" - "calendar.details=sparse # Calendar shows information for tasks w/due dates: full, sparse or none\n" - "calendar.details.report=list # Report to use when showing task information in cal\n" - "calendar.offset=0 # Apply an offset value to control the first month of the calendar\n" - "calendar.offset.value=-1 # The number of months the first month of the calendar is moved\n" - "calendar.holidays=none # Show public holidays on calendar:full, sparse or none\n" - "#calendar.monthsperline=3 # Number of calendar months on a line\n" - "\n" - "# Journal controls\n" - "journal.time=0 # Record start/stop commands as annotation\n" - "journal.time.start.annotation=Started task # Annotation description for the start journal entry\n" - "journal.time.stop.annotation=Stopped task # Annotation description for the stop journal entry\n" - "journal.info=1 # Display task journal with info command\n" - "\n" - "# Dependency controls\n" - "dependency.reminder=1 # Nags on dependency chain violations\n" - "dependency.confirmation=1 # Should dependency chain repair be confirmed?\n" - "\n" - "# Urgency Coefficients\n" - "urgency.user.tag.next.coefficient=15.0 # Urgency coefficient for 'next' special tag\n" - "urgency.due.coefficient=12.0 # Urgency coefficient for due dates\n" - "urgency.blocking.coefficient=8.0 # Urgency coefficient for blocking tasks\n" - "urgency.active.coefficient=4.0 # Urgency coefficient for active tasks\n" - "urgency.scheduled.coefficient=5.0 # Urgency coefficient for scheduled tasks\n" - "urgency.age.coefficient=2.0 # Urgency coefficient for age\n" - "urgency.annotations.coefficient=1.0 # Urgency coefficient for annotations\n" - "urgency.tags.coefficient=1.0 # Urgency coefficient for tags\n" - "urgency.project.coefficient=1.0 # Urgency coefficient for projects\n" - "urgency.blocked.coefficient=-5.0 # Urgency coefficient for blocked tasks\n" - "urgency.waiting.coefficient=-3.0 # Urgency coefficient for waiting status\n" - "urgency.inherit=0 # Recursively inherit highest urgency value from blocked tasks\n" - "urgency.age.max=365 # Maximum age in days\n" - "\n" - "#urgency.user.project.foo.coefficient=5.0 # Urgency coefficients for 'foo' project\n" - "#urgency.user.tag.foo.coefficient=5.0 # Urgency coefficients for 'foo' tag\n" - "#urgency.uda.foo.coefficient=5.0 # Urgency coefficients for UDA 'foo'\n" - "\n" - "# Color controls.\n" - "color=1 # Enable color\n" - "\n" - "# Here is the rule precedence order, highest to lowest.\n" - "# Note that these are just the color rule names, without the leading 'color.'\n" - "# and any trailing '.value'.\n" - "rule.precedence.color=deleted,completed,active,keyword.,tag.,project.,overdue,scheduled,due.today,due,blocked,blocking,recurring,tagged,uda.\n" - "\n" - "# General decoration\n" - "rule.color.merge=1\n" - "color.label=\n" - "color.label.sort=\n" - "color.alternate=on gray2\n" - "color.header=color3\n" - "color.footnote=color3\n" - "color.warning=bold red\n" - "color.error=white on red\n" - "color.debug=color4\n" - "\n" - "# Task state\n" - "color.completed=\n" - "color.deleted=\n" - "color.active=rgb555 on rgb410\n" - "color.recurring=rgb013\n" - "color.scheduled=on rgb001\n" - "color.until=\n" - "color.blocked=white on color8\n" - "color.blocking=black on color15\n" - "\n" - "# Project\n" - "color.project.none=\n" - "\n" - "# Priority UDA\n" - "color.uda.priority.H=color255\n" - "color.uda.priority.L=color245\n" - "color.uda.priority.M=color250\n" - "\n" - "# Tags\n" - "color.tag.next=rgb440\n" - "color.tag.none=\n" - "color.tagged=rgb031\n" - "\n" - "# Due\n" - "color.due.today=rgb400\n" - "color.due=color1\n" - "color.overdue=color9\n" - "\n" - "# Report: burndown\n" - "color.burndown.done=on rgb010\n" - "color.burndown.pending=on color9\n" - "color.burndown.started=on color11\n" - "\n" - "# Report: history\n" - "color.history.add=color0 on rgb500\n" - "color.history.delete=color0 on rgb550\n" - "color.history.done=color0 on rgb050\n" - "\n" - "# Report: summary\n" - "color.summary.background=white on color0\n" - "color.summary.bar=black on rgb141\n" - "\n" - "# Command: calendar\n" - "color.calendar.due.today=color15 on color1\n" - "color.calendar.due=color0 on color1\n" - "color.calendar.holiday=color0 on color11\n" - "color.calendar.scheduled=rgb013 on color15\n" - "color.calendar.overdue=color0 on color9\n" - "color.calendar.today=color15 on rgb013\n" - "color.calendar.weekend=on color235\n" - "color.calendar.weeknumber=rgb013\n" - "\n" - "# Command: sync\n" - "color.sync.added=rgb010\n" - "color.sync.changed=color11\n" - "color.sync.rejected=color9\n" - "\n" - "# Command: undo\n" - "color.undo.after=color2\n" - "color.undo.before=color1\n" - "\n" - "# UDA priority\n" - "uda.priority.type=string # UDA priority is a string type\n" - "uda.priority.label=Priority # UDA priority has a display label'\n" - "uda.priority.values=H,M,L, # UDA priority values are 'H', 'M', 'L' or ''\n" - " # UDA priority sorting is 'H' > 'M' > 'L' > '' (highest to lowest)\n" - "#uda.priority.default=M # UDA priority default value of 'M'\n" - "urgency.uda.priority.H.coefficient=6.0 # UDA priority coefficient for value 'H'\n" - "urgency.uda.priority.M.coefficient=3.9 # UDA priority coefficient for value 'M'\n" - "urgency.uda.priority.L.coefficient=1.8 # UDA priority coefficient for value 'L'\n" - "\n" - "#default.project=foo # Default project for 'add' command\n" - "#default.due=eom # Default due date for 'add' command\n" - "#default.scheduled=eom # Default scheduled date for 'add' command\n" - "default.command=next # When no arguments are specified\n" - "default.timesheet.filter=( +PENDING and start.after:now-4wks ) or ( +COMPLETED and end.after:now-4wks )\n" - "\n" - "_forcecolor=0 # Forces color to be on, even for non TTY output\n" - "complete.all.tags=0 # Include old tag names in '_ags' command\n" - "list.all.projects=0 # Include old project names in 'projects' command\n" - "summary.all.projects=0 # Include old project names in 'summary' command\n" - "list.all.tags=0 # Include old tag names in 'tags' command\n" - "print.empty.columns=0 # Print columns which have no data for any task\n" - "debug=0 # Display diagnostics\n" - "sugar=1 # Syntactic sugar\n" - "obfuscate=0 # Obfuscate data for error reporting\n" - "fontunderline=1 # Uses underlines rather than -------\n" - "\n" - "# WARNING: Please read the documentation (man task-sync) before setting up\n" - "# Taskwarrior for Taskserver synchronization.\n" - "\n" - "#sync.encryption_secret # Encryption secret for sync to a server\n" - "#sync.server.client_id # Client ID for sync to a server\n" - "#sync.server.url # URL of the sync server\n" - "#sync.local.server_dir # Directory for local sync\n" - "#sync.gcp.credential_path # Path to JSON file containing credentials to authenticate GCP Sync\n" - "#sync.gcp.bucket # Bucket for sync to GCP\n" - "\n" - "# Aliases - alternate names for commands\n" - "alias.rm=delete # Alias for the delete command\n" - "alias.history=history.monthly # Prefer monthly over annual history reports\n" - "alias.ghistory=ghistory.monthly # Prefer monthly graphical over annual history reports\n" - "alias.burndown=burndown.weekly # Prefer the weekly burndown chart\n" - "\n" - "# Reports\n" - "\n" - "report.long.description=All details of tasks\n" - "report.long.labels=ID,A,Created,Mod,Deps,P,Project,Tags,Recur,Wait,Sched,Due,Until,Description\n" - "report.long.columns=id,start.active,entry,modified.age,depends,priority,project,tags,recur,wait.remaining,scheduled,due,until,description\n" - "report.long.filter=status:pending -WAITING\n" - "report.long.sort=modified-\n" - "report.long.context=1\n" - "\n" - "report.list.description=Most details of tasks\n" - "report.list.labels=ID,Active,Age,D,P,Project,Tags,R,Sch,Due,Until,Description,Urg\n" - "report.list.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due,until.remaining,description.count,urgency\n" - "report.list.filter=status:pending -WAITING\n" - "report.list.sort=start-,due+,project+,urgency-\n" - "report.list.context=1\n" - "\n" - "report.ls.description=Few details of tasks\n" - "report.ls.labels=ID,A,D,Project,Tags,R,Wait,S,Due,Until,Description\n" - "report.ls.columns=id,start.active,depends.indicator,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due.countdown,until.countdown,description.count\n" - "report.ls.filter=status:pending -WAITING\n" - "report.ls.sort=start-,description+\n" - "report.ls.context=1\n" - "\n" - "report.minimal.description=Minimal details of tasks\n" - "report.minimal.labels=ID,Project,Tags,Description\n" - "report.minimal.columns=id,project,tags.count,description.count\n" - "report.minimal.filter=status:pending -WAITING\n" - "report.minimal.sort=project+/,description+\n" - "report.minimal.context=1\n" - "\n" - "report.newest.description=Newest tasks\n" - "report.newest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n" - "report.newest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n" - "report.newest.filter=status:pending -WAITING\n" - "report.newest.sort=entry-\n" - "report.newest.context=1\n" - "\n" - "report.oldest.description=Oldest tasks\n" - "report.oldest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n" - "report.oldest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n" - "report.oldest.filter=status:pending -WAITING\n" - "report.oldest.sort=entry+\n" - "report.oldest.context=1\n" - "\n" - "report.overdue.description=Overdue tasks\n" - "report.overdue.labels=ID,Active,Age,Deps,P,Project,Tag,R,S,Due,Until,Description,Urg\n" - "report.overdue.columns=id,start.age,entry.age,depends,priority,project,tags,recur.indicator,scheduled.countdown,due,until,description,urgency\n" - "report.overdue.filter=status:pending -WAITING +OVERDUE\n" - "report.overdue.sort=urgency-,due+\n" - "report.overdue.context=1\n" - "\n" - "report.active.description=Active tasks\n" - "report.active.labels=ID,Started,Active,Age,D,P,Project,Tags,Recur,W,Sch,Due,Until,Description\n" - "report.active.columns=id,start,start.age,entry.age,depends.indicator,priority,project,tags,recur,wait,scheduled.remaining,due,until,description\n" - "report.active.filter=status:pending -WAITING +ACTIVE\n" - "report.active.sort=project+,start+\n" - "report.active.context=1\n" - "\n" - "report.completed.description=Completed tasks\n" - "report.completed.labels=ID,UUID,Created,Completed,Age,Deps,P,Project,Tags,R,Due,Description\n" - "report.completed.columns=id,uuid.short,entry,end,entry.age,depends,priority,project,tags,recur.indicator,due,description\n" - "report.completed.filter=status:completed -WAITING \n" - "report.completed.sort=end+\n" - "report.completed.context=1\n" - "\n" - "report.recurring.description=Recurring Tasks\n" - "report.recurring.labels=ID,Active,Age,D,P,Parent,Project,Tags,Recur,Sch,Due,Until,Description,Urg\n" - "report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,parent.short,project,tags,recur,scheduled.countdown,due,until.remaining,description,urgency\n" - "report.recurring.filter=(status:pending -WAITING +CHILD) or (status:recurring -WAITING +PARENT)\n" - "report.recurring.sort=due+,urgency-,entry+\n" - "report.recurring.context=1\n" - "\n" - "report.waiting.description=Waiting (hidden) tasks\n" - "report.waiting.labels=ID,A,Age,D,P,Project,Tags,R,Wait,Remaining,Sched,Due,Until,Description\n" - "report.waiting.columns=id,start.active,entry.age,depends.indicator,priority,project,tags,recur.indicator,wait,wait.remaining,scheduled,due,until,description\n" - "report.waiting.filter=+WAITING\n" - "report.waiting.sort=due+,wait+,entry+\n" - "report.waiting.context=1\n" - "\n" - "report.all.description=All tasks\n" - "report.all.labels=ID,St,UUID,A,Age,Done,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n" - "report.all.columns=id,status.short,uuid.short,start.active,entry.age,end.age,depends.indicator,priority,project.parent,tags.count,recur.indicator,wait.remaining,scheduled.remaining,due,until.remaining,description\n" - "report.all.sort=entry-\n" - "report.all.context=1\n" - "\n" - "report.next.description=Most urgent tasks\n" - "report.next.labels=ID,Active,Age,Deps,P,Project,Tag,Recur,S,Due,Until,Description,Urg\n" - "report.next.columns=id,start.age,entry.age,depends,priority,project,tags,recur,scheduled.countdown,due.relative,until.remaining,description,urgency\n" - "report.next.filter=status:pending -WAITING limit:page\n" - "report.next.sort=urgency-\n" - "report.next.context=1\n" - "\n" - "report.ready.description=Most urgent actionable tasks\n" - "report.ready.labels=ID,Active,Age,D,P,Project,Tags,R,S,Due,Until,Description,Urg\n" - "report.ready.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due.countdown,until.remaining,description,urgency\n" - "report.ready.filter=+READY\n" - "report.ready.sort=start-,urgency-\n" - "report.ready.context=1\n" - "\n" - "report.blocked.description=Blocked tasks\n" - "report.blocked.columns=id,depends,project,priority,due,start.active,entry.age,description\n" - "report.blocked.labels=ID,Deps,Proj,Pri,Due,Active,Age,Description\n" - "report.blocked.sort=due+,priority-,start-,project+\n" - "report.blocked.filter=status:pending -WAITING +BLOCKED\n" - "report.blocked.context=1\n" - "\n" - "report.unblocked.description=Unblocked tasks\n" - "report.unblocked.columns=id,depends,project,priority,due,start.active,entry.age,description\n" - "report.unblocked.labels=ID,Deps,Proj,Pri,Due,Active,Age,Description\n" - "report.unblocked.sort=due+,priority-,start-,project+\n" - "report.unblocked.filter=status:pending -WAITING -BLOCKED\n" - "report.unblocked.context=1\n" - "\n" - "report.blocking.description=Blocking tasks\n" - "report.blocking.labels=ID,UUID,A,Deps,Project,Tags,R,W,Sch,Due,Until,Description,Urg\n" - "report.blocking.columns=id,uuid.short,start.active,depends,project,tags,recur,wait,scheduled.remaining,due.relative,until.remaining,description.count,urgency\n" - "report.blocking.sort=urgency-,due+,entry+\n" - "report.blocking.filter=status:pending -WAITING +BLOCKING\n" - "report.blocking.context=1\n" - "\n" - "report.timesheet.filter=(+PENDING -WAITING start.after:now-4wks) or (+COMPLETED -WAITING end.after:now-4wks)\n" - "report.timesheet.context=0\n" - "\n"; + "# Taskwarrior program configuration file.\n" + "# For more documentation, see https://taskwarrior.org or try 'man task', 'man task-color',\n" + "# 'man task-sync' or 'man taskrc'\n" + "\n" + "# Here is an example of entries that use the default, override and blank values\n" + "# variable=foo -- By specifying a value, this overrides the default\n" + "# variable= -- By specifying no value, this means no default\n" + "# #variable=foo -- By commenting out the line, or deleting it, this uses the default\n" + "\n" + "# You can also refence environment variables:\n" + "# variable=$HOME/task\n" + "# variable=$VALUE\n" + "\n" + "# Use the command 'task show' to see all defaults and overrides\n" + "\n" + "# Files\n" + "data.location=~/.task\n" + "gc=1 # Garbage-collect data files - DO NOT CHANGE " + "unless you are sure\n" + "exit.on.missing.db=0 # Whether to exit if ~/.task is not found\n" + "hooks=1 # Master control switch for hooks\n" + "\n" + "# Terminal\n" + "detection=1 # Detects terminal width\n" + "defaultwidth=80 # Without detection, assumed width\n" + "defaultheight=24 # Without detection, assumed height\n" + "avoidlastcolumn=0 # Fixes Cygwin width problem\n" + "hyphenate=1 # Hyphenates lines wrapped on non-word-breaks\n" + "#editor=vi # Preferred text editor\n" + "reserved.lines=1 # Assume a 1-line prompt\n" + "\n" + "# Miscellaneous\n" + "# verbose= # Comma-separated list. May contain any " + "subset of:\n" + "# " + "affected,blank,context,default,edit,filter,footnote,header,label,new-id,new-uuid,news," + "override,project,recur,special,sync\n" + "verbose=affected,blank,context,edit,header,footnote,label,new-id,news,project,special,sync," + "override,recur\n" + "confirmation=1 # Confirmation on delete, big changes\n" + "recurrence=1 # Enable recurrence\n" + "recurrence.confirmation=prompt # Confirmation for propagating changes among " + "recurring tasks (yes/no/prompt)\n" + "allow.empty.filter=1 # An empty filter gets a warning and requires " + "confirmation\n" + "indent.annotation=2 # Indent spaces for annotations\n" + "indent.report=0 # Indent spaces for whole report\n" + "row.padding=0 # Left and right padding for each row of " + "report\n" + "column.padding=1 # Spaces between each column in a report\n" + "bulk=3 # 3 or more tasks considered a bulk change and " + "is confirmed\n" + "nag=You have more urgent tasks. # Nag message to keep you honest\n" // TODO + "search.case.sensitive=1 # Setting to no allows case insensitive " + "searches\n" + "active.indicator=* # What to show as an active task indicator\n" + "tag.indicator=+ # What to show as a tag indicator\n" + "dependency.indicator=D # What to show as a dependency indicator\n" + "recurrence.indicator=R # What to show as a task recurrence indicator\n" + "recurrence.limit=1 # Number of future recurring pending tasks\n" + "undo.style=side # Undo style - can be 'side', or 'diff'\n" + "regex=1 # Assume all search/filter strings are " + "regexes\n" + "xterm.title=0 # Sets xterm title for some commands\n" + "expressions=infix # Prefer infix over postfix expressions\n" + "json.array=1 # Enclose JSON output in [ ]\n" + "abbreviation.minimum=2 # Shortest allowed abbreviation\n" + "news.version= # Latest version highlights read by the user\n" + "purge.on-sync=0 # Purge old tasks on sync\n" + "\n" + "# Dates\n" + "dateformat=Y-M-D # Preferred input and display date format\n" + "dateformat.holiday=YMD # Preferred input date format for holidays\n" + "dateformat.edit=Y-M-D H:N:S # Preferred display date format when editing\n" + "dateformat.info=Y-M-D H:N:S # Preferred display date format for " + "information\n" + "dateformat.report= # Preferred display date format for reports\n" + "dateformat.annotation= # Preferred display date format for " + "annotations\n" + "date.iso=1 # Enable ISO date support\n" + "weekstart=sunday # Sunday or Monday only\n" + "displayweeknumber=1 # Show week numbers on calendar\n" + "due=7 # Task is considered due in 7 days\n" + "\n" + "# Calendar controls\n" + "calendar.legend=1 # Display the legend on calendar\n" + "calendar.details=sparse # Calendar shows information for tasks w/due " + "dates: full, sparse or none\n" + "calendar.details.report=list # Report to use when showing task information " + "in cal\n" + "calendar.offset=0 # Apply an offset value to control the first " + "month of the calendar\n" + "calendar.offset.value=-1 # The number of months the first month of the " + "calendar is moved\n" + "calendar.holidays=none # Show public holidays on calendar:full, " + "sparse or none\n" + "#calendar.monthsperline=3 # Number of calendar months on a line\n" + "\n" + "# Journal controls\n" + "journal.time=0 # Record start/stop commands as annotation\n" + "journal.time.start.annotation=Started task # Annotation description for the start journal " + "entry\n" + "journal.time.stop.annotation=Stopped task # Annotation description for the stop journal " + "entry\n" + "journal.info=1 # Display task journal with info command\n" + "\n" + "# Dependency controls\n" + "dependency.reminder=1 # Nags on dependency chain violations\n" + "dependency.confirmation=1 # Should dependency chain repair be " + "confirmed?\n" + "\n" + "# Urgency Coefficients\n" + "urgency.user.tag.next.coefficient=15.0 # Urgency coefficient for 'next' special tag\n" + "urgency.due.coefficient=12.0 # Urgency coefficient for due dates\n" + "urgency.blocking.coefficient=8.0 # Urgency coefficient for blocking tasks\n" + "urgency.active.coefficient=4.0 # Urgency coefficient for active tasks\n" + "urgency.scheduled.coefficient=5.0 # Urgency coefficient for scheduled tasks\n" + "urgency.age.coefficient=2.0 # Urgency coefficient for age\n" + "urgency.annotations.coefficient=1.0 # Urgency coefficient for annotations\n" + "urgency.tags.coefficient=1.0 # Urgency coefficient for tags\n" + "urgency.project.coefficient=1.0 # Urgency coefficient for projects\n" + "urgency.blocked.coefficient=-5.0 # Urgency coefficient for blocked tasks\n" + "urgency.waiting.coefficient=-3.0 # Urgency coefficient for waiting status\n" + "urgency.inherit=0 # Recursively inherit highest urgency value " + "from blocked tasks\n" + "urgency.age.max=365 # Maximum age in days\n" + "\n" + "#urgency.user.project.foo.coefficient=5.0 # Urgency coefficients for 'foo' project\n" + "#urgency.user.tag.foo.coefficient=5.0 # Urgency coefficients for 'foo' tag\n" + "#urgency.uda.foo.coefficient=5.0 # Urgency coefficients for UDA 'foo'\n" + "\n" + "# Color controls.\n" + "color=1 # Enable color\n" + "\n" + "# Here is the rule precedence order, highest to lowest.\n" + "# Note that these are just the color rule names, without the leading 'color.'\n" + "# and any trailing '.value'.\n" + "rule.precedence.color=deleted,completed,active,keyword.,tag.,project.,overdue,scheduled,due." + "today,due,blocked,blocking,recurring,tagged,uda.\n" + "\n" + "# General decoration\n" + "rule.color.merge=1\n" + "color.label=\n" + "color.label.sort=\n" + "color.alternate=on gray2\n" + "color.header=color3\n" + "color.footnote=color3\n" + "color.warning=bold red\n" + "color.error=white on red\n" + "color.debug=color4\n" + "\n" + "# Task state\n" + "color.completed=\n" + "color.deleted=\n" + "color.active=rgb555 on rgb410\n" + "color.recurring=rgb013\n" + "color.scheduled=on rgb001\n" + "color.until=\n" + "color.blocked=white on color8\n" + "color.blocking=black on color15\n" + "\n" + "# Project\n" + "color.project.none=\n" + "\n" + "# Priority UDA\n" + "color.uda.priority.H=color255\n" + "color.uda.priority.L=color245\n" + "color.uda.priority.M=color250\n" + "\n" + "# Tags\n" + "color.tag.next=rgb440\n" + "color.tag.none=\n" + "color.tagged=rgb031\n" + "\n" + "# Due\n" + "color.due.today=rgb400\n" + "color.due=color1\n" + "color.overdue=color9\n" + "\n" + "# Report: burndown\n" + "color.burndown.done=on rgb010\n" + "color.burndown.pending=on color9\n" + "color.burndown.started=on color11\n" + "\n" + "# Report: history\n" + "color.history.add=color0 on rgb500\n" + "color.history.delete=color0 on rgb550\n" + "color.history.done=color0 on rgb050\n" + "\n" + "# Report: summary\n" + "color.summary.background=white on color0\n" + "color.summary.bar=black on rgb141\n" + "\n" + "# Command: calendar\n" + "color.calendar.due.today=color15 on color1\n" + "color.calendar.due=color0 on color1\n" + "color.calendar.holiday=color0 on color11\n" + "color.calendar.scheduled=rgb013 on color15\n" + "color.calendar.overdue=color0 on color9\n" + "color.calendar.today=color15 on rgb013\n" + "color.calendar.weekend=on color235\n" + "color.calendar.weeknumber=rgb013\n" + "\n" + "# Command: sync\n" + "color.sync.added=rgb010\n" + "color.sync.changed=color11\n" + "color.sync.rejected=color9\n" + "\n" + "# Command: undo\n" + "color.undo.after=color2\n" + "color.undo.before=color1\n" + "\n" + "# UDA priority\n" + "uda.priority.type=string # UDA priority is a string type\n" + "uda.priority.label=Priority # UDA priority has a display label'\n" + "uda.priority.values=H,M,L, # UDA priority values are 'H', 'M', 'L' or ''\n" + " # UDA priority sorting is 'H' > 'M' > 'L' > '' " + "(highest to lowest)\n" + "#uda.priority.default=M # UDA priority default value of 'M'\n" + "urgency.uda.priority.H.coefficient=6.0 # UDA priority coefficient for value 'H'\n" + "urgency.uda.priority.M.coefficient=3.9 # UDA priority coefficient for value 'M'\n" + "urgency.uda.priority.L.coefficient=1.8 # UDA priority coefficient for value 'L'\n" + "\n" + "#default.project=foo # Default project for 'add' command\n" + "#default.due=eom # Default due date for 'add' command\n" + "#default.scheduled=eom # Default scheduled date for 'add' command\n" + "default.command=next # When no arguments are specified\n" + "default.timesheet.filter=( +PENDING and start.after:now-4wks ) or ( +COMPLETED and " + "end.after:now-4wks )\n" + "\n" + "_forcecolor=0 # Forces color to be on, even for non TTY " + "output\n" + "complete.all.tags=0 # Include old tag names in '_ags' command\n" + "list.all.projects=0 # Include old project names in 'projects' " + "command\n" + "summary.all.projects=0 # Include old project names in 'summary' " + "command\n" + "list.all.tags=0 # Include old tag names in 'tags' command\n" + "print.empty.columns=0 # Print columns which have no data for any " + "task\n" + "debug=0 # Display diagnostics\n" + "sugar=1 # Syntactic sugar\n" + "obfuscate=0 # Obfuscate data for error reporting\n" + "fontunderline=1 # Uses underlines rather than -------\n" + "\n" + "# WARNING: Please read the documentation (man task-sync) before setting up\n" + "# Taskwarrior for Taskserver synchronization.\n" + "\n" + "#sync.encryption_secret # Encryption secret for sync to a server\n" + "#sync.server.client_id # Client ID for sync to a server\n" + "#sync.server.url # URL of the sync server\n" + "#sync.local.server_dir # Directory for local sync\n" + "#sync.gcp.credential_path # Path to JSON file containing credentials to " + "authenticate GCP Sync\n" + "#sync.gcp.bucket # Bucket for sync to GCP\n" + "\n" + "# Aliases - alternate names for commands\n" + "alias.rm=delete # Alias for the delete command\n" + "alias.history=history.monthly # Prefer monthly over annual history reports\n" + "alias.ghistory=ghistory.monthly # Prefer monthly graphical over annual history " + "reports\n" + "alias.burndown=burndown.weekly # Prefer the weekly burndown chart\n" + "\n" + "# Reports\n" + "\n" + "report.long.description=All details of tasks\n" + "report.long.labels=ID,A,Created,Mod,Deps,P,Project,Tags,Recur,Wait,Sched,Due,Until," + "Description\n" + "report.long.columns=id,start.active,entry,modified.age,depends,priority,project,tags,recur," + "wait.remaining,scheduled,due,until,description\n" + "report.long.filter=status:pending -WAITING\n" + "report.long.sort=modified-\n" + "report.long.context=1\n" + "\n" + "report.list.description=Most details of tasks\n" + "report.list.labels=ID,Active,Age,D,P,Project,Tags,R,Sch,Due,Until,Description,Urg\n" + "report.list.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur." + "indicator,scheduled.countdown,due,until.remaining,description.count,urgency\n" + "report.list.filter=status:pending -WAITING\n" + "report.list.sort=start-,due+,project+,urgency-\n" + "report.list.context=1\n" + "\n" + "report.ls.description=Few details of tasks\n" + "report.ls.labels=ID,A,D,Project,Tags,R,Wait,S,Due,Until,Description\n" + "report.ls.columns=id,start.active,depends.indicator,project,tags,recur.indicator,wait." + "remaining,scheduled.countdown,due.countdown,until.countdown,description.count\n" + "report.ls.filter=status:pending -WAITING\n" + "report.ls.sort=start-,description+\n" + "report.ls.context=1\n" + "\n" + "report.minimal.description=Minimal details of tasks\n" + "report.minimal.labels=ID,Project,Tags,Description\n" + "report.minimal.columns=id,project,tags.count,description.count\n" + "report.minimal.filter=status:pending -WAITING\n" + "report.minimal.sort=project+/,description+\n" + "report.minimal.context=1\n" + "\n" + "report.newest.description=Newest tasks\n" + "report.newest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until," + "Description\n" + "report.newest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority," + "project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n" + "report.newest.filter=status:pending -WAITING\n" + "report.newest.sort=entry-\n" + "report.newest.context=1\n" + "\n" + "report.oldest.description=Oldest tasks\n" + "report.oldest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until," + "Description\n" + "report.oldest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority," + "project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n" + "report.oldest.filter=status:pending -WAITING\n" + "report.oldest.sort=entry+\n" + "report.oldest.context=1\n" + "\n" + "report.overdue.description=Overdue tasks\n" + "report.overdue.labels=ID,Active,Age,Deps,P,Project,Tag,R,S,Due,Until,Description,Urg\n" + "report.overdue.columns=id,start.age,entry.age,depends,priority,project,tags,recur.indicator," + "scheduled.countdown,due,until,description,urgency\n" + "report.overdue.filter=status:pending -WAITING +OVERDUE\n" + "report.overdue.sort=urgency-,due+\n" + "report.overdue.context=1\n" + "\n" + "report.active.description=Active tasks\n" + "report.active.labels=ID,Started,Active,Age,D,P,Project,Tags,Recur,W,Sch,Due,Until," + "Description\n" + "report.active.columns=id,start,start.age,entry.age,depends.indicator,priority,project,tags," + "recur,wait,scheduled.remaining,due,until,description\n" + "report.active.filter=status:pending -WAITING +ACTIVE\n" + "report.active.sort=project+,start+\n" + "report.active.context=1\n" + "\n" + "report.completed.description=Completed tasks\n" + "report.completed.labels=ID,UUID,Created,Completed,Age,Deps,P,Project,Tags,R,Due,Description\n" + "report.completed.columns=id,uuid.short,entry,end,entry.age,depends,priority,project,tags," + "recur.indicator,due,description\n" + "report.completed.filter=status:completed -WAITING \n" + "report.completed.sort=end+\n" + "report.completed.context=1\n" + "\n" + "report.recurring.description=Recurring Tasks\n" + "report.recurring.labels=ID,Active,Age,D,P,Parent,Project,Tags,Recur,Sch,Due,Until,Description," + "Urg\n" + "report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,parent.short," + "project,tags,recur,scheduled.countdown,due,until.remaining,description,urgency\n" + "report.recurring.filter=(status:pending -WAITING +CHILD) or (status:recurring -WAITING " + "+PARENT)\n" + "report.recurring.sort=due+,urgency-,entry+\n" + "report.recurring.context=1\n" + "\n" + "report.waiting.description=Waiting (hidden) tasks\n" + "report.waiting.labels=ID,A,Age,D,P,Project,Tags,R,Wait,Remaining,Sched,Due,Until,Description\n" + "report.waiting.columns=id,start.active,entry.age,depends.indicator,priority,project,tags," + "recur.indicator,wait,wait.remaining,scheduled,due,until,description\n" + "report.waiting.filter=+WAITING\n" + "report.waiting.sort=due+,wait+,entry+\n" + "report.waiting.context=1\n" + "\n" + "report.all.description=All tasks\n" + "report.all.labels=ID,St,UUID,A,Age,Done,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n" + "report.all.columns=id,status.short,uuid.short,start.active,entry.age,end.age,depends." + "indicator,priority,project.parent,tags.count,recur.indicator,wait.remaining,scheduled." + "remaining,due,until.remaining,description\n" + "report.all.sort=entry-\n" + "report.all.context=1\n" + "\n" + "report.next.description=Most urgent tasks\n" + "report.next.labels=ID,Active,Age,Deps,P,Project,Tag,Recur,S,Due,Until,Description,Urg\n" + "report.next.columns=id,start.age,entry.age,depends,priority,project,tags,recur,scheduled." + "countdown,due.relative,until.remaining,description,urgency\n" + "report.next.filter=status:pending -WAITING limit:page\n" + "report.next.sort=urgency-\n" + "report.next.context=1\n" + "\n" + "report.ready.description=Most urgent actionable tasks\n" + "report.ready.labels=ID,Active,Age,D,P,Project,Tags,R,S,Due,Until,Description,Urg\n" + "report.ready.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur." + "indicator,scheduled.countdown,due.countdown,until.remaining,description,urgency\n" + "report.ready.filter=+READY\n" + "report.ready.sort=start-,urgency-\n" + "report.ready.context=1\n" + "\n" + "report.blocked.description=Blocked tasks\n" + "report.blocked.columns=id,depends,project,priority,due,start.active,entry.age,description\n" + "report.blocked.labels=ID,Deps,Proj,Pri,Due,Active,Age,Description\n" + "report.blocked.sort=due+,priority-,start-,project+\n" + "report.blocked.filter=status:pending -WAITING +BLOCKED\n" + "report.blocked.context=1\n" + "\n" + "report.unblocked.description=Unblocked tasks\n" + "report.unblocked.columns=id,depends,project,priority,due,start.active,entry.age,description\n" + "report.unblocked.labels=ID,Deps,Proj,Pri,Due,Active,Age,Description\n" + "report.unblocked.sort=due+,priority-,start-,project+\n" + "report.unblocked.filter=status:pending -WAITING -BLOCKED\n" + "report.unblocked.context=1\n" + "\n" + "report.blocking.description=Blocking tasks\n" + "report.blocking.labels=ID,UUID,A,Deps,Project,Tags,R,W,Sch,Due,Until,Description,Urg\n" + "report.blocking.columns=id,uuid.short,start.active,depends,project,tags,recur,wait,scheduled." + "remaining,due.relative,until.remaining,description.count,urgency\n" + "report.blocking.sort=urgency-,due+,entry+\n" + "report.blocking.filter=status:pending -WAITING +BLOCKING\n" + "report.blocking.context=1\n" + "\n" + "report.timesheet.filter=(+PENDING -WAITING start.after:now-4wks) or (+COMPLETED -WAITING " + "end.after:now-4wks)\n" + "report.timesheet.context=0\n" + "\n"; // Supported modifiers, synonyms on the same line. -static const char* modifierNames[] = -{ - "before", "under", "below", - "after", "over", "above", - "by", - "none", - "any", - "is", "equals", - "isnt", "not", - "has", "contains", - "hasnt", - "startswith", "left", - "endswith", "right", - "word", - "noword" -}; +static const char* modifierNames[] = { + "before", "under", "below", "after", "over", "above", "by", "none", + "any", "is", "equals", "isnt", "not", "has", "contains", "hasnt", + "startswith", "left", "endswith", "right", "word", "noword"}; Context* Context::context; //////////////////////////////////////////////////////////////////////////////// -Context& Context::getContext () -{ - assert (Context::context); +Context& Context::getContext() { + assert(Context::context); return *Context::context; } //////////////////////////////////////////////////////////////////////////////// -void Context::setContext (Context* context) -{ - Context::context = context; +void Context::setContext(Context* context) { Context::context = context; } + +//////////////////////////////////////////////////////////////////////////////// +Context::~Context() { + for (auto& com : commands) delete com.second; + + for (auto& col : columns) delete col.second; } //////////////////////////////////////////////////////////////////////////////// -Context::~Context () -{ - for (auto& com : commands) - delete com.second; - - for (auto& col : columns) - delete col.second; -} - -//////////////////////////////////////////////////////////////////////////////// -int Context::initialize (int argc, const char** argv) -{ - timer_total.start (); +int Context::initialize(int argc, const char** argv) { + timer_total.start(); int rc = 0; - home_dir = getenv ("HOME"); + home_dir = getenv("HOME"); - std::vector searchPaths { TASK_RCDIR }; + std::vector searchPaths{TASK_RCDIR}; - try - { + try { //////////////////////////////////////////////////////////////////////////// // // [1] Load the correct config file. @@ -489,51 +523,46 @@ int Context::initialize (int argc, const char** argv) bool taskrc_overridden = false; // XDG_CONFIG_HOME doesn't count as an override (no warning header) - if (! rc_file.exists ()) - { + if (!rc_file.exists()) { // Use XDG_CONFIG_HOME if defined, otherwise default to ~/.config std::string xdg_config_home; - const char* env_xdg_config_home = getenv ("XDG_CONFIG_HOME"); + const char* env_xdg_config_home = getenv("XDG_CONFIG_HOME"); if (env_xdg_config_home) - xdg_config_home = format ("{1}", env_xdg_config_home); + xdg_config_home = format("{1}", env_xdg_config_home); else - xdg_config_home = format ("{1}/.config", home_dir); + xdg_config_home = format("{1}/.config", home_dir); // Ensure the path does not end with '/' - if (xdg_config_home.back () == '/') - xdg_config_home.pop_back(); + if (xdg_config_home.back() == '/') xdg_config_home.pop_back(); // https://github.com/GothenburgBitFactory/libshared/issues/32 - std::string rcfile_path = format ("{1}/task/taskrc", xdg_config_home); + std::string rcfile_path = format("{1}/task/taskrc", xdg_config_home); - File maybe_rc_file = File (rcfile_path); - if ( maybe_rc_file.exists ()) - rc_file = maybe_rc_file; + File maybe_rc_file = File(rcfile_path); + if (maybe_rc_file.exists()) rc_file = maybe_rc_file; } - char *override = getenv ("TASKRC"); - if (override) - { - rc_file = File (override); + char* override = getenv("TASKRC"); + if (override) { + rc_file = File(override); taskrc_overridden = true; } - taskrc_overridden = - CLI2::getOverride (argc, argv, rc_file) || taskrc_overridden; + taskrc_overridden = CLI2::getOverride(argc, argv, rc_file) || taskrc_overridden; // Artificial scope for timing purposes. { Timer timer; - config.parse (configurationDefaults, 1, searchPaths); - config.load (rc_file._data, 1, searchPaths); - debugTiming (format ("Config::load ({1})", rc_file._data), timer); + config.parse(configurationDefaults, 1, searchPaths); + config.load(rc_file._data, 1, searchPaths); + debugTiming(format("Config::load ({1})", rc_file._data), timer); } - CLI2::applyOverrides (argc, argv); + CLI2::applyOverrides(argc, argv); - if (taskrc_overridden && verbose ("override")) - header (format ("TASKRC override: {1}", rc_file._data)); + if (taskrc_overridden && verbose("override")) + header(format("TASKRC override: {1}", rc_file._data)); //////////////////////////////////////////////////////////////////////////// // @@ -548,24 +577,22 @@ int Context::initialize (int argc, const char** argv) bool taskdata_overridden = false; - override = getenv ("TASKDATA"); - if (override) - { - data_dir = Directory (override); - config.set ("data.location", data_dir._data); + override = getenv("TASKDATA"); + if (override) { + data_dir = Directory(override); + config.set("data.location", data_dir._data); taskdata_overridden = true; } - taskdata_overridden = - CLI2::getDataLocation (argc, argv, data_dir) || taskdata_overridden; + taskdata_overridden = CLI2::getDataLocation(argc, argv, data_dir) || taskdata_overridden; - if (taskdata_overridden && verbose ("override")) - header (format ("TASKDATA override: {1}", data_dir._data)); + if (taskdata_overridden && verbose("override")) + header(format("TASKDATA override: {1}", data_dir._data)); - createDefaultConfig (); + createDefaultConfig(); - bool create_if_missing = !config.getBoolean ("exit.on.missing.db"); - tdb2.open_replica (data_dir, create_if_missing); + bool create_if_missing = !config.getBoolean("exit.on.missing.db"); + tdb2.open_replica(data_dir, create_if_missing); //////////////////////////////////////////////////////////////////////////// // @@ -573,9 +600,8 @@ int Context::initialize (int argc, const char** argv) // //////////////////////////////////////////////////////////////////////////// - Command::factory (commands); - for (auto& cmd : commands) - cli2.entity ("cmd", cmd.first); + Command::factory(commands); + for (auto& cmd : commands) cli2.entity("cmd", cmd.first); //////////////////////////////////////////////////////////////////////////// // @@ -583,11 +609,10 @@ int Context::initialize (int argc, const char** argv) // //////////////////////////////////////////////////////////////////////////// - Column::factory (columns); - for (auto& col : columns) - cli2.entity ("attribute", col.first); + Column::factory(columns); + for (auto& col : columns) cli2.entity("attribute", col.first); - cli2.entity ("pseudo", "limit"); + cli2.entity("pseudo", "limit"); //////////////////////////////////////////////////////////////////////////// // @@ -595,14 +620,11 @@ int Context::initialize (int argc, const char** argv) // //////////////////////////////////////////////////////////////////////////// - for (auto& modifierName : modifierNames) - cli2.entity ("modifier", modifierName); + for (auto& modifierName : modifierNames) cli2.entity("modifier", modifierName); - for (auto& op : Eval::getOperators ()) - cli2.entity ("operator", op); + for (auto& op : Eval::getOperators()) cli2.entity("operator", op); - for (auto& op : Eval::getBinaryOperators ()) - cli2.entity ("binary_operator", op); + for (auto& op : Eval::getBinaryOperators()) cli2.entity("binary_operator", op); //////////////////////////////////////////////////////////////////////////// // @@ -610,10 +632,10 @@ int Context::initialize (int argc, const char** argv) // //////////////////////////////////////////////////////////////////////////// - initializeColorRules (); - staticInitialization (); - propagateDebug (); - loadAliases (); + initializeColorRules(); + staticInitialization(); + propagateDebug(); + loadAliases(); //////////////////////////////////////////////////////////////////////////// // @@ -621,35 +643,28 @@ int Context::initialize (int argc, const char** argv) // //////////////////////////////////////////////////////////////////////////// - for (int i = 0; i < argc; i++) - cli2.add (argv[i]); + for (int i = 0; i < argc; i++) cli2.add(argv[i]); - cli2.analyze (); + cli2.analyze(); // Extract a recomposed command line. auto foundDefault = false; auto foundAssumed = false; std::string combined; - for (auto& a : cli2._args) - { - if (combined.length ()) - combined += ' '; + for (auto& a : cli2._args) { + if (combined.length()) combined += ' '; - combined += a.attribute ("raw"); + combined += a.attribute("raw"); - if (a.hasTag ("DEFAULT")) - foundDefault = true; + if (a.hasTag("DEFAULT")) foundDefault = true; - if (a.hasTag ("ASSUMED")) - foundAssumed = true; + if (a.hasTag("ASSUMED")) foundAssumed = true; } - if (verbose ("default")) { - if (foundDefault) - header ("[" + combined + "]"); + if (verbose("default")) { + if (foundDefault) header("[" + combined + "]"); - if (foundAssumed) - header ("No command specified - assuming 'information'."); + if (foundAssumed) header("No command specified - assuming 'information'."); } //////////////////////////////////////////////////////////////////////////// @@ -658,60 +673,51 @@ int Context::initialize (int argc, const char** argv) // //////////////////////////////////////////////////////////////////////////// - hooks.initialize (); + hooks.initialize(); } - catch (const std::string& message) - { - error (message); + catch (const std::string& message) { + error(message); rc = 2; } - catch (int) - { + catch (int) { // Hooks can terminate processing by throwing integers. rc = 4; } - catch (const std::regex_error& e) - { + catch (const std::regex_error& e) { std::cout << "regex_error caught: " << e.what() << '\n'; - } - catch (...) - { - error ("knknown error. Please report."); + } catch (...) { + error("knknown error. Please report."); rc = 3; } // On initialization failure... - if (rc) - { + if (rc) { // Dump all debug messages, controlled by rc.debug. - if (config.getBoolean ("debug")) - { + if (config.getBoolean("debug")) { for (auto& d : debugMessages) - if (color ()) - std::cerr << colorizeDebug (d) << '\n'; + if (color()) + std::cerr << colorizeDebug(d) << '\n'; else std::cerr << d << '\n'; } // Dump all headers, controlled by 'header' verbosity token. - if (verbose ("header")) - { + if (verbose("header")) { for (auto& h : headers) - if (color ()) - std::cerr << colorizeHeader (h) << '\n'; + if (color()) + std::cerr << colorizeHeader(h) << '\n'; else std::cerr << h << '\n'; } // Dump all footnotes, controlled by 'footnote' verbosity token. - if (verbose ("footnote")) - { + if (verbose("footnote")) { for (auto& f : footnotes) - if (color ()) - std::cerr << colorizeFootnote (f) << '\n'; + if (color()) + std::cerr << colorizeFootnote(f) << '\n'; else std::cerr << f << '\n'; } @@ -719,98 +725,77 @@ int Context::initialize (int argc, const char** argv) // Dump all errors, non-maskable. // Colorized as footnotes. for (auto& e : errors) - if (color ()) - std::cerr << colorizeFootnote (e) << '\n'; + if (color()) + std::cerr << colorizeFootnote(e) << '\n'; else std::cerr << e << '\n'; } - time_init_us += timer_total.total_us (); + time_init_us += timer_total.total_us(); return rc; } //////////////////////////////////////////////////////////////////////////////// -int Context::run () -{ +int Context::run() { int rc; std::string output; - try - { - hooks.onLaunch (); - rc = dispatch (output); - hooks.onExit (); // No chance to update data. + try { + hooks.onLaunch(); + rc = dispatch(output); + hooks.onExit(); // No chance to update data. - timer_total.stop (); - time_total_us += timer_total.total_us (); + timer_total.stop(); + time_total_us += timer_total.total_us(); std::stringstream s; - s << "Perf " - << PACKAGE_STRING - << ' ' + s << "Perf " << PACKAGE_STRING << ' ' #ifdef HAVE_COMMIT << COMMIT #else << '-' #endif - << ' ' - << Datetime ().toISO () + << ' ' << Datetime().toISO() - << " init:" << time_init_us - << " load:" << time_load_us - << " gc:" << (time_gc_us > 0 ? time_gc_us - time_load_us : time_gc_us) - << " filter:" << time_filter_us - << " commit:" << time_commit_us - << " sort:" << time_sort_us - << " render:" << time_render_us - << " hooks:" << time_hooks_us - << " other:" << time_total_us - - time_init_us - - time_gc_us - - time_filter_us - - time_commit_us - - time_sort_us - - time_render_us - - time_hooks_us - << " total:" << time_total_us - << '\n'; - debug (s.str ()); + << " init:" << time_init_us << " load:" << time_load_us + << " gc:" << (time_gc_us > 0 ? time_gc_us - time_load_us : time_gc_us) + << " filter:" << time_filter_us << " commit:" << time_commit_us << " sort:" << time_sort_us + << " render:" << time_render_us << " hooks:" << time_hooks_us << " other:" + << time_total_us - time_init_us - time_gc_us - time_filter_us - time_commit_us - + time_sort_us - time_render_us - time_hooks_us + << " total:" << time_total_us << '\n'; + debug(s.str()); } - catch (const std::string& message) - { - error (message); + catch (const std::string& message) { + error(message); rc = 2; } - catch (int) - { + catch (int) { // Hooks can terminate processing by throwing integers. rc = 4; } - catch (...) - { - error ("Unknown error. Please report."); + catch (...) { + error("Unknown error. Please report."); rc = 3; } // Dump all debug messages, controlled by rc.debug. - if (config.getBoolean ("debug")) - { + if (config.getBoolean("debug")) { for (auto& d : debugMessages) - if (color ()) - std::cerr << colorizeDebug (d) << '\n'; + if (color()) + std::cerr << colorizeDebug(d) << '\n'; else std::cerr << d << '\n'; } // Dump all headers, controlled by 'header' verbosity token. - if (verbose ("header")) - { + if (verbose("header")) { for (auto& h : headers) - if (color ()) - std::cerr << colorizeHeader (h) << '\n'; + if (color()) + std::cerr << colorizeHeader(h) << '\n'; else std::cerr << h << '\n'; } @@ -819,11 +804,10 @@ int Context::run () std::cout << output; // Dump all footnotes, controlled by 'footnote' verbosity token. - if (verbose ("footnote")) - { + if (verbose("footnote")) { for (auto& f : footnotes) - if (color ()) - std::cerr << colorizeFootnote (f) << '\n'; + if (color()) + std::cerr << colorizeFootnote(f) << '\n'; else std::cerr << f << '\n'; } @@ -831,8 +815,8 @@ int Context::run () // Dump all errors, non-maskable. // Colorized as footnotes. for (auto& e : errors) - if (color ()) - std::cerr << colorizeError (e) << '\n'; + if (color()) + std::cerr << colorizeError(e) << '\n'; else std::cerr << e << '\n'; @@ -841,64 +825,52 @@ int Context::run () //////////////////////////////////////////////////////////////////////////////// // Dispatch to the command found by the CLI parser. -int Context::dispatch (std::string &out) -{ +int Context::dispatch(std::string& out) { // Autocomplete args against keywords. - std::string command = cli2.getCommand (); - if (command != "") - { - updateXtermTitle (); - updateVerbosity (); + std::string command = cli2.getCommand(); + if (command != "") { + updateXtermTitle(); + updateVerbosity(); Command* c = commands[command]; - assert (c); + assert(c); // The command know whether they need a GC. - if (c->needs_gc ()) - { - tdb2.gc (); + if (c->needs_gc()) { + tdb2.gc(); } // This is something that is only needed for write commands with no other // filter processing. - if (c->accepts_modifications () && - ! c->accepts_filter ()) - { - cli2.prepareFilter (); + if (c->accepts_modifications() && !c->accepts_filter()) { + cli2.prepareFilter(); } // With rc.debug.parser == 2, there are more tree dumps than you might want, // but we need the rc.debug.parser == 1 case covered also, with the final // tree. - if (config.getBoolean ("debug") && - config.getInteger ("debug.parser") == 1) - debug (cli2.dump ("Parse Tree (before command-specifіc processing)")); + if (config.getBoolean("debug") && config.getInteger("debug.parser") == 1) + debug(cli2.dump("Parse Tree (before command-specifіc processing)")); - return c->execute (out); + return c->execute(out); } - assert (commands["help"]); - return commands["help"]->execute (out); + assert(commands["help"]); + return commands["help"]->execute(out); } //////////////////////////////////////////////////////////////////////////////// -int Context::getWidth () -{ +int Context::getWidth() { // Determine window size. - auto width = config.getInteger ("defaultwidth"); + auto width = config.getInteger("defaultwidth"); // A zero width value means 'infinity', which is approximated here by 2^16. - if (width == 0) - return 65536; + if (width == 0) return 65536; - if (config.getBoolean ("detection")) - { - if (terminal_width == 0 && - terminal_height == 0) - { + if (config.getBoolean("detection")) { + if (terminal_width == 0 && terminal_height == 0) { unsigned short buff[4]; - if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &buff) != -1) - { + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &buff) != -1) { terminal_height = buff[0]; terminal_width = buff[1]; } @@ -909,31 +881,24 @@ int Context::getWidth () // Ncurses does this, and perhaps we need to as well, to avoid a problem on // Cygwin where the display goes right up to the terminal width, and causes // an odd color wrapping problem. - if (config.getBoolean ("avoidlastcolumn")) - --width; + if (config.getBoolean("avoidlastcolumn")) --width; } return width; } //////////////////////////////////////////////////////////////////////////////// -int Context::getHeight () -{ +int Context::getHeight() { // Determine window size. - auto height = config.getInteger ("defaultheight"); + auto height = config.getInteger("defaultheight"); // A zero height value means 'infinity', which is approximated here by 2^16. - if (height == 0) - return 65536; + if (height == 0) return 65536; - if (config.getBoolean ("detection")) - { - if (terminal_width == 0 && - terminal_height == 0) - { + if (config.getBoolean("detection")) { + if (terminal_width == 0 && terminal_height == 0) { unsigned short buff[4]; - if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &buff) != -1) - { + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &buff) != -1) { terminal_height = buff[0]; terminal_width = buff[1]; } @@ -946,63 +911,54 @@ int Context::getHeight () } //////////////////////////////////////////////////////////////////////////////// -std::string Context::getTaskContext (const std::string& kind, std::string name, bool fallback /* = true */) -{ +std::string Context::getTaskContext(const std::string& kind, std::string name, + bool fallback /* = true */) { // Consider currently selected context, if none specified - if (name.empty ()) - name = config.get ("context"); + if (name.empty()) name = config.get("context"); // Detect if any context is set, and bail out if not - if (! name.empty ()) - debug (format ("Applying context '{1}'", name)); - else - { - debug ("No context set"); + if (!name.empty()) + debug(format("Applying context '{1}'", name)); + else { + debug("No context set"); return ""; } // Figure out the context string for this kind (read/write) std::string contextString = ""; - if (! config.has ("context." + name + "." + kind) && kind == "read") - { - debug ("Specific " + kind + " context for '" + name + "' not defined. "); - if (fallback) - { - debug ("Trying to interpret old-style context definition as read context."); - contextString = config.get ("context." + name); + if (!config.has("context." + name + "." + kind) && kind == "read") { + debug("Specific " + kind + " context for '" + name + "' not defined. "); + if (fallback) { + debug("Trying to interpret old-style context definition as read context."); + contextString = config.get("context." + name); } - } - else - contextString = config.get ("context." + name + "." + kind); + } else + contextString = config.get("context." + name + "." + kind); - debug (format ("Detected context string: {1}", contextString.empty() ? "(empty)" : contextString)); + debug(format("Detected context string: {1}", contextString.empty() ? "(empty)" : contextString)); return contextString; } //////////////////////////////////////////////////////////////////////////////// -bool Context::color () -{ - if (determine_color_use) - { +bool Context::color() { + if (determine_color_use) { // What the config says. - use_color = config.getBoolean ("color"); + use_color = config.getBoolean("color"); // Only tty's support color. - if (! isatty (STDOUT_FILENO)) - { + if (!isatty(STDOUT_FILENO)) { // No ioctl. - config.set ("detection", "off"); - config.set ("color", "off"); + config.set("detection", "off"); + config.set("color", "off"); // Files don't get color. use_color = false; } // Override. - if (config.getBoolean ("_forcecolor")) - { - config.set ("color", "on"); + if (config.getBoolean("_forcecolor")) { + config.set("color", "on"); use_color = true; } @@ -1025,67 +981,55 @@ bool Context::color () // TODO This mechanism is clunky, and should slowly evolve into something more // logical and consistent. This should probably mean that 'nothing' should // take the place of '0'. -bool Context::verbose (const std::string& token) -{ - if (verbosity.empty ()) - { - verbosity_legacy = config.getBoolean ("verbose"); - for (auto& token : split (config.get ("verbose"), ',')) - verbosity.insert (token); +bool Context::verbose(const std::string& token) { + if (verbosity.empty()) { + verbosity_legacy = config.getBoolean("verbose"); + for (auto& token : split(config.get("verbose"), ',')) verbosity.insert(token); // Regular feedback means almost everything. // This odd test is to see if a Boolean-false value is a real one, which // means it is not 1/true/T/yes/on, but also should not be one of the // valid tokens either. - if (! verbosity_legacy && ! verbosity.empty ()) - { - std::string v = *(verbosity.begin ()); - if (v != "nothing" && - v != "affected" && // This list must be complete. - v != "blank" && // - v != "context" && // - v != "default" && // - v != "edit" && // - v != "filter" && // - v != "footnote" && // - v != "header" && // - v != "label" && // - v != "new-id" && // - v != "new-uuid" && // - v != "news" && // - v != "override" && // - v != "project" && // - v != "recur" && // - v != "special" && // - v != "sync") - { + if (!verbosity_legacy && !verbosity.empty()) { + std::string v = *(verbosity.begin()); + if (v != "nothing" && v != "affected" && // This list must be complete. + v != "blank" && // + v != "context" && // + v != "default" && // + v != "edit" && // + v != "filter" && // + v != "footnote" && // + v != "header" && // + v != "label" && // + v != "new-id" && // + v != "new-uuid" && // + v != "news" && // + v != "override" && // + v != "project" && // + v != "recur" && // + v != "special" && // + v != "sync") { // This list emulates rc.verbose=off in version 1.9.4. verbosity = {"blank", "label", "new-id", "edit"}; } } // Some flags imply "footnote" verbosity being active. Make it so. - if (! verbosity.count ("footnote")) - { + if (!verbosity.count("footnote")) { // TODO: Some of these may not use footnotes yet. They should. - for (auto flag : {"affected", "new-id", "new-uuid", "project", "override", "recur"}) - { - if (verbosity.count (flag)) - { - verbosity.insert ("footnote"); + for (auto flag : {"affected", "new-id", "new-uuid", "project", "override", "recur"}) { + if (verbosity.count(flag)) { + verbosity.insert("footnote"); break; } } } // Some flags imply "header" verbosity being active. Make it so. - if (! verbosity.count ("header")) - { - for (auto flag : {"default"}) - { - if (verbosity.count (flag)) - { - verbosity.insert ("header"); + if (!verbosity.count("header")) { + for (auto flag : {"default"}) { + if (verbosity.count(flag)) { + verbosity.insert("header"); break; } } @@ -1093,27 +1037,21 @@ bool Context::verbose (const std::string& token) } // rc.verbose=true|y|yes|1|on overrides all. - if (verbosity_legacy) - return true; + if (verbosity_legacy) return true; // rc.verbose=nothing overrides all. - if (verbosity.size () == 1 && - *(verbosity.begin ()) == "nothing") - return false; + if (verbosity.size() == 1 && *(verbosity.begin()) == "nothing") return false; // Specific token match. - if (verbosity.count (token)) - return true; + if (verbosity.count(token)) return true; return false; } //////////////////////////////////////////////////////////////////////////////// -const std::vector Context::getColumns () const -{ - std::vector output; - for (auto& col : columns) - output.push_back (col.first); +const std::vector Context::getColumns() const { + std::vector output; + for (auto& col : columns) output.push_back(col.first); return output; } @@ -1122,23 +1060,18 @@ const std::vector Context::getColumns () const // A value of zero mean unlimited. // A value of 'page' means however many screen lines there are. // A value of a positive integer is a row/task limit. -void Context::getLimits (int& rows, int& lines) -{ +void Context::getLimits(int& rows, int& lines) { rows = 0; lines = 0; // This is an integer specified as a filter (limit:10). - auto limit = config.get ("limit"); - if (limit != "") - { - if (limit == "page") - { + auto limit = config.get("limit"); + if (limit != "") { + if (limit == "page") { rows = 0; - lines = getHeight (); - } - else - { - rows = (int) strtol (limit.c_str (), nullptr, 10); + lines = getHeight(); + } else { + rows = (int)strtol(limit.c_str(), nullptr, 10); lines = 0; } } @@ -1147,85 +1080,76 @@ void Context::getLimits (int& rows, int& lines) //////////////////////////////////////////////////////////////////////////////// // The 'Task' object, among others, is shared between projects. To make this // easier, it has been decoupled from Context. -void Context::staticInitialization () -{ - CLI2::minimumMatchLength = config.getInteger ("abbreviation.minimum"); - Lexer::minimumMatchLength = config.getInteger ("abbreviation.minimum"); +void Context::staticInitialization() { + CLI2::minimumMatchLength = config.getInteger("abbreviation.minimum"); + Lexer::minimumMatchLength = config.getInteger("abbreviation.minimum"); - Task::defaultProject = config.get ("default.project"); - Task::defaultDue = config.get ("default.due"); - Task::defaultScheduled = config.get ("default.scheduled"); + Task::defaultProject = config.get("default.project"); + Task::defaultDue = config.get("default.due"); + Task::defaultScheduled = config.get("default.scheduled"); - Task::searchCaseSensitive = Variant::searchCaseSensitive = config.getBoolean ("search.case.sensitive"); - Task::regex = Variant::searchUsingRegex = config.getBoolean ("regex"); - Lexer::dateFormat = Variant::dateFormat = config.get ("dateformat"); + Task::searchCaseSensitive = Variant::searchCaseSensitive = + config.getBoolean("search.case.sensitive"); + Task::regex = Variant::searchUsingRegex = config.getBoolean("regex"); + Lexer::dateFormat = Variant::dateFormat = config.get("dateformat"); - Datetime::isoEnabled = config.getBoolean ("date.iso"); - Datetime::standaloneDateEnabled = false; - Datetime::standaloneTimeEnabled = false; + Datetime::isoEnabled = config.getBoolean("date.iso"); + Datetime::standaloneDateEnabled = false; + Datetime::standaloneTimeEnabled = false; Duration::standaloneSecondsEnabled = false; - TDB2::debug_mode = config.getBoolean ("debug"); + TDB2::debug_mode = config.getBoolean("debug"); - for (auto& rc : config) - { - if (rc.first.substr (0, 4) == "uda." && - rc.first.substr (rc.first.length () - 7, 7) == ".values") - { - std::string name = rc.first.substr (4, rc.first.length () - 7 - 4); - auto values = split (rc.second, ','); + for (auto& rc : config) { + if (rc.first.substr(0, 4) == "uda." && rc.first.substr(rc.first.length() - 7, 7) == ".values") { + std::string name = rc.first.substr(4, rc.first.length() - 7 - 4); + auto values = split(rc.second, ','); - for (auto r = values.rbegin(); r != values.rend (); ++r) - Task::customOrder[name].push_back (*r); + for (auto r = values.rbegin(); r != values.rend(); ++r) Task::customOrder[name].push_back(*r); } } - for (auto& col : columns) - { - Task::attributes[col.first] = col.second->type (); - Lexer::attributes[col.first] = col.second->type (); + for (auto& col : columns) { + Task::attributes[col.first] = col.second->type(); + Lexer::attributes[col.first] = col.second->type(); } - Task::urgencyProjectCoefficient = config.getReal ("urgency.project.coefficient"); - Task::urgencyActiveCoefficient = config.getReal ("urgency.active.coefficient"); - Task::urgencyScheduledCoefficient = config.getReal ("urgency.scheduled.coefficient"); - Task::urgencyWaitingCoefficient = config.getReal ("urgency.waiting.coefficient"); - Task::urgencyBlockedCoefficient = config.getReal ("urgency.blocked.coefficient"); - Task::urgencyAnnotationsCoefficient = config.getReal ("urgency.annotations.coefficient"); - Task::urgencyTagsCoefficient = config.getReal ("urgency.tags.coefficient"); - Task::urgencyDueCoefficient = config.getReal ("urgency.due.coefficient"); - Task::urgencyBlockingCoefficient = config.getReal ("urgency.blocking.coefficient"); - Task::urgencyAgeCoefficient = config.getReal ("urgency.age.coefficient"); - Task::urgencyAgeMax = config.getReal ("urgency.age.max"); + Task::urgencyProjectCoefficient = config.getReal("urgency.project.coefficient"); + Task::urgencyActiveCoefficient = config.getReal("urgency.active.coefficient"); + Task::urgencyScheduledCoefficient = config.getReal("urgency.scheduled.coefficient"); + Task::urgencyWaitingCoefficient = config.getReal("urgency.waiting.coefficient"); + Task::urgencyBlockedCoefficient = config.getReal("urgency.blocked.coefficient"); + Task::urgencyAnnotationsCoefficient = config.getReal("urgency.annotations.coefficient"); + Task::urgencyTagsCoefficient = config.getReal("urgency.tags.coefficient"); + Task::urgencyDueCoefficient = config.getReal("urgency.due.coefficient"); + Task::urgencyBlockingCoefficient = config.getReal("urgency.blocking.coefficient"); + Task::urgencyAgeCoefficient = config.getReal("urgency.age.coefficient"); + Task::urgencyAgeMax = config.getReal("urgency.age.max"); // Tag- and project-specific coefficients. - for (auto& var : config.all ()) - if (var.substr (0, 13) == "urgency.user." || - var.substr (0, 12) == "urgency.uda.") - Task::coefficients[var] = config.getReal (var); + for (auto& var : config.all()) + if (var.substr(0, 13) == "urgency.user." || var.substr(0, 12) == "urgency.uda.") + Task::coefficients[var] = config.getReal(var); } //////////////////////////////////////////////////////////////////////////////// -void Context::createDefaultConfig () -{ +void Context::createDefaultConfig() { // Do we need to create a default rc? - if (rc_file._data != "" && ! rc_file.exists ()) - { - if (config.getBoolean ("confirmation") && - ! confirm ( format ("A configuration file could not be found in {1}\n\nWould you like a sample {2} created, so Taskwarrior can proceed?", home_dir, rc_file._data))) - throw std::string ("Cannot proceed without rc file."); + if (rc_file._data != "" && !rc_file.exists()) { + if (config.getBoolean("confirmation") && + !confirm(format("A configuration file could not be found in {1}\n\nWould you like a sample " + "{2} created, so Taskwarrior can proceed?", + home_dir, rc_file._data))) + throw std::string("Cannot proceed without rc file."); Datetime now; std::stringstream contents; - contents << "# [Created by " - << PACKAGE_STRING - << ' ' - << now.toString ("m/d/Y H:N:S") - << "]\n" + contents << "# [Created by " << PACKAGE_STRING << ' ' << now.toString("m/d/Y H:N:S") << "]\n" << "data.location=" << data_dir._original << "\n" << "news.version=" << Version::Current() << "\n" << "\n# To use the default location of the XDG directories,\n" - << "# move this configuration file from ~/.taskrc to ~/.config/task/taskrc and update location config as follows:\n" + << "# move this configuration file from ~/.taskrc to ~/.config/task/taskrc and update " + "location config as follows:\n" << "\n#data.location=~/.local/share/task\n" << "#hooks.location=~/.config/task/hooks\n" << "\n# Color theme (uncomment one to use)\n" @@ -1246,8 +1170,8 @@ void Context::createDefaultConfig () << '\n'; // Write out the new file. - if (! File::write (rc_file._data, contents.str ())) - throw format ("Could not write to '{1}'.", rc_file._data); + if (!File::write(rc_file._data, contents.str())) + throw format("Could not write to '{1}'.", rc_file._data); // Load it so that it takes effect for this run. config.load(rc_file); @@ -1255,75 +1179,52 @@ void Context::createDefaultConfig () } //////////////////////////////////////////////////////////////////////////////// -void Context::decomposeSortField ( - const std::string& field, - std::string& key, - bool& ascending, - bool& breakIndicator) -{ - int length = field.length (); +void Context::decomposeSortField(const std::string& field, std::string& key, bool& ascending, + bool& breakIndicator) { + int length = field.length(); int decoration = 1; breakIndicator = false; - if (field[length - decoration] == '/') - { + if (field[length - decoration] == '/') { breakIndicator = true; ++decoration; } - if (field[length - decoration] == '+') - { + if (field[length - decoration] == '+') { ascending = true; - key = field.substr (0, length - decoration); - } - else if (field[length - decoration] == '-') - { + key = field.substr(0, length - decoration); + } else if (field[length - decoration] == '-') { ascending = false; - key = field.substr (0, length - decoration); - } - else - { + key = field.substr(0, length - decoration); + } else { ascending = true; key = field; } } //////////////////////////////////////////////////////////////////////////////// -void Context::debugTiming (const std::string& details, const Timer& timer) -{ +void Context::debugTiming(const std::string& details, const Timer& timer) { std::stringstream out; - out << "Timer " - << details - << ' ' - << std::setprecision (6) - << std::fixed - << timer.total_us () / 1.0e6 - << " sec"; - debug (out.str ()); + out << "Timer " << details << ' ' << std::setprecision(6) << std::fixed + << timer.total_us() / 1.0e6 << " sec"; + debug(out.str()); } //////////////////////////////////////////////////////////////////////////////// -CurrentTask Context::withCurrentTask (const Task *task) -{ - return CurrentTask(*this, task); -} +CurrentTask Context::withCurrentTask(const Task* task) { return CurrentTask(*this, task); } //////////////////////////////////////////////////////////////////////////////// // This capability is to answer the question of 'what did I just do to generate // this output?'. -void Context::updateXtermTitle () -{ - if (config.getBoolean ("xterm.title") && isatty (STDOUT_FILENO)) - { - auto command = cli2.getCommand (); +void Context::updateXtermTitle() { + if (config.getBoolean("xterm.title") && isatty(STDOUT_FILENO)) { + auto command = cli2.getCommand(); std::string title; - for (auto a = cli2._args.begin (); a != cli2._args.end (); ++a) - { - if (a != cli2._args.begin ()) - title += ' '; + for (auto a = cli2._args.begin(); a != cli2._args.end(); ++a) { + if (a != cli2._args.begin()) title += ' '; - title += a->attribute ("raw"); + title += a->attribute("raw"); } std::cout << "]0;task " << command << ' ' << title << ""; @@ -1332,92 +1233,69 @@ void Context::updateXtermTitle () //////////////////////////////////////////////////////////////////////////////// // This function allows a clean output if the command is a helper subcommand. -void Context::updateVerbosity () -{ - auto command = cli2.getCommand (); - if (command != "" && - command[0] == '_') - { +void Context::updateVerbosity() { + auto command = cli2.getCommand(); + if (command != "" && command[0] == '_') { verbosity = {"nothing"}; } } //////////////////////////////////////////////////////////////////////////////// -void Context::loadAliases () -{ +void Context::loadAliases() { for (auto& i : config) - if (i.first.substr (0, 6) == "alias.") - cli2.alias (i.first.substr (6), i.second); + if (i.first.substr(0, 6) == "alias.") cli2.alias(i.first.substr(6), i.second); } //////////////////////////////////////////////////////////////////////////////// // Using the general rc.debug setting automaticalls sets debug.hooks // and debug.parser, unless they already have values, which by default they do // not. -void Context::propagateDebug () -{ - if (config.getBoolean ("debug")) - { - if (! config.has ("debug.hooks")) - config.set ("debug.hooks", 1); +void Context::propagateDebug() { + if (config.getBoolean("debug")) { + if (!config.has("debug.hooks")) config.set("debug.hooks", 1); - if (! config.has ("debug.parser")) - config.set ("debug.parser", 1); - } - else - { - if ((config.has ("debug.hooks") && config.getInteger ("debug.hooks")) || - (config.has ("debug.parser") && config.getInteger ("debug.parser")) ) - config.set ("debug", true); + if (!config.has("debug.parser")) config.set("debug.parser", 1); + } else { + if ((config.has("debug.hooks") && config.getInteger("debug.hooks")) || + (config.has("debug.parser") && config.getInteger("debug.parser"))) + config.set("debug", true); } } //////////////////////////////////////////////////////////////////////////////// // No duplicates. -void Context::header (const std::string& input) -{ - if (input.length () && - std::find (headers.begin (), headers.end (), input) == headers.end ()) - headers.push_back (input); +void Context::header(const std::string& input) { + if (input.length() && std::find(headers.begin(), headers.end(), input) == headers.end()) + headers.push_back(input); } //////////////////////////////////////////////////////////////////////////////// // No duplicates. -void Context::footnote (const std::string& input) -{ - if (input.length () && - std::find (footnotes.begin (), footnotes.end (), input) == footnotes.end ()) - footnotes.push_back (input); +void Context::footnote(const std::string& input) { + if (input.length() && std::find(footnotes.begin(), footnotes.end(), input) == footnotes.end()) + footnotes.push_back(input); } //////////////////////////////////////////////////////////////////////////////// // No duplicates. -void Context::error (const std::string& input) -{ - if (input.length () && - std::find (errors.begin (), errors.end (), input) == errors.end ()) - errors.push_back (input); +void Context::error(const std::string& input) { + if (input.length() && std::find(errors.begin(), errors.end(), input) == errors.end()) + errors.push_back(input); } //////////////////////////////////////////////////////////////////////////////// -void Context::debug (const std::string& input) -{ - if (input.length ()) - debugMessages.push_back (input); +void Context::debug(const std::string& input) { + if (input.length()) debugMessages.push_back(input); } //////////////////////////////////////////////////////////////////////////////// -CurrentTask::CurrentTask (Context &context, const Task *task) - : context {context}, previous {context.currentTask} -{ +CurrentTask::CurrentTask(Context& context, const Task* task) + : context{context}, previous{context.currentTask} { context.currentTask = task; } //////////////////////////////////////////////////////////////////////////////// -CurrentTask::~CurrentTask () -{ - context.currentTask = previous; -} +CurrentTask::~CurrentTask() { context.currentTask = previous; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Context.h b/src/Context.h index 023178ccc..a456364e9 100644 --- a/src/Context.h +++ b/src/Context.h @@ -27,111 +27,111 @@ #ifndef INCLUDED_CONTEXT #define INCLUDED_CONTEXT -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include #include + #include class CurrentTask; -class Context -{ -public: - Context () = default; // Default constructor - ~Context (); // Destructor +class Context { + public: + Context() = default; // Default constructor + ~Context(); // Destructor - Context (const Context&); - Context& operator= (const Context&); + Context(const Context &); + Context &operator=(const Context &); - static Context& getContext (); - static void setContext (Context*); + static Context &getContext(); + static void setContext(Context *); - int initialize (int, const char**); // all startup - int run (); - int dispatch (std::string&); // command handler dispatch + int initialize(int, const char **); // all startup + int run(); + int dispatch(std::string &); // command handler dispatch - int getWidth (); // determine terminal width - int getHeight (); // determine terminal height + int getWidth(); // determine terminal width + int getHeight(); // determine terminal height - std::string getTaskContext (const std::string&, std::string, bool fallback=true); + std::string getTaskContext(const std::string &, std::string, bool fallback = true); - const std::vector getColumns () const; - void getLimits (int&, int&); + const std::vector getColumns() const; + void getLimits(int &, int &); - bool color (); // TTY or ? - bool verbose (const std::string&); // Verbosity control + bool color(); // TTY or ? + bool verbose(const std::string &); // Verbosity control - void header (const std::string&); // Header message sink - void footnote (const std::string&); // Footnote message sink - void debug (const std::string&); // Debug message sink - void error (const std::string&); // Error message sink - non-maskable + void header(const std::string &); // Header message sink + void footnote(const std::string &); // Footnote message sink + void debug(const std::string &); // Debug message sink + void error(const std::string &); // Error message sink - non-maskable - void decomposeSortField (const std::string&, std::string&, bool&, bool&); - void debugTiming (const std::string&, const Timer&); + void decomposeSortField(const std::string &, std::string &, bool &, bool &); + void debugTiming(const std::string &, const Timer &); - CurrentTask withCurrentTask (const Task *); + CurrentTask withCurrentTask(const Task *); friend class CurrentTask; -private: - void staticInitialization (); - void createDefaultConfig (); - void updateXtermTitle (); - void updateVerbosity (); - void loadAliases (); - void propagateDebug (); + private: + void staticInitialization(); + void createDefaultConfig(); + void updateXtermTitle(); + void updateVerbosity(); + void loadAliases(); + void propagateDebug(); - static Context* context; + static Context *context; -public: - CLI2 cli2 {}; - std::string home_dir {}; - File rc_file {"~/.taskrc"}; - Path data_dir {"~/.task"}; - Configuration config {}; - TDB2 tdb2 {}; - Hooks hooks {}; - bool determine_color_use {true}; - bool use_color {true}; - bool verbosity_legacy {false}; - std::set verbosity {}; - std::vector headers {}; - std::vector footnotes {}; - std::vector errors {}; - std::vector debugMessages {}; - std::map commands {}; - std::map columns {}; - int terminal_width {0}; - int terminal_height {0}; + public: + CLI2 cli2{}; + std::string home_dir{}; + File rc_file{"~/.taskrc"}; + Path data_dir{"~/.task"}; + Configuration config{}; + TDB2 tdb2{}; + Hooks hooks{}; + bool determine_color_use{true}; + bool use_color{true}; + bool verbosity_legacy{false}; + std::set verbosity{}; + std::vector headers{}; + std::vector footnotes{}; + std::vector errors{}; + std::vector debugMessages{}; + std::map commands{}; + std::map columns{}; + int terminal_width{0}; + int terminal_height{0}; - Timer timer_total {}; - long time_total_us {0}; - long time_init_us {0}; - long time_load_us {0}; - long time_gc_us {0}; - long time_filter_us {0}; - long time_commit_us {0}; - long time_sort_us {0}; - long time_render_us {0}; - long time_hooks_us {0}; + Timer timer_total{}; + long time_total_us{0}; + long time_init_us{0}; + long time_load_us{0}; + long time_gc_us{0}; + long time_filter_us{0}; + long time_commit_us{0}; + long time_sort_us{0}; + long time_render_us{0}; + long time_hooks_us{0}; // the current task for DOM references, or NULL if there is no task - const Task * currentTask {NULL}; + const Task *currentTask{NULL}; }; //////////////////////////////////////////////////////////////////////////////// // CurrentTask resets Context::currentTask to previous context task on destruction; this ensures // that this context value is restored when exiting the scope where the context was applied. class CurrentTask { -public: + public: ~CurrentTask(); -private: + private: CurrentTask(Context &context, const Task *previous); Context &context; diff --git a/src/DOM.cpp b/src/DOM.cpp index e2eea1b68..fe47f2878 100644 --- a/src/DOM.cpp +++ b/src/DOM.cpp @@ -27,19 +27,20 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include -#include #include +#include #include #include -#include +#include +#include #include +#include +#include #include +#include +#include + //////////////////////////////////////////////////////////////////////////////// // DOM Supported References: // @@ -62,23 +63,18 @@ // system.version // system.os // -bool getDOM (const std::string& name, Variant& value) -{ +bool getDOM(const std::string& name, Variant& value) { // Special case, blank refs cause problems. - if (name == "") - return false; + if (name == "") return false; - auto len = name.length (); + auto len = name.length(); // rc. --> context.config - if (len > 3 && - ! name.compare (0, 3, "rc.", 3)) - { - auto key = name.substr (3); - auto c = Context::getContext ().config.find (key); - if (c != Context::getContext ().config.end ()) - { - value = Variant (c->second); + if (len > 3 && !name.compare(0, 3, "rc.", 3)) { + auto key = name.substr(3); + auto c = Context::getContext().config.find(key); + if (c != Context::getContext().config.end()) { + value = Variant(c->second); return true; } @@ -86,55 +82,41 @@ bool getDOM (const std::string& name, Variant& value) } // tw.* - if (len > 3 && - ! name.compare (0, 3, "tw.", 3)) - { - if (name == "tw.syncneeded") - { - value = Variant (0); - if (Context::getContext ().tdb2.num_local_changes () > 0) { - value = Variant (1); + if (len > 3 && !name.compare(0, 3, "tw.", 3)) { + if (name == "tw.syncneeded") { + value = Variant(0); + if (Context::getContext().tdb2.num_local_changes() > 0) { + value = Variant(1); } return true; - } - else if (name == "tw.program") - { - value = Variant (Context::getContext ().cli2.getBinary ()); + } else if (name == "tw.program") { + value = Variant(Context::getContext().cli2.getBinary()); return true; - } - else if (name == "tw.args") - { + } else if (name == "tw.args") { std::string commandLine; - for (auto& arg : Context::getContext ().cli2._original_args) - { - if (commandLine != "") - commandLine += ' '; + for (auto& arg : Context::getContext().cli2._original_args) { + if (commandLine != "") commandLine += ' '; commandLine += arg.attribute("raw"); } - value = Variant (commandLine); + value = Variant(commandLine); return true; - } - else if (name == "tw.width") - { - value = Variant (static_cast (Context::getContext ().terminal_width - ? Context::getContext ().terminal_width - : Context::getContext ().getWidth ())); + } else if (name == "tw.width") { + value = Variant(static_cast(Context::getContext().terminal_width + ? Context::getContext().terminal_width + : Context::getContext().getWidth())); return true; - } - else if (name == "tw.height") - { - value = Variant (static_cast (Context::getContext ().terminal_height - ? Context::getContext ().terminal_height - : Context::getContext ().getHeight ())); + } else if (name == "tw.height") { + value = Variant(static_cast(Context::getContext().terminal_height + ? Context::getContext().terminal_height + : Context::getContext().getHeight())); return true; } - else if (name == "tw.version") - { - value = Variant (VERSION); + else if (name == "tw.version") { + value = Variant(VERSION); return true; } @@ -142,40 +124,29 @@ bool getDOM (const std::string& name, Variant& value) } // context.* - if (len > 8 && - ! name.compare (0, 8, "context.", 8)) - { - if (name == "context.program") - { - value = Variant (Context::getContext ().cli2.getBinary ()); + if (len > 8 && !name.compare(0, 8, "context.", 8)) { + if (name == "context.program") { + value = Variant(Context::getContext().cli2.getBinary()); return true; - } - else if (name == "context.args") - { + } else if (name == "context.args") { std::string commandLine; - for (auto& arg : Context::getContext ().cli2._original_args) - { - if (commandLine != "") - commandLine += ' '; + for (auto& arg : Context::getContext().cli2._original_args) { + if (commandLine != "") commandLine += ' '; commandLine += arg.attribute("raw"); } - value = Variant (commandLine); + value = Variant(commandLine); return true; - } - else if (name == "context.width") - { - value = Variant (static_cast (Context::getContext ().terminal_width - ? Context::getContext ().terminal_width - : Context::getContext ().getWidth ())); + } else if (name == "context.width") { + value = Variant(static_cast(Context::getContext().terminal_width + ? Context::getContext().terminal_width + : Context::getContext().getWidth())); return true; - } - else if (name == "context.height") - { - value = Variant (static_cast (Context::getContext ().terminal_height - ? Context::getContext ().terminal_height - : Context::getContext ().getHeight ())); + } else if (name == "context.height") { + value = Variant(static_cast(Context::getContext().terminal_height + ? Context::getContext().terminal_height + : Context::getContext().getHeight())); return true; } @@ -183,20 +154,16 @@ bool getDOM (const std::string& name, Variant& value) } // system. --> Implement locally. - if (len > 7 && - ! name.compare (0, 7, "system.", 7)) - { + if (len > 7 && !name.compare(0, 7, "system.", 7)) { // Taskwarrior version number. - if (name == "system.version") - { - value = Variant (VERSION); + if (name == "system.version") { + value = Variant(VERSION); return true; } // OS type. - else if (name == "system.os") - { - value = Variant (osName ()); + else if (name == "system.os") { + value = Variant(osName()); return true; } @@ -239,210 +206,192 @@ bool getDOM (const std::string& name, Variant& value) // // If task is NULL, then the contextual task will be determined from the DOM // string, if any exists. -bool getDOM (const std::string& name, const Task* task, Variant& value) -{ +bool getDOM(const std::string& name, const Task* task, Variant& value) { // Special case, blank refs cause problems. - if (name == "") - return false; + if (name == "") return false; // Quickly deal with the most common cases. - if (task && name == "id") - { - value = Variant (static_cast (task->id)); + if (task && name == "id") { + value = Variant(static_cast(task->id)); return true; } - if (task && name == "urgency") - { - value = Variant (task->urgency_c ()); + if (task && name == "urgency") { + value = Variant(task->urgency_c()); return true; } // split name on '.' - auto elements = split (name, '.'); + auto elements = split(name, '.'); Task loaded_task; // decide whether the reference is going to be the passed // "task" or whether it's going to be a newly loaded task (if id/uuid was // given). const Task* ref = task; - Lexer lexer (elements[0]); + Lexer lexer(elements[0]); std::string token; Lexer::Type type; // If this can be ID/UUID reference (the name contains '.'), // lex it to figure out. Otherwise don't lex, as lexing can be slow. - if ((elements.size() > 1) and lexer.token (token, type)) - { + if ((elements.size() > 1) and lexer.token(token, type)) { bool reloaded = false; - if (type == Lexer::Type::uuid && - token.length () == elements[0].length ()) - { - if (!task || token != task->get ("uuid")) - { - if (Context::getContext ().tdb2.get (token, loaded_task)) - reloaded = true; + if (type == Lexer::Type::uuid && token.length() == elements[0].length()) { + if (!task || token != task->get("uuid")) { + if (Context::getContext().tdb2.get(token, loaded_task)) reloaded = true; } // Eat elements[0]/UUID. - elements.erase (elements.begin ()); - } - else if (type == Lexer::Type::number && - token.find ('.') == std::string::npos) - { - auto id = strtol (token.c_str (), nullptr, 10); - if (id && (!task || id != task->id)) - { - if (Context::getContext ().tdb2.get (id, loaded_task)) - reloaded = true; + elements.erase(elements.begin()); + } else if (type == Lexer::Type::number && token.find('.') == std::string::npos) { + auto id = strtol(token.c_str(), nullptr, 10); + if (id && (!task || id != task->id)) { + if (Context::getContext().tdb2.get(id, loaded_task)) reloaded = true; } // Eat elements[0]/ID. - elements.erase (elements.begin ()); + elements.erase(elements.begin()); } - if (reloaded) - ref = &loaded_task; + if (reloaded) ref = &loaded_task; } - // The remainder of this method requires a contextual task, so if we do not // have one, delegate to the two-argument getDOM - if (!ref) - return getDOM (name, value); + if (!ref) return getDOM(name, value); - auto size = elements.size (); + auto size = elements.size(); std::string canonical; - if ((size == 1 || size == 2) && Context::getContext ().cli2.canonicalize (canonical, "attribute", elements[0])) - { + if ((size == 1 || size == 2) && + Context::getContext().cli2.canonicalize(canonical, "attribute", elements[0])) { // Now that 'ref' is the contextual task, and any ID/UUID is chopped off the // elements vector, DOM resolution is now simple. - if (size == 1 && canonical == "id") - { - value = Variant (static_cast (ref->id)); + if (size == 1 && canonical == "id") { + value = Variant(static_cast(ref->id)); return true; } - if (size == 1 && canonical == "urgency") - { - value = Variant (ref->urgency_c ()); + if (size == 1 && canonical == "urgency") { + value = Variant(ref->urgency_c()); return true; } // Special handling of status required for virtual waiting status // implementation. Remove in 3.0.0. - if (size == 1 && canonical == "status") - { - value = Variant (ref->statusToText (ref->getStatus ())); + if (size == 1 && canonical == "status") { + value = Variant(ref->statusToText(ref->getStatus())); return true; } - Column* column = Context::getContext ().columns[canonical]; + Column* column = Context::getContext().columns[canonical]; - if (size == 1 && column) - { - if (column->is_uda () && ! ref->has (canonical)) - { - value = Variant (""); + if (size == 1 && column) { + if (column->is_uda() && !ref->has(canonical)) { + value = Variant(""); return true; } - if (column->type () == "date") - { - auto numeric = ref->get_date (canonical); + if (column->type() == "date") { + auto numeric = ref->get_date(canonical); if (numeric == 0) - value = Variant (""); + value = Variant(""); else - value = Variant (numeric, Variant::type_date); - } - else if (column->type () == "duration" || canonical == "recur") - { - auto period = ref->get (canonical); + value = Variant(numeric, Variant::type_date); + } else if (column->type() == "duration" || canonical == "recur") { + auto period = ref->get(canonical); Duration iso; std::string::size_type cursor = 0; - if (iso.parse (period, cursor)) - value = Variant (iso.toTime_t (), Variant::type_duration); + if (iso.parse(period, cursor)) + value = Variant(iso.toTime_t(), Variant::type_duration); else - value = Variant (Duration (ref->get (canonical)).toTime_t (), Variant::type_duration); - } - else if (column->type () == "numeric") - value = Variant (ref->get_float (canonical)); + value = Variant(Duration(ref->get(canonical)).toTime_t(), Variant::type_duration); + } else if (column->type() == "numeric") + value = Variant(ref->get_float(canonical)); else - value = Variant (ref->get (canonical)); + value = Variant(ref->get(canonical)); return true; } - if (size == 2 && canonical == "tags") - { - value = Variant (ref->hasTag (elements[1]) ? elements[1] : ""); + if (size == 2 && canonical == "tags") { + value = Variant(ref->hasTag(elements[1]) ? elements[1] : ""); return true; } - if (size == 2 && column && column->type () == "date") - { - Datetime date (ref->get_date (canonical)); - if (elements[1] == "year") { value = Variant (static_cast (date.year ())); return true; } - else if (elements[1] == "month") { value = Variant (static_cast (date.month ())); return true; } - else if (elements[1] == "day") { value = Variant (static_cast (date.day ())); return true; } - else if (elements[1] == "week") { value = Variant (static_cast (date.week ())); return true; } - else if (elements[1] == "weekday") { value = Variant (static_cast (date.dayOfWeek ())); return true; } - else if (elements[1] == "julian") { value = Variant (static_cast (date.dayOfYear ())); return true; } - else if (elements[1] == "hour") { value = Variant (static_cast (date.hour ())); return true; } - else if (elements[1] == "minute") { value = Variant (static_cast (date.minute ())); return true; } - else if (elements[1] == "second") { value = Variant (static_cast (date.second ())); return true; } + if (size == 2 && column && column->type() == "date") { + Datetime date(ref->get_date(canonical)); + if (elements[1] == "year") { + value = Variant(static_cast(date.year())); + return true; + } else if (elements[1] == "month") { + value = Variant(static_cast(date.month())); + return true; + } else if (elements[1] == "day") { + value = Variant(static_cast(date.day())); + return true; + } else if (elements[1] == "week") { + value = Variant(static_cast(date.week())); + return true; + } else if (elements[1] == "weekday") { + value = Variant(static_cast(date.dayOfWeek())); + return true; + } else if (elements[1] == "julian") { + value = Variant(static_cast(date.dayOfYear())); + return true; + } else if (elements[1] == "hour") { + value = Variant(static_cast(date.hour())); + return true; + } else if (elements[1] == "minute") { + value = Variant(static_cast(date.minute())); + return true; + } else if (elements[1] == "second") { + value = Variant(static_cast(date.second())); + return true; + } } } - if (size == 2 && elements[0] == "annotations" && elements[1] == "count") - { - value = Variant (static_cast (ref->getAnnotationCount ())); + if (size == 2 && elements[0] == "annotations" && elements[1] == "count") { + value = Variant(static_cast(ref->getAnnotationCount())); return true; } - if (size == 3 && elements[0] == "annotations") - { - auto annos = ref->getAnnotations (); + if (size == 3 && elements[0] == "annotations") { + auto annos = ref->getAnnotations(); - int a = strtol (elements[1].c_str (), nullptr, 10); + int a = strtol(elements[1].c_str(), nullptr, 10); int count = 0; // Count off the 'a'th annotation. - for (const auto& i : annos) - { - if (++count == a) - { - if (elements[2] == "entry") - { + for (const auto& i : annos) { + if (++count == a) { + if (elements[2] == "entry") { // annotation_1234567890 // 0 ^11 - value = Variant ((time_t) strtoll (i.first.substr (11).c_str (), NULL, 10), Variant::type_date); + value = + Variant((time_t)strtoll(i.first.substr(11).c_str(), NULL, 10), Variant::type_date); return true; - } - else if (elements[2] == "description") - { - value = Variant (i.second); + } else if (elements[2] == "description") { + value = Variant(i.second); return true; } } } } - if (size == 4 && elements[0] == "annotations" && elements[2] == "entry") - { - auto annos = ref->getAnnotations (); + if (size == 4 && elements[0] == "annotations" && elements[2] == "entry") { + auto annos = ref->getAnnotations(); - int a = strtol (elements[1].c_str (), nullptr, 10); + int a = strtol(elements[1].c_str(), nullptr, 10); int count = 0; // Count off the 'a'th annotation. - for (const auto& i : annos) - { - if (++count == a) - { + for (const auto& i : annos) { + if (++count == a) { // ..entry.year // ..entry.month // ..entry.day @@ -452,22 +401,41 @@ bool getDOM (const std::string& name, const Task* task, Variant& value) // ..entry.hour // ..entry.minute // ..entry.second - Datetime date (i.first.substr (11)); - if (elements[3] == "year") { value = Variant (static_cast (date.year ())); return true; } - else if (elements[3] == "month") { value = Variant (static_cast (date.month ())); return true; } - else if (elements[3] == "day") { value = Variant (static_cast (date.day ())); return true; } - else if (elements[3] == "week") { value = Variant (static_cast (date.week ())); return true; } - else if (elements[3] == "weekday") { value = Variant (static_cast (date.dayOfWeek ())); return true; } - else if (elements[3] == "julian") { value = Variant (static_cast (date.dayOfYear ())); return true; } - else if (elements[3] == "hour") { value = Variant (static_cast (date.hour ())); return true; } - else if (elements[3] == "minute") { value = Variant (static_cast (date.minute ())); return true; } - else if (elements[3] == "second") { value = Variant (static_cast (date.second ())); return true; } + Datetime date(i.first.substr(11)); + if (elements[3] == "year") { + value = Variant(static_cast(date.year())); + return true; + } else if (elements[3] == "month") { + value = Variant(static_cast(date.month())); + return true; + } else if (elements[3] == "day") { + value = Variant(static_cast(date.day())); + return true; + } else if (elements[3] == "week") { + value = Variant(static_cast(date.week())); + return true; + } else if (elements[3] == "weekday") { + value = Variant(static_cast(date.dayOfWeek())); + return true; + } else if (elements[3] == "julian") { + value = Variant(static_cast(date.dayOfYear())); + return true; + } else if (elements[3] == "hour") { + value = Variant(static_cast(date.hour())); + return true; + } else if (elements[3] == "minute") { + value = Variant(static_cast(date.minute())); + return true; + } else if (elements[3] == "second") { + value = Variant(static_cast(date.second())); + return true; + } } } } // Delegate to the context-free version of DOM::get. - return getDOM (name, value); + return getDOM(name, value); } //////////////////////////////////////////////////////////////////////////////// @@ -513,41 +481,28 @@ bool getDOM (const std::string& name, const Task* task, Variant& value) // This makes the DOM class a reusible object. //////////////////////////////////////////////////////////////////////////////// -DOM::~DOM () -{ - delete _node; +DOM::~DOM() { delete _node; } + +//////////////////////////////////////////////////////////////////////////////// +void DOM::addSource(const std::string& reference, bool (*provider)(const std::string&, Variant&)) { + if (_node == nullptr) _node = new DOM::Node(); + + _node->addSource(reference, provider); } //////////////////////////////////////////////////////////////////////////////// -void DOM::addSource ( - const std::string& reference, - bool (*provider)(const std::string&, Variant&)) -{ - if (_node == nullptr) - _node = new DOM::Node (); - - _node->addSource (reference, provider); +bool DOM::valid(const std::string& reference) const { + return _node && _node->find(reference) != nullptr; } //////////////////////////////////////////////////////////////////////////////// -bool DOM::valid (const std::string& reference) const -{ - return _node && _node->find (reference) != nullptr; -} +Variant DOM::get(const std::string& reference) const { + Variant v(""); -//////////////////////////////////////////////////////////////////////////////// -Variant DOM::get (const std::string& reference) const -{ - Variant v (""); - - if (_node) - { - auto node = _node->find (reference); - if (node != nullptr && - node->_provider != nullptr) - { - if (node->_provider (reference, v)) - return v; + if (_node) { + auto node = _node->find(reference); + if (node != nullptr && node->_provider != nullptr) { + if (node->_provider(reference, v)) return v; } } @@ -555,60 +510,47 @@ Variant DOM::get (const std::string& reference) const } //////////////////////////////////////////////////////////////////////////////// -int DOM::count () const -{ - if (_node) - return _node->count (); +int DOM::count() const { + if (_node) return _node->count(); return 0; } //////////////////////////////////////////////////////////////////////////////// -std::vector DOM::decomposeReference (const std::string& reference) -{ - return split (reference, '.'); +std::vector DOM::decomposeReference(const std::string& reference) { + return split(reference, '.'); } //////////////////////////////////////////////////////////////////////////////// -std::string DOM::dump () const -{ - if (_node) - return _node->dump (); +std::string DOM::dump() const { + if (_node) return _node->dump(); return ""; } //////////////////////////////////////////////////////////////////////////////// -DOM::Node::~Node () -{ - for (auto& branch : _branches) - delete branch; +DOM::Node::~Node() { + for (auto& branch : _branches) delete branch; } //////////////////////////////////////////////////////////////////////////////// -void DOM::Node::addSource ( - const std::string& reference, - bool (*provider)(const std::string&, Variant&)) -{ +void DOM::Node::addSource(const std::string& reference, + bool (*provider)(const std::string&, Variant&)) { auto cursor = this; - for (const auto& element : DOM::decomposeReference (reference)) - { - auto found {false}; - for (auto& branch : cursor->_branches) - { - if (branch->_name == element) - { + for (const auto& element : DOM::decomposeReference(reference)) { + auto found{false}; + for (auto& branch : cursor->_branches) { + if (branch->_name == element) { cursor = branch; found = true; break; } } - if (! found) - { - auto branch = new DOM::Node (); + if (!found) { + auto branch = new DOM::Node(); branch->_name = element; - cursor->_branches.push_back (branch); + cursor->_branches.push_back(branch); cursor = branch; } } @@ -618,85 +560,66 @@ void DOM::Node::addSource ( //////////////////////////////////////////////////////////////////////////////// // A valid reference is one that has a provider function. -bool DOM::Node::valid (const std::string& reference) const -{ - return find (reference) != nullptr; -} +bool DOM::Node::valid(const std::string& reference) const { return find(reference) != nullptr; } //////////////////////////////////////////////////////////////////////////////// -const DOM::Node* DOM::Node::find (const std::string& reference) const -{ +const DOM::Node* DOM::Node::find(const std::string& reference) const { auto cursor = this; - for (const auto& element : DOM::decomposeReference (reference)) - { - auto found {false}; - for (auto& branch : cursor->_branches) - { - if (branch->_name == element) - { + for (const auto& element : DOM::decomposeReference(reference)) { + auto found{false}; + for (auto& branch : cursor->_branches) { + if (branch->_name == element) { cursor = branch; found = true; break; } } - if (! found) - break; + if (!found) break; } - if (reference.length () && cursor != this) - return cursor; + if (reference.length() && cursor != this) return cursor; return nullptr; } //////////////////////////////////////////////////////////////////////////////// -int DOM::Node::count () const -{ +int DOM::Node::count() const { // Recurse and count the branches. - int total {0}; - for (auto& branch : _branches) - { - if (branch->_provider) - ++total; - total += branch->count (); + int total{0}; + for (auto& branch : _branches) { + if (branch->_provider) ++total; + total += branch->count(); } return total; } //////////////////////////////////////////////////////////////////////////////// -std::string DOM::Node::dumpNode ( - const DOM::Node* node, - int depth) const -{ +std::string DOM::Node::dumpNode(const DOM::Node* node, int depth) const { std::stringstream out; // Indent. - out << std::string (depth * 2, ' '); + out << std::string(depth * 2, ' '); out << "\033[31m" << node->_name << "\033[0m"; - if (node->_provider) - out << " 0x" << std::hex << (long long) (void*) node->_provider; + if (node->_provider) out << " 0x" << std::hex << (long long)(void*)node->_provider; out << '\n'; // Recurse for branches. - for (auto& b : node->_branches) - out << dumpNode (b, depth + 1); + for (auto& b : node->_branches) out << dumpNode(b, depth + 1); - return out.str (); + return out.str(); } //////////////////////////////////////////////////////////////////////////////// -std::string DOM::Node::dump () const -{ +std::string DOM::Node::dump() const { std::stringstream out; - out << "DOM::Node (" << count () << " nodes)\n" - << dumpNode (this, 1); + out << "DOM::Node (" << count() << " nodes)\n" << dumpNode(this, 1); - return out.str (); + return out.str(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/DOM.h b/src/DOM.h index e3595e411..3c2bd3f79 100644 --- a/src/DOM.h +++ b/src/DOM.h @@ -27,49 +27,48 @@ #ifndef INCLUDED_DOM #define INCLUDED_DOM -#include -#include #include +#include + +#include // 2017-04-22 Deprecated, use DOM::get. -bool getDOM (const std::string&, Variant&); -bool getDOM (const std::string&, const Task*, Variant&); +bool getDOM(const std::string&, Variant&); +bool getDOM(const std::string&, const Task*, Variant&); -class DOM -{ -public: - ~DOM (); - void addSource (const std::string&, bool (*)(const std::string&, Variant&)); - bool valid (const std::string&) const; -/* - // TODO Task object should register a generic provider. - Variant get (const Task&, const std::string&) const; -*/ - Variant get (const std::string&) const; - int count () const; - static std::vector decomposeReference (const std::string&); - std::string dump () const; +class DOM { + public: + ~DOM(); + void addSource(const std::string&, bool (*)(const std::string&, Variant&)); + bool valid(const std::string&) const; + /* + // TODO Task object should register a generic provider. + Variant get (const Task&, const std::string&) const; + */ + Variant get(const std::string&) const; + int count() const; + static std::vector decomposeReference(const std::string&); + std::string dump() const; -private: - class Node - { - public: - ~Node (); - void addSource (const std::string&, bool (*)(const std::string&, Variant&)); - bool valid (const std::string&) const; - const DOM::Node* find (const std::string&) const; - int count () const; - std::string dumpNode (const DOM::Node*, int) const; - std::string dump () const; + private: + class Node { + public: + ~Node(); + void addSource(const std::string&, bool (*)(const std::string&, Variant&)); + bool valid(const std::string&) const; + const DOM::Node* find(const std::string&) const; + int count() const; + std::string dumpNode(const DOM::Node*, int) const; + std::string dump() const; - public: - std::string _name {"Unknown"}; - bool (*_provider)(const std::string&, Variant&) {nullptr}; - std::vector _branches {}; + public: + std::string _name{"Unknown"}; + bool (*_provider)(const std::string&, Variant&){nullptr}; + std::vector _branches{}; }; -private: - DOM::Node* _node {nullptr}; + private: + DOM::Node* _node{nullptr}; }; #endif diff --git a/src/Eval.cpp b/src/Eval.cpp index 9d2748aa3..5b869061e 100644 --- a/src/Eval.cpp +++ b/src/Eval.cpp @@ -27,75 +27,76 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include #include +#include +#include + +#include //////////////////////////////////////////////////////////////////////////////// // Supported operators, borrowed from C++, particularly the precedence. // Note: table is sorted by length of operator string, so searches match // longest first. -static struct -{ +static struct { std::string op; - int precedence; - char type; // b=binary, u=unary, c=circumfix - char associativity; // l=left, r=right, _=? -} operators[] = -{ - // Operator Precedence Type Associativity - { "^", 16, 'b', 'r' }, // Exponent + int precedence; + char type; // b=binary, u=unary, c=circumfix + char associativity; // l=left, r=right, _=? +} operators[] = { + // Operator Precedence Type Associativity + {"^", 16, 'b', 'r'}, // Exponent - { "!", 15, 'u', 'r' }, // Unary not - { "_neg_", 15, 'u', 'r' }, // Unary minus - { "_pos_", 15, 'u', 'r' }, // Unary plus + {"!", 15, 'u', 'r'}, // Unary not + {"_neg_", 15, 'u', 'r'}, // Unary minus + {"_pos_", 15, 'u', 'r'}, // Unary plus - { "_hastag_", 14, 'b', 'l'}, // +tag [Pseudo-op] - { "_notag_", 14, 'b', 'l'}, // -tag [Pseudo-op] + {"_hastag_", 14, 'b', 'l'}, // +tag [Pseudo-op] + {"_notag_", 14, 'b', 'l'}, // -tag [Pseudo-op] - { "*", 13, 'b', 'l' }, // Multiplication - { "/", 13, 'b', 'l' }, // Division - { "%", 13, 'b', 'l' }, // Modulus + {"*", 13, 'b', 'l'}, // Multiplication + {"/", 13, 'b', 'l'}, // Division + {"%", 13, 'b', 'l'}, // Modulus - { "+", 12, 'b', 'l' }, // Addition - { "-", 12, 'b', 'l' }, // Subtraction + {"+", 12, 'b', 'l'}, // Addition + {"-", 12, 'b', 'l'}, // Subtraction - { "<=", 10, 'b', 'l' }, // Less than or equal - { ">=", 10, 'b', 'l' }, // Greater than or equal - { ">", 10, 'b', 'l' }, // Greater than - { "<", 10, 'b', 'l' }, // Less than + {"<=", 10, 'b', 'l'}, // Less than or equal + {">=", 10, 'b', 'l'}, // Greater than or equal + {">", 10, 'b', 'l'}, // Greater than + {"<", 10, 'b', 'l'}, // Less than - { "=", 9, 'b', 'l' }, // Equal (partial) - { "==", 9, 'b', 'l' }, // Equal (exact) - { "!=", 9, 'b', 'l' }, // Inequal (partial) - { "!==", 9, 'b', 'l' }, // Inequal (exact) + {"=", 9, 'b', 'l'}, // Equal (partial) + {"==", 9, 'b', 'l'}, // Equal (exact) + {"!=", 9, 'b', 'l'}, // Inequal (partial) + {"!==", 9, 'b', 'l'}, // Inequal (exact) - { "~", 8, 'b', 'l' }, // Regex match - { "!~", 8, 'b', 'l' }, // Regex non-match + {"~", 8, 'b', 'l'}, // Regex match + {"!~", 8, 'b', 'l'}, // Regex non-match - { "and", 5, 'b', 'l' }, // Conjunction - { "or", 4, 'b', 'l' }, // Disjunction - { "xor", 3, 'b', 'l' }, // Disjunction + {"and", 5, 'b', 'l'}, // Conjunction + {"or", 4, 'b', 'l'}, // Disjunction + {"xor", 3, 'b', 'l'}, // Disjunction - { "(", 0, 'c', '_' }, // Precedence start - { ")", 0, 'c', '_' }, // Precedence end + {"(", 0, 'c', '_'}, // Precedence start + {")", 0, 'c', '_'}, // Precedence end }; -#define NUM_OPERATORS (sizeof (operators) / sizeof (operators[0])) +#define NUM_OPERATORS (sizeof(operators) / sizeof(operators[0])) //////////////////////////////////////////////////////////////////////////////// // Built-in support for some named constants. -static bool namedConstants (const std::string& name, Variant& value) -{ - if (name == "true") value = Variant (true); - else if (name == "false") value = Variant (false); - else if (name == "pi") value = Variant (3.14159165); +static bool namedConstants(const std::string& name, Variant& value) { + if (name == "true") + value = Variant(true); + else if (name == "false") + value = Variant(false); + else if (name == "pi") + value = Variant(3.14159165); else return false; @@ -104,11 +105,9 @@ static bool namedConstants (const std::string& name, Variant& value) //////////////////////////////////////////////////////////////////////////////// // Support for evaluating DOM references (add with `e.AddSource(domSource)`) -bool domSource (const std::string& identifier, Variant& value) -{ - if (getDOM (identifier, Context::getContext ().currentTask, value)) - { - value.source (identifier); +bool domSource(const std::string& identifier, Variant& value) { + if (getDOM(identifier, Context::getContext().currentTask, value)) { + value.source(identifier); return true; } @@ -116,300 +115,273 @@ bool domSource (const std::string& identifier, Variant& value) } //////////////////////////////////////////////////////////////////////////////// -Eval::Eval () -{ - addSource (namedConstants); -} +Eval::Eval() { addSource(namedConstants); } //////////////////////////////////////////////////////////////////////////////// -void Eval::addSource (bool (*source)(const std::string&, Variant&)) -{ - _sources.push_back (source); -} +void Eval::addSource(bool (*source)(const std::string&, Variant&)) { _sources.push_back(source); } //////////////////////////////////////////////////////////////////////////////// -void Eval::evaluateInfixExpression (const std::string& e, Variant& v) const -{ +void Eval::evaluateInfixExpression(const std::string& e, Variant& v) const { // Reduce e to a vector of tokens. - Lexer l (e); - std::vector > tokens; + Lexer l(e); + std::vector> tokens; std::string token; Lexer::Type type; - while (l.token (token, type)) - tokens.emplace_back (token, type); + while (l.token(token, type)) tokens.emplace_back(token, type); // Parse for syntax checking and operator replacement. - if (_debug) - Context::getContext ().debug ("FILTER Infix " + dump (tokens)); - infixParse (tokens); - if (_debug) - Context::getContext ().debug ("FILTER Infix parsed " + dump (tokens)); + if (_debug) Context::getContext().debug("FILTER Infix " + dump(tokens)); + infixParse(tokens); + if (_debug) Context::getContext().debug("FILTER Infix parsed " + dump(tokens)); // Convert infix --> postfix. - infixToPostfix (tokens); - if (_debug) - Context::getContext ().debug ("FILTER Postfix " + dump (tokens)); + infixToPostfix(tokens); + if (_debug) Context::getContext().debug("FILTER Postfix " + dump(tokens)); // Call the postfix evaluator. - evaluatePostfixStack (tokens, v); + evaluatePostfixStack(tokens, v); } //////////////////////////////////////////////////////////////////////////////// -void Eval::evaluatePostfixExpression (const std::string& e, Variant& v) const -{ +void Eval::evaluatePostfixExpression(const std::string& e, Variant& v) const { // Reduce e to a vector of tokens. - Lexer l (e); - std::vector > tokens; + Lexer l(e); + std::vector> tokens; std::string token; Lexer::Type type; - while (l.token (token, type)) - tokens.emplace_back (token, type); + while (l.token(token, type)) tokens.emplace_back(token, type); - if (_debug) - Context::getContext ().debug ("FILTER Postfix " + dump (tokens)); + if (_debug) Context::getContext().debug("FILTER Postfix " + dump(tokens)); // Call the postfix evaluator. - evaluatePostfixStack (tokens, v); + evaluatePostfixStack(tokens, v); } //////////////////////////////////////////////////////////////////////////////// -void Eval::compileExpression ( - const std::vector >& precompiled) -{ +void Eval::compileExpression(const std::vector>& precompiled) { _compiled = precompiled; // Parse for syntax checking and operator replacement. - if (_debug) - Context::getContext ().debug ("FILTER Infix " + dump (_compiled)); - infixParse (_compiled); - if (_debug) - Context::getContext ().debug ("FILTER Infix parsed " + dump (_compiled)); + if (_debug) Context::getContext().debug("FILTER Infix " + dump(_compiled)); + infixParse(_compiled); + if (_debug) Context::getContext().debug("FILTER Infix parsed " + dump(_compiled)); // Convert infix --> postfix. - infixToPostfix (_compiled); - if (_debug) - Context::getContext ().debug ("FILTER Postfix " + dump (_compiled)); + infixToPostfix(_compiled); + if (_debug) Context::getContext().debug("FILTER Postfix " + dump(_compiled)); } //////////////////////////////////////////////////////////////////////////////// -void Eval::evaluateCompiledExpression (Variant& v) -{ +void Eval::evaluateCompiledExpression(Variant& v) { // Call the postfix evaluator. - evaluatePostfixStack (_compiled, v); + evaluatePostfixStack(_compiled, v); } //////////////////////////////////////////////////////////////////////////////// -void Eval::debug (bool value) -{ - _debug = value; -} +void Eval::debug(bool value) { _debug = value; } //////////////////////////////////////////////////////////////////////////////// // Static. -std::vector Eval::getOperators () -{ - std::vector all; +std::vector Eval::getOperators() { + std::vector all; all.reserve(NUM_OPERATORS); - for (const auto &opr : operators) - all.push_back (opr.op); + for (const auto& opr : operators) all.push_back(opr.op); return all; } //////////////////////////////////////////////////////////////////////////////// // Static. -std::vector Eval::getBinaryOperators () -{ - std::vector all; - for (const auto &opr : operators) - if (opr.type == 'b') - all.push_back (opr.op); +std::vector Eval::getBinaryOperators() { + std::vector all; + for (const auto& opr : operators) + if (opr.type == 'b') all.push_back(opr.op); return all; } //////////////////////////////////////////////////////////////////////////////// -void Eval::evaluatePostfixStack ( - const std::vector >& tokens, - Variant& result) const -{ - if (tokens.size () == 0) - throw std::string ("No expression to evaluate."); +void Eval::evaluatePostfixStack(const std::vector>& tokens, + Variant& result) const { + if (tokens.size() == 0) throw std::string("No expression to evaluate."); // This is stack used by the postfix evaluator. - std::vector values; + std::vector values; values.reserve(tokens.size()); - for (const auto& token : tokens) - { + for (const auto& token : tokens) { // Unary operators. - if (token.second == Lexer::Type::op && - token.first == "!") - { - if (values.size () < 1) - throw std::string ("The expression could not be evaluated."); + if (token.second == Lexer::Type::op && token.first == "!") { + if (values.size() < 1) throw std::string("The expression could not be evaluated."); - Variant right = values.back (); - values.pop_back (); - Variant result = ! right; - values.push_back (result); + Variant right = values.back(); + values.pop_back(); + Variant result = !right; + values.push_back(result); if (_debug) - Context::getContext ().debug (format ("Eval {1} ↓'{2}' → ↑'{3}'", token.first, (std::string) right, (std::string) result)); - } - else if (token.second == Lexer::Type::op && - token.first == "_neg_") - { - if (values.size () < 1) - throw std::string ("The expression could not be evaluated."); + Context::getContext().debug(format("Eval {1} ↓'{2}' → ↑'{3}'", token.first, + (std::string)right, (std::string)result)); + } else if (token.second == Lexer::Type::op && token.first == "_neg_") { + if (values.size() < 1) throw std::string("The expression could not be evaluated."); - Variant right = values.back (); - values.pop_back (); + Variant right = values.back(); + values.pop_back(); - Variant result (0); + Variant result(0); result -= right; - values.push_back (result); + values.push_back(result); if (_debug) - Context::getContext ().debug (format ("Eval {1} ↓'{2}' → ↑'{3}'", token.first, (std::string) right, (std::string) result)); - } - else if (token.second == Lexer::Type::op && - token.first == "_pos_") - { + Context::getContext().debug(format("Eval {1} ↓'{2}' → ↑'{3}'", token.first, + (std::string)right, (std::string)result)); + } else if (token.second == Lexer::Type::op && token.first == "_pos_") { // The _pos_ operator is a NOP. if (_debug) - Context::getContext ().debug (format ("[{1}] eval op {2} NOP", values.size (), token.first)); + Context::getContext().debug(format("[{1}] eval op {2} NOP", values.size(), token.first)); } // Binary operators. - else if (token.second == Lexer::Type::op) - { - if (values.size () < 2) - throw std::string ("The expression could not be evaluated."); + else if (token.second == Lexer::Type::op) { + if (values.size() < 2) throw std::string("The expression could not be evaluated."); - Variant right = values.back (); - values.pop_back (); + Variant right = values.back(); + values.pop_back(); - Variant left = values.back (); - values.pop_back (); + Variant left = values.back(); + values.pop_back(); - auto contextTask = Context::getContext ().currentTask; + auto contextTask = Context::getContext().currentTask; // Ordering these by anticipation frequency of use is a good idea. Variant result; - if (token.first == "and") result = left && right; - else if (token.first == "or") result = left || right; - else if (token.first == "&&") result = left && right; - else if (token.first == "||") result = left || right; - else if (token.first == "<") result = left < right; - else if (token.first == "<=") result = left <= right; - else if (token.first == ">") result = left > right; - else if (token.first == ">=") result = left >= right; - else if (token.first == "==") result = left.operator== (right); - else if (token.first == "!==") result = left.operator!= (right); - else if (token.first == "=") result = left.operator_partial (right); - else if (token.first == "!=") result = left.operator_nopartial (right); - else if (token.first == "+") result = left + right; - else if (token.first == "-") result = left - right; - else if (token.first == "*") result = left * right; - else if (token.first == "/") result = left / right; - else if (token.first == "^") result = left ^ right; - else if (token.first == "%") result = left % right; - else if (token.first == "xor") result = left.operator_xor (right); + if (token.first == "and") + result = left && right; + else if (token.first == "or") + result = left || right; + else if (token.first == "&&") + result = left && right; + else if (token.first == "||") + result = left || right; + else if (token.first == "<") + result = left < right; + else if (token.first == "<=") + result = left <= right; + else if (token.first == ">") + result = left > right; + else if (token.first == ">=") + result = left >= right; + else if (token.first == "==") + result = left.operator==(right); + else if (token.first == "!==") + result = left.operator!=(right); + else if (token.first == "=") + result = left.operator_partial(right); + else if (token.first == "!=") + result = left.operator_nopartial(right); + else if (token.first == "+") + result = left + right; + else if (token.first == "-") + result = left - right; + else if (token.first == "*") + result = left * right; + else if (token.first == "/") + result = left / right; + else if (token.first == "^") + result = left ^ right; + else if (token.first == "%") + result = left % right; + else if (token.first == "xor") + result = left.operator_xor(right); else if (contextTask) { - if (token.first == "~") result = left.operator_match (right, *contextTask); - else if (token.first == "!~") result = left.operator_nomatch (right, *contextTask); - else if (token.first == "_hastag_") result = left.operator_hastag (right, *contextTask); - else if (token.first == "_notag_") result = left.operator_notag (right, *contextTask); + if (token.first == "~") + result = left.operator_match(right, *contextTask); + else if (token.first == "!~") + result = left.operator_nomatch(right, *contextTask); + else if (token.first == "_hastag_") + result = left.operator_hastag(right, *contextTask); + else if (token.first == "_notag_") + result = left.operator_notag(right, *contextTask); else - throw format ("Unsupported operator '{1}'.", token.first); - } - else - throw format ("Unsupported operator '{1}'.", token.first); + throw format("Unsupported operator '{1}'.", token.first); + } else + throw format("Unsupported operator '{1}'.", token.first); - values.push_back (result); + values.push_back(result); if (_debug) - Context::getContext ().debug (format ("Eval ↓'{1}' {2} ↓'{3}' → ↑'{4}'", (std::string) left, token.first, (std::string) right, (std::string) result)); + Context::getContext().debug(format("Eval ↓'{1}' {2} ↓'{3}' → ↑'{4}'", (std::string)left, + token.first, (std::string)right, (std::string)result)); } // Literals and identifiers. - else - { - Variant v (token.first); - switch (token.second) - { - case Lexer::Type::number: - if (Lexer::isAllDigits (token.first)) - { - v.cast (Variant::type_integer); - if (_debug) - Context::getContext ().debug (format ("Eval literal number ↑'{1}'", (std::string) v)); - } - else - { - v.cast (Variant::type_real); - if (_debug) - Context::getContext ().debug (format ("Eval literal decimal ↑'{1}'", (std::string) v)); - } - break; + else { + Variant v(token.first); + switch (token.second) { + case Lexer::Type::number: + if (Lexer::isAllDigits(token.first)) { + v.cast(Variant::type_integer); + if (_debug) + Context::getContext().debug(format("Eval literal number ↑'{1}'", (std::string)v)); + } else { + v.cast(Variant::type_real); + if (_debug) + Context::getContext().debug(format("Eval literal decimal ↑'{1}'", (std::string)v)); + } + break; - case Lexer::Type::op: - throw std::string ("Operator expected."); - break; + case Lexer::Type::op: + throw std::string("Operator expected."); + break; - case Lexer::Type::dom: - case Lexer::Type::identifier: - { + case Lexer::Type::dom: + case Lexer::Type::identifier: { bool found = false; - for (const auto& source : _sources) - { - if (source (token.first, v)) - { + for (const auto& source : _sources) { + if (source(token.first, v)) { if (_debug) - Context::getContext ().debug (format ("Eval identifier source '{1}' → ↑'{2}'", token.first, (std::string) v)); + Context::getContext().debug( + format("Eval identifier source '{1}' → ↑'{2}'", token.first, (std::string)v)); found = true; break; } } // An identifier that fails lookup is a string. - if (! found) - { - v.cast (Variant::type_string); + if (!found) { + v.cast(Variant::type_string); if (_debug) - Context::getContext ().debug (format ("Eval identifier source failed '{1}'", token.first)); + Context::getContext().debug( + format("Eval identifier source failed '{1}'", token.first)); } - } - break; + } break; - case Lexer::Type::date: - v.cast (Variant::type_date); - if (_debug) - Context::getContext ().debug (format ("Eval literal date ↑'{1}'", (std::string) v)); - break; + case Lexer::Type::date: + v.cast(Variant::type_date); + if (_debug) + Context::getContext().debug(format("Eval literal date ↑'{1}'", (std::string)v)); + break; - case Lexer::Type::duration: - v.cast (Variant::type_duration); - if (_debug) - Context::getContext ().debug (format ("Eval literal duration ↑'{1}'", (std::string) v)); - break; + case Lexer::Type::duration: + v.cast(Variant::type_duration); + if (_debug) + Context::getContext().debug(format("Eval literal duration ↑'{1}'", (std::string)v)); + break; - // Nothing to do. - case Lexer::Type::string: - default: - if (_debug) - Context::getContext ().debug (format ("Eval literal string ↑'{1}'", (std::string) v)); - break; + // Nothing to do. + case Lexer::Type::string: + default: + if (_debug) + Context::getContext().debug(format("Eval literal string ↑'{1}'", (std::string)v)); + break; } - values.push_back (v); + values.push_back(v); } } // If there is more than one variant left on the stack, then the original // expression was not valid. - if (values.size () != 1) - throw std::string ("The value is not an expression."); + if (values.size() != 1) throw std::string("The value is not an expression."); result = values[0]; } @@ -428,31 +400,20 @@ void Eval::evaluatePostfixStack ( // Exponent --> Primitive ["^" Primitive] // Primitive --> "(" Logical ")" | Variant // -void Eval::infixParse ( - std::vector >& infix) const -{ +void Eval::infixParse(std::vector>& infix) const { unsigned int i = 0; - parseLogical (infix, i); + parseLogical(infix, i); } //////////////////////////////////////////////////////////////////////////////// // Logical --> Regex {( "and" | "or" | "xor" ) Regex} -bool Eval::parseLogical ( - std::vector >& infix, - unsigned int &i) const -{ - if (i < infix.size () && - parseRegex (infix, i)) - { - while (i < infix.size () && - infix[i].second == Lexer::Type::op && - (infix[i].first == "and" || - infix[i].first == "or" || - infix[i].first == "xor")) - { +bool Eval::parseLogical(std::vector>& infix, + unsigned int& i) const { + if (i < infix.size() && parseRegex(infix, i)) { + while (i < infix.size() && infix[i].second == Lexer::Type::op && + (infix[i].first == "and" || infix[i].first == "or" || infix[i].first == "xor")) { ++i; - if (! parseRegex (infix, i)) - return false; + if (!parseRegex(infix, i)) return false; } return true; @@ -463,21 +424,13 @@ bool Eval::parseLogical ( //////////////////////////////////////////////////////////////////////////////// // Regex --> Equality {( "~" | "!~" ) Equality} -bool Eval::parseRegex ( - std::vector >& infix, - unsigned int &i) const -{ - if (i < infix.size () && - parseEquality (infix, i)) - { - while (i < infix.size () && - infix[i].second == Lexer::Type::op && - (infix[i].first == "~" || - infix[i].first == "!~")) - { +bool Eval::parseRegex(std::vector>& infix, + unsigned int& i) const { + if (i < infix.size() && parseEquality(infix, i)) { + while (i < infix.size() && infix[i].second == Lexer::Type::op && + (infix[i].first == "~" || infix[i].first == "!~")) { ++i; - if (! parseEquality (infix, i)) - return false; + if (!parseEquality(infix, i)) return false; } return true; @@ -488,23 +441,14 @@ bool Eval::parseRegex ( //////////////////////////////////////////////////////////////////////////////// // Equality --> Comparative {( "==" | "=" | "!==" | "!=" ) Comparative} -bool Eval::parseEquality ( - std::vector >& infix, - unsigned int &i) const -{ - if (i < infix.size () && - parseComparative (infix, i)) - { - while (i < infix.size () && - infix[i].second == Lexer::Type::op && - (infix[i].first == "==" || - infix[i].first == "=" || - infix[i].first == "!==" || - infix[i].first == "!=")) - { +bool Eval::parseEquality(std::vector>& infix, + unsigned int& i) const { + if (i < infix.size() && parseComparative(infix, i)) { + while (i < infix.size() && infix[i].second == Lexer::Type::op && + (infix[i].first == "==" || infix[i].first == "=" || infix[i].first == "!==" || + infix[i].first == "!=")) { ++i; - if (! parseComparative (infix, i)) - return false; + if (!parseComparative(infix, i)) return false; } return true; @@ -515,23 +459,14 @@ bool Eval::parseEquality ( //////////////////////////////////////////////////////////////////////////////// // Comparative --> Arithmetic {( "<=" | "<" | ">=" | ">" ) Arithmetic} -bool Eval::parseComparative ( - std::vector >& infix, - unsigned int &i) const -{ - if (i < infix.size () && - parseArithmetic (infix, i)) - { - while (i < infix.size () && - infix[i].second == Lexer::Type::op && - (infix[i].first == "<=" || - infix[i].first == "<" || - infix[i].first == ">=" || - infix[i].first == ">")) - { +bool Eval::parseComparative(std::vector>& infix, + unsigned int& i) const { + if (i < infix.size() && parseArithmetic(infix, i)) { + while (i < infix.size() && infix[i].second == Lexer::Type::op && + (infix[i].first == "<=" || infix[i].first == "<" || infix[i].first == ">=" || + infix[i].first == ">")) { ++i; - if (! parseArithmetic (infix, i)) - return false; + if (!parseArithmetic(infix, i)) return false; } return true; @@ -542,21 +477,13 @@ bool Eval::parseComparative ( //////////////////////////////////////////////////////////////////////////////// // Arithmetic --> Geometric {( "+" | "-" ) Geometric} -bool Eval::parseArithmetic ( - std::vector >& infix, - unsigned int &i) const -{ - if (i < infix.size () && - parseGeometric (infix, i)) - { - while (i < infix.size () && - infix[i].second == Lexer::Type::op && - (infix[i].first == "+" || - infix[i].first == "-")) - { +bool Eval::parseArithmetic(std::vector>& infix, + unsigned int& i) const { + if (i < infix.size() && parseGeometric(infix, i)) { + while (i < infix.size() && infix[i].second == Lexer::Type::op && + (infix[i].first == "+" || infix[i].first == "-")) { ++i; - if (! parseGeometric (infix, i)) - return false; + if (!parseGeometric(infix, i)) return false; } return true; @@ -567,22 +494,13 @@ bool Eval::parseArithmetic ( //////////////////////////////////////////////////////////////////////////////// // Geometric --> Tag {( "*" | "/" | "%" ) Tag} -bool Eval::parseGeometric ( - std::vector >& infix, - unsigned int &i) const -{ - if (i < infix.size () && - parseTag (infix, i)) - { - while (i < infix.size () && - infix[i].second == Lexer::Type::op && - (infix[i].first == "*" || - infix[i].first == "/" || - infix[i].first == "%")) - { +bool Eval::parseGeometric(std::vector>& infix, + unsigned int& i) const { + if (i < infix.size() && parseTag(infix, i)) { + while (i < infix.size() && infix[i].second == Lexer::Type::op && + (infix[i].first == "*" || infix[i].first == "/" || infix[i].first == "%")) { ++i; - if (! parseTag (infix, i)) - return false; + if (!parseTag(infix, i)) return false; } return true; @@ -593,21 +511,13 @@ bool Eval::parseGeometric ( //////////////////////////////////////////////////////////////////////////////// // Tag --> Unary {( "_hastag_" | "_notag_" ) Unary} -bool Eval::parseTag ( - std::vector >& infix, - unsigned int &i) const -{ - if (i < infix.size () && - parseUnary (infix, i)) - { - while (i < infix.size () && - infix[i].second == Lexer::Type::op && - (infix[i].first == "_hastag_" || - infix[i].first == "_notag_")) - { +bool Eval::parseTag(std::vector>& infix, + unsigned int& i) const { + if (i < infix.size() && parseUnary(infix, i)) { + while (i < infix.size() && infix[i].second == Lexer::Type::op && + (infix[i].first == "_hastag_" || infix[i].first == "_notag_")) { ++i; - if (! parseUnary (infix, i)) - return false; + if (!parseUnary(infix, i)) return false; } return true; @@ -618,47 +528,31 @@ bool Eval::parseTag ( //////////////////////////////////////////////////////////////////////////////// // Unary --> [( "-" | "+" | "!" )] Exponent -bool Eval::parseUnary ( - std::vector >& infix, - unsigned int &i) const -{ - if (i < infix.size ()) - { - if (infix[i].first == "-") - { +bool Eval::parseUnary(std::vector>& infix, + unsigned int& i) const { + if (i < infix.size()) { + if (infix[i].first == "-") { infix[i].first = "_neg_"; ++i; - } - else if (infix[i].first == "+") - { + } else if (infix[i].first == "+") { infix[i].first = "_pos_"; ++i; - } - else if (infix[i].first == "!") - { + } else if (infix[i].first == "!") { ++i; } } - return parseExponent (infix, i); + return parseExponent(infix, i); } //////////////////////////////////////////////////////////////////////////////// // Exponent --> Primitive ["^" Primitive] -bool Eval::parseExponent ( - std::vector >& infix, - unsigned int& i) const -{ - if (i < infix.size () && - parsePrimitive (infix, i)) - { - while (i < infix.size () && - infix[i].second == Lexer::Type::op && - infix[i].first == "^") - { +bool Eval::parseExponent(std::vector>& infix, + unsigned int& i) const { + if (i < infix.size() && parsePrimitive(infix, i)) { + while (i < infix.size() && infix[i].second == Lexer::Type::op && infix[i].first == "^") { ++i; - if (! parsePrimitive (infix, i)) - return false; + if (!parsePrimitive(infix, i)) return false; } return true; @@ -669,46 +563,31 @@ bool Eval::parseExponent ( //////////////////////////////////////////////////////////////////////////////// // Primitive --> "(" Logical ")" | Variant -bool Eval::parsePrimitive ( - std::vector >& infix, - unsigned int &i) const -{ - if (i < infix.size ()) - { - if (infix[i].first == "(") - { +bool Eval::parsePrimitive(std::vector>& infix, + unsigned int& i) const { + if (i < infix.size()) { + if (infix[i].first == "(") { ++i; - if (i < infix.size () && - parseLogical (infix, i)) - { - if (i < infix.size () && - infix[i].first == ")") - { + if (i < infix.size() && parseLogical(infix, i)) { + if (i < infix.size() && infix[i].first == ")") { ++i; return true; } } - } - else - { + } else { bool found = false; - for (const auto& source : _sources) - { + for (const auto& source : _sources) { Variant v; - if (source (infix[i].first, v)) - { + if (source(infix[i].first, v)) { found = true; break; } } - if (found) - { + if (found) { ++i; return true; - } - else if (infix[i].second != Lexer::Type::op) - { + } else if (infix[i].second != Lexer::Type::op) { ++i; return true; } @@ -750,95 +629,71 @@ bool Eval::parsePrimitive ( // Pop the operator onto the output queue. // Exit. // -void Eval::infixToPostfix ( - std::vector >& infix) const -{ +void Eval::infixToPostfix(std::vector>& infix) const { // Short circuit. - if (infix.size () == 1) - return; + if (infix.size() == 1) return; // Result. - std::vector > postfix; + std::vector> postfix; // Shunting yard. - std::vector > op_stack; + std::vector> op_stack; // Operator characteristics. char type; unsigned int precedence; char associativity; - for (auto& token : infix) - { - if (token.second == Lexer::Type::op && - token.first == "(") - { - op_stack.push_back (token); - } - else if (token.second == Lexer::Type::op && - token.first == ")") - { - while (op_stack.size () && - op_stack.back ().first != "(") - { - postfix.push_back (op_stack.back ()); - op_stack.pop_back (); + for (auto& token : infix) { + if (token.second == Lexer::Type::op && token.first == "(") { + op_stack.push_back(token); + } else if (token.second == Lexer::Type::op && token.first == ")") { + while (op_stack.size() && op_stack.back().first != "(") { + postfix.push_back(op_stack.back()); + op_stack.pop_back(); } - if (op_stack.size ()) - op_stack.pop_back (); + if (op_stack.size()) + op_stack.pop_back(); else - throw std::string ("Mismatched parentheses in expression"); - } - else if (token.second == Lexer::Type::op && - identifyOperator (token.first, type, precedence, associativity)) - { + throw std::string("Mismatched parentheses in expression"); + } else if (token.second == Lexer::Type::op && + identifyOperator(token.first, type, precedence, associativity)) { char type2; unsigned int precedence2; char associativity2; - while (op_stack.size () > 0 && - identifyOperator (op_stack.back ().first, type2, precedence2, associativity2) && + while (op_stack.size() > 0 && + identifyOperator(op_stack.back().first, type2, precedence2, associativity2) && ((associativity == 'l' && precedence <= precedence2) || - (associativity == 'r' && precedence < precedence2))) - { - postfix.push_back (op_stack.back ()); - op_stack.pop_back (); + (associativity == 'r' && precedence < precedence2))) { + postfix.push_back(op_stack.back()); + op_stack.pop_back(); } - op_stack.push_back (token); - } - else - { - postfix.push_back (token); + op_stack.push_back(token); + } else { + postfix.push_back(token); } } - while (op_stack.size ()) - { - if (op_stack.back ().first == "(" || - op_stack.back ().first == ")") - throw std::string ("Mismatched parentheses in expression"); + while (op_stack.size()) { + if (op_stack.back().first == "(" || op_stack.back().first == ")") + throw std::string("Mismatched parentheses in expression"); - postfix.push_back (op_stack.back ()); - op_stack.pop_back (); + postfix.push_back(op_stack.back()); + op_stack.pop_back(); } infix = postfix; } //////////////////////////////////////////////////////////////////////////////// -bool Eval::identifyOperator ( - const std::string& op, - char& type, - unsigned int& precedence, - char& associativity) const -{ - for (const auto& opr : operators) - { - if (opr.op == op) - { - type = opr.type; - precedence = opr.precedence; +bool Eval::identifyOperator(const std::string& op, char& type, unsigned int& precedence, + char& associativity) const { + for (const auto& opr : operators) { + if (opr.op == op) { + type = opr.type; + precedence = opr.precedence; associativity = opr.associativity; return true; } @@ -848,33 +703,29 @@ bool Eval::identifyOperator ( } //////////////////////////////////////////////////////////////////////////////// -std::string Eval::dump ( - std::vector >& tokens) const -{ +std::string Eval::dump(std::vector>& tokens) const { // Set up a color mapping. - std::map color_map; - color_map[Lexer::Type::op] = Color ("gray14 on gray6"); - color_map[Lexer::Type::number] = Color ("rgb530 on gray6"); - color_map[Lexer::Type::hex] = Color ("rgb303 on gray6"); - color_map[Lexer::Type::string] = Color ("rgb550 on gray6"); - color_map[Lexer::Type::dom] = Color ("rgb045 on gray6"); - color_map[Lexer::Type::identifier] = Color ("rgb035 on gray6"); - color_map[Lexer::Type::date] = Color ("rgb150 on gray6"); - color_map[Lexer::Type::duration] = Color ("rgb531 on gray6"); + std::map color_map; + color_map[Lexer::Type::op] = Color("gray14 on gray6"); + color_map[Lexer::Type::number] = Color("rgb530 on gray6"); + color_map[Lexer::Type::hex] = Color("rgb303 on gray6"); + color_map[Lexer::Type::string] = Color("rgb550 on gray6"); + color_map[Lexer::Type::dom] = Color("rgb045 on gray6"); + color_map[Lexer::Type::identifier] = Color("rgb035 on gray6"); + color_map[Lexer::Type::date] = Color("rgb150 on gray6"); + color_map[Lexer::Type::duration] = Color("rgb531 on gray6"); std::string output; - for (auto i = tokens.begin (); i != tokens.end (); ++i) - { - if (i != tokens.begin ()) - output += ' '; + for (auto i = tokens.begin(); i != tokens.end(); ++i) { + if (i != tokens.begin()) output += ' '; Color c; - if (color_map[i->second].nontrivial ()) + if (color_map[i->second].nontrivial()) c = color_map[i->second]; else - c = Color ("rgb000 on gray6"); + c = Color("rgb000 on gray6"); - output += c.colorize (i->first); + output += c.colorize(i->first); } return output; diff --git a/src/Eval.h b/src/Eval.h index 190d357b1..3203e8a15 100644 --- a/src/Eval.h +++ b/src/Eval.h @@ -27,50 +27,51 @@ #ifndef INCLUDED_EVAL #define INCLUDED_EVAL -#include -#include #include #include -bool domSource (const std::string&, Variant&); +#include +#include -class Eval -{ -public: - Eval (); +bool domSource(const std::string &, Variant &); - void addSource (bool (*fn)(const std::string&, Variant&)); - void evaluateInfixExpression (const std::string&, Variant&) const; - void evaluatePostfixExpression (const std::string&, Variant&) const; - void compileExpression (const std::vector >&); - void evaluateCompiledExpression (Variant&); - void debug (bool); +class Eval { + public: + Eval(); - static std::vector getOperators (); - static std::vector getBinaryOperators (); + void addSource(bool (*fn)(const std::string &, Variant &)); + void evaluateInfixExpression(const std::string &, Variant &) const; + void evaluatePostfixExpression(const std::string &, Variant &) const; + void compileExpression(const std::vector> &); + void evaluateCompiledExpression(Variant &); + void debug(bool); -private: - void evaluatePostfixStack (const std::vector >&, Variant&) const; - void infixToPostfix (std::vector >&) const; - void infixParse (std::vector >&) const; - bool parseLogical (std::vector >&, unsigned int &) const; - bool parseRegex (std::vector >&, unsigned int &) const; - bool parseEquality (std::vector >&, unsigned int &) const; - bool parseComparative (std::vector >&, unsigned int &) const; - bool parseArithmetic (std::vector >&, unsigned int &) const; - bool parseGeometric (std::vector >&, unsigned int &) const; - bool parseTag (std::vector >&, unsigned int &) const; - bool parseUnary (std::vector >&, unsigned int &) const; - bool parseExponent (std::vector >&, unsigned int &) const; - bool parsePrimitive (std::vector >&, unsigned int &) const; - bool identifyOperator (const std::string&, char&, unsigned int&, char&) const; + static std::vector getOperators(); + static std::vector getBinaryOperators(); - std::string dump (std::vector >&) const; + private: + void evaluatePostfixStack(const std::vector> &, + Variant &) const; + void infixToPostfix(std::vector> &) const; + void infixParse(std::vector> &) const; + bool parseLogical(std::vector> &, unsigned int &) const; + bool parseRegex(std::vector> &, unsigned int &) const; + bool parseEquality(std::vector> &, unsigned int &) const; + bool parseComparative(std::vector> &, unsigned int &) const; + bool parseArithmetic(std::vector> &, unsigned int &) const; + bool parseGeometric(std::vector> &, unsigned int &) const; + bool parseTag(std::vector> &, unsigned int &) const; + bool parseUnary(std::vector> &, unsigned int &) const; + bool parseExponent(std::vector> &, unsigned int &) const; + bool parsePrimitive(std::vector> &, unsigned int &) const; + bool identifyOperator(const std::string &, char &, unsigned int &, char &) const; -private: - std::vector _sources {}; - bool _debug {false}; - std::vector > _compiled {}; + std::string dump(std::vector> &) const; + + private: + std::vector _sources{}; + bool _debug{false}; + std::vector> _compiled{}; }; #endif diff --git a/src/Filter.cpp b/src/Filter.cpp index e6ee0f550..0de77e88d 100644 --- a/src/Filter.cpp +++ b/src/Filter.cpp @@ -27,145 +27,130 @@ #include // cmake.h include header must come first -#include -#include #include -#include #include #include +#include +#include #include #include #include +#include + //////////////////////////////////////////////////////////////////////////////// // Take an input set of tasks and filter into a subset. -void Filter::subset (const std::vector & input, std::vector & output) -{ +void Filter::subset(const std::vector& input, std::vector& output) { Timer timer; - _startCount = (int) input.size (); + _startCount = (int)input.size(); - Context::getContext ().cli2.prepareFilter (); + Context::getContext().cli2.prepareFilter(); - std::vector > precompiled; - for (auto& a : Context::getContext ().cli2._args) - if (a.hasTag ("FILTER")) - precompiled.emplace_back (a.getToken (), a._lextype); + std::vector> precompiled; + for (auto& a : Context::getContext().cli2._args) + if (a.hasTag("FILTER")) precompiled.emplace_back(a.getToken(), a._lextype); - if (precompiled.size ()) - { + if (precompiled.size()) { Eval eval; - eval.addSource (domSource); + eval.addSource(domSource); // Debug output from Eval during compilation is useful. During evaluation // it is mostly noise. - eval.debug (Context::getContext ().config.getInteger ("debug.parser") >= 3 ? true : false); - eval.compileExpression (precompiled); + eval.debug(Context::getContext().config.getInteger("debug.parser") >= 3 ? true : false); + eval.compileExpression(precompiled); - for (auto& task : input) - { + for (auto& task : input) { // Set up context for any DOM references. - auto currentTask = Context::getContext ().withCurrentTask(&task); + auto currentTask = Context::getContext().withCurrentTask(&task); Variant var; - eval.evaluateCompiledExpression (var); - if (var.get_bool ()) - output.push_back (task); + eval.evaluateCompiledExpression(var); + if (var.get_bool()) output.push_back(task); } - eval.debug (false); - } - else + eval.debug(false); + } else output = input; - _endCount = (int) output.size (); - Context::getContext ().debug (format ("Filtered {1} tasks --> {2} tasks [list subset]", _startCount, _endCount)); - Context::getContext ().time_filter_us += timer.total_us (); + _endCount = (int)output.size(); + Context::getContext().debug( + format("Filtered {1} tasks --> {2} tasks [list subset]", _startCount, _endCount)); + Context::getContext().time_filter_us += timer.total_us(); } //////////////////////////////////////////////////////////////////////////////// // Take the set of all tasks and filter into a subset. -void Filter::subset (std::vector & output) -{ +void Filter::subset(std::vector& output) { Timer timer; - Context::getContext ().cli2.prepareFilter (); + Context::getContext().cli2.prepareFilter(); - std::vector > precompiled; - for (auto& a : Context::getContext ().cli2._args) - if (a.hasTag ("FILTER")) - precompiled.emplace_back (a.getToken (), a._lextype); + std::vector> precompiled; + for (auto& a : Context::getContext().cli2._args) + if (a.hasTag("FILTER")) precompiled.emplace_back(a.getToken(), a._lextype); // Shortcut indicates that only pending.data needs to be loaded. bool shortcut = false; - if (precompiled.size ()) - { + if (precompiled.size()) { Timer timer_pending; - auto pending = Context::getContext ().tdb2.pending_tasks (); - Context::getContext ().time_filter_us -= timer_pending.total_us (); - _startCount = (int) pending.size (); + auto pending = Context::getContext().tdb2.pending_tasks(); + Context::getContext().time_filter_us -= timer_pending.total_us(); + _startCount = (int)pending.size(); Eval eval; - eval.addSource (domSource); + eval.addSource(domSource); // Debug output from Eval during compilation is useful. During evaluation // it is mostly noise. - eval.debug (Context::getContext ().config.getInteger ("debug.parser") >= 3 ? true : false); - eval.compileExpression (precompiled); + eval.debug(Context::getContext().config.getInteger("debug.parser") >= 3 ? true : false); + eval.compileExpression(precompiled); - output.clear (); - for (auto& task : pending) - { + output.clear(); + for (auto& task : pending) { // Set up context for any DOM references. - auto currentTask = Context::getContext ().withCurrentTask(&task); + auto currentTask = Context::getContext().withCurrentTask(&task); Variant var; - eval.evaluateCompiledExpression (var); - if (var.get_bool ()) - output.push_back (task); + eval.evaluateCompiledExpression(var); + if (var.get_bool()) output.push_back(task); } - shortcut = pendingOnly (); - if (! shortcut) - { + shortcut = pendingOnly(); + if (!shortcut) { Timer timer_completed; - auto completed = Context::getContext ().tdb2.completed_tasks (); - Context::getContext ().time_filter_us -= timer_completed.total_us (); - _startCount += (int) completed.size (); + auto completed = Context::getContext().tdb2.completed_tasks(); + Context::getContext().time_filter_us -= timer_completed.total_us(); + _startCount += (int)completed.size(); - for (auto& task : completed) - { + for (auto& task : completed) { // Set up context for any DOM references. - auto currentTask = Context::getContext ().withCurrentTask(&task); + auto currentTask = Context::getContext().withCurrentTask(&task); Variant var; - eval.evaluateCompiledExpression (var); - if (var.get_bool ()) - output.push_back (task); + eval.evaluateCompiledExpression(var); + if (var.get_bool()) output.push_back(task); } } - eval.debug (false); - } - else - { - safety (); + eval.debug(false); + } else { + safety(); Timer pending_completed; - output = Context::getContext ().tdb2.all_tasks (); - Context::getContext ().time_filter_us -= pending_completed.total_us (); + output = Context::getContext().tdb2.all_tasks(); + Context::getContext().time_filter_us -= pending_completed.total_us(); } - _endCount = (int) output.size (); - Context::getContext ().debug (format ("Filtered {1} tasks --> {2} tasks [{3}]", _startCount, _endCount, (shortcut ? "pending only" : "all tasks"))); - Context::getContext ().time_filter_us += timer.total_us (); + _endCount = (int)output.size(); + Context::getContext().debug(format("Filtered {1} tasks --> {2} tasks [{3}]", _startCount, + _endCount, (shortcut ? "pending only" : "all tasks"))); + Context::getContext().time_filter_us += timer.total_us(); } //////////////////////////////////////////////////////////////////////////////// -bool Filter::hasFilter () const -{ - for (const auto& a : Context::getContext ().cli2._args) - if (a.hasTag ("FILTER")) - return true; +bool Filter::hasFilter() const { + for (const auto& a : Context::getContext().cli2._args) + if (a.hasTag("FILTER")) return true; return false; } @@ -174,11 +159,9 @@ bool Filter::hasFilter () const // If the filter contains no 'or', 'xor' or 'not' operators, and only includes // status values 'pending', 'waiting' or 'recurring', then the filter is // guaranteed to only need data from pending.data. -bool Filter::pendingOnly () const -{ +bool Filter::pendingOnly() const { // When GC is off, there are no shortcuts. - if (! Context::getContext ().config.getBoolean ("gc")) - return false; + if (!Context::getContext().config.getBoolean("gc")) return false; // To skip loading completed.data, there should be: // - 'status' in filter @@ -186,61 +169,51 @@ bool Filter::pendingOnly () const // - no 'deleted' // - no 'xor' // - no 'or' - int countStatus = 0; - int countPending = 0; - int countWaiting = 0; + int countStatus = 0; + int countPending = 0; + int countWaiting = 0; int countRecurring = 0; - int countId = (int) Context::getContext ().cli2._id_ranges.size (); - int countUUID = (int) Context::getContext ().cli2._uuid_list.size (); - int countOr = 0; - int countXor = 0; - int countNot = 0; + int countId = (int)Context::getContext().cli2._id_ranges.size(); + int countUUID = (int)Context::getContext().cli2._uuid_list.size(); + int countOr = 0; + int countXor = 0; + int countNot = 0; bool pendingTag = false; - bool activeTag = false; + bool activeTag = false; - for (const auto& a : Context::getContext ().cli2._args) - { - if (a.hasTag ("FILTER")) - { - std::string raw = a.attribute ("raw"); - std::string canonical = a.attribute ("canonical"); + for (const auto& a : Context::getContext().cli2._args) { + if (a.hasTag("FILTER")) { + std::string raw = a.attribute("raw"); + std::string canonical = a.attribute("canonical"); - if (a._lextype == Lexer::Type::op && raw == "or") ++countOr; - if (a._lextype == Lexer::Type::op && raw == "xor") ++countXor; - if (a._lextype == Lexer::Type::op && raw == "not") ++countNot; + if (a._lextype == Lexer::Type::op && raw == "or") ++countOr; + if (a._lextype == Lexer::Type::op && raw == "xor") ++countXor; + if (a._lextype == Lexer::Type::op && raw == "not") ++countNot; if (a._lextype == Lexer::Type::dom && canonical == "status") ++countStatus; - if ( raw == "pending") ++countPending; - if ( raw == "waiting") ++countWaiting; - if ( raw == "recurring") ++countRecurring; + if (raw == "pending") ++countPending; + if (raw == "waiting") ++countWaiting; + if (raw == "recurring") ++countRecurring; } } - for (const auto& word : Context::getContext ().cli2._original_args) - { - if (word.attribute ("raw") == "+PENDING") pendingTag = true; - if (word.attribute ("raw") == "+ACTIVE") activeTag = true; + for (const auto& word : Context::getContext().cli2._original_args) { + if (word.attribute("raw") == "+PENDING") pendingTag = true; + if (word.attribute("raw") == "+ACTIVE") activeTag = true; } + if (countUUID) return false; - if (countUUID) - return false; + if (countOr || countXor || countNot) return false; - if (countOr || countXor || countNot) - return false; + if (pendingTag || activeTag) return true; - if (pendingTag || activeTag) - return true; - - if (countStatus) - { - if (!countPending && !countWaiting && !countRecurring) - return false; + if (countStatus) { + if (!countPending && !countWaiting && !countRecurring) return false; return true; } - if (countId) - return true; + if (countId) return true; return false; } @@ -248,43 +221,35 @@ bool Filter::pendingOnly () const //////////////////////////////////////////////////////////////////////////////// // Disaster avoidance mechanism. If a !READONLY has no filter, then it can cause // all tasks to be modified. This is usually not intended. -void Filter::safety () const -{ - if (_safety) - { +void Filter::safety() const { + if (_safety) { bool readonly = true; bool filter = false; - for (const auto& a : Context::getContext ().cli2._args) - { - if (a.hasTag ("CMD") && - ! a.hasTag ("READONLY")) - readonly = false; + for (const auto& a : Context::getContext().cli2._args) { + if (a.hasTag("CMD") && !a.hasTag("READONLY")) readonly = false; - if (a.hasTag ("FILTER")) - filter = true; + if (a.hasTag("FILTER")) filter = true; } - if (! readonly && - ! filter) - { - if (! Context::getContext ().config.getBoolean ("allow.empty.filter")) - throw std::string ("You did not specify a filter, and with the 'allow.empty.filter' value, no action is taken."); + if (!readonly && !filter) { + if (!Context::getContext().config.getBoolean("allow.empty.filter")) + throw std::string( + "You did not specify a filter, and with the 'allow.empty.filter' value, no action is " + "taken."); // If user is willing to be asked, this can be avoided. - if (Context::getContext ().config.getBoolean ("confirmation") && - confirm ("This command has no filter, and will modify all (including completed and deleted) tasks. Are you sure?")) + if (Context::getContext().config.getBoolean("confirmation") && + confirm("This command has no filter, and will modify all (including completed and " + "deleted) tasks. Are you sure?")) return; // Sound the alarm. - throw std::string ("Command prevented from running."); + throw std::string("Command prevented from running."); } } } //////////////////////////////////////////////////////////////////////////////// -void Filter::disableSafety () -{ - _safety = false; -} +void Filter::disableSafety() { _safety = false; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Filter.h b/src/Filter.h index da15c68b3..2c47da6c4 100644 --- a/src/Filter.h +++ b/src/Filter.h @@ -27,28 +27,27 @@ #ifndef INCLUDED_FILTER #define INCLUDED_FILTER -#include -#include #include #include -class Filter -{ -public: - Filter () = default; +#include +#include - void subset (const std::vector &, std::vector &); - void subset (std::vector &); - bool hasFilter () const; - bool pendingOnly () const; - void safety () const; - void disableSafety (); +class Filter { + public: + Filter() = default; -private: - int _startCount {0}; - int _endCount {0}; - bool _safety {true}; + void subset(const std::vector&, std::vector&); + void subset(std::vector&); + bool hasFilter() const; + bool pendingOnly() const; + void safety() const; + void disableSafety(); + + private: + int _startCount{0}; + int _endCount{0}; + bool _safety{true}; }; #endif - diff --git a/src/Hooks.cpp b/src/Hooks.cpp index c0fdc056d..a8ad76b82 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -28,90 +28,83 @@ // cmake.h include header must come first #include + #include // If is included, put it after , because it includes // , and therefore would ignore the _WITH_GETLINE. #ifdef FREEBSD #define _WITH_GETLINE #endif -#include -#include -#include -#include #include -#include #include -#include -#include -#include #include +#include +#include +#include +#include #include #include +#include +#include +#include +#include #include -#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected from hook script: {1}" -#define STRING_HOOK_ERROR_NODESC "Hook Error: JSON Object missing 'description' attribute from hook script: {1}" -#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute from hook script: {1}" -#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}" -#define STRING_HOOK_ERROR_JSON "Hook Error: JSON " -#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: " -#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}, in hook script: {3}" -#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}, in hook script: {2}" -#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}, in hook script: {3}" +#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected from hook script: {1}" +#define STRING_HOOK_ERROR_NODESC \ + "Hook Error: JSON Object missing 'description' attribute from hook script: {1}" +#define STRING_HOOK_ERROR_NOUUID \ + "Hook Error: JSON Object missing 'uuid' attribute from hook script: {1}" +#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}" +#define STRING_HOOK_ERROR_JSON "Hook Error: JSON " +#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: " +#define STRING_HOOK_ERROR_BAD_NUM \ + "Hook Error: Expected {1} JSON task(s), found {2}, in hook script: {3}" +#define STRING_HOOK_ERROR_SAME1 \ + "Hook Error: JSON must be for the same task: {1}, in hook script: {2}" +#define STRING_HOOK_ERROR_SAME2 \ + "Hook Error: JSON must be for the same task: {1} != {2}, in hook script: {3}" #define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from failing hook script: {1}" //////////////////////////////////////////////////////////////////////////////// -void Hooks::initialize () -{ - _debug = Context::getContext ().config.getInteger ("debug.hooks"); +void Hooks::initialize() { + _debug = Context::getContext().config.getInteger("debug.hooks"); // Scan // /hooks Directory d; - if (Context::getContext ().config.has ("hooks.location")) - { - d = Directory (Context::getContext ().config.get ("hooks.location")); - } - else - { - d = Directory (Context::getContext ().config.get ("data.location")); + if (Context::getContext().config.has("hooks.location")) { + d = Directory(Context::getContext().config.get("hooks.location")); + } else { + d = Directory(Context::getContext().config.get("data.location")); d += "hooks"; } - if (d.is_directory () && - d.readable ()) - { - _scripts = d.list (); - std::sort (_scripts.begin (), _scripts.end ()); + if (d.is_directory() && d.readable()) { + _scripts = d.list(); + std::sort(_scripts.begin(), _scripts.end()); - if (_debug >= 1) - { - for (auto& i : _scripts) - { - Path p (i); - if (! p.is_directory ()) - { - std::string name = p.name (); - if (name.substr (0, 6) == "on-add" || - name.substr (0, 9) == "on-modify" || - name.substr (0, 9) == "on-launch" || - name.substr (0, 7) == "on-exit") - Context::getContext ().debug ("Found hook script " + i); + if (_debug >= 1) { + for (auto& i : _scripts) { + Path p(i); + if (!p.is_directory()) { + std::string name = p.name(); + if (name.substr(0, 6) == "on-add" || name.substr(0, 9) == "on-modify" || + name.substr(0, 9) == "on-launch" || name.substr(0, 7) == "on-exit") + Context::getContext().debug("Found hook script " + i); else - Context::getContext ().debug ("Found misnamed hook script " + i); + Context::getContext().debug("Found misnamed hook script " + i); } } } - } - else if (_debug >= 1) - Context::getContext ().debug ("Hook directory not readable: " + d._data); + } else if (_debug >= 1) + Context::getContext().debug("Hook directory not readable: " + d._data); - _enabled = Context::getContext ().config.getBoolean ("hooks"); + _enabled = Context::getContext().config.getBoolean("hooks"); } //////////////////////////////////////////////////////////////////////////////// -bool Hooks::enable (bool value) -{ +bool Hooks::enable(bool value) { bool old_value = _enabled; _enabled = value; return old_value; @@ -129,45 +122,36 @@ bool Hooks::enable (bool value) // - all emitted non-JSON lines are considered feedback or error messages // depending on the status code. // -void Hooks::onLaunch () const -{ - if (! _enabled) - return; +void Hooks::onLaunch() const { + if (!_enabled) return; Timer timer; - std::vector matchingScripts = scripts ("on-launch"); - if (matchingScripts.size ()) - { - for (auto& script : matchingScripts) - { - std::vector input; - std::vector output; - int status = callHookScript (script, input, output); + std::vector matchingScripts = scripts("on-launch"); + if (matchingScripts.size()) { + for (auto& script : matchingScripts) { + std::vector input; + std::vector output; + int status = callHookScript(script, input, output); - std::vector outputJSON; - std::vector outputFeedback; - separateOutput (output, outputJSON, outputFeedback); + std::vector outputJSON; + std::vector outputFeedback; + separateOutput(output, outputJSON, outputFeedback); - assertNTasks (outputJSON, 0, script); + assertNTasks(outputJSON, 0, script); - if (status == 0) - { - for (auto& message : outputFeedback) - Context::getContext ().footnote (message); - } - else - { - assertFeedback (outputFeedback, script); - for (auto& message : outputFeedback) - Context::getContext ().error (message); + if (status == 0) { + for (auto& message : outputFeedback) Context::getContext().footnote(message); + } else { + assertFeedback(outputFeedback, script); + for (auto& message : outputFeedback) Context::getContext().error(message); throw 0; // This is how hooks silently terminate processing. } } } - Context::getContext ().time_hooks_us += timer.total_us (); + Context::getContext().time_hooks_us += timer.total_us(); } //////////////////////////////////////////////////////////////////////////////// @@ -182,55 +166,45 @@ void Hooks::onLaunch () const // - all emitted non-JSON lines are considered feedback or error messages // depending on the status code. // -void Hooks::onExit () const -{ - if (! _enabled) - return; +void Hooks::onExit() const { + if (!_enabled) return; Timer timer; - std::vector matchingScripts = scripts ("on-exit"); - if (matchingScripts.size ()) - { + std::vector matchingScripts = scripts("on-exit"); + if (matchingScripts.size()) { // Get the set of changed tasks. - std::vector tasks; - Context::getContext ().tdb2.get_changes (tasks); + std::vector tasks; + Context::getContext().tdb2.get_changes(tasks); // Convert to a vector of strings. - std::vector input; + std::vector input; input.reserve(tasks.size()); - for (auto& t : tasks) - input.push_back (t.composeJSON ()); + for (auto& t : tasks) input.push_back(t.composeJSON()); // Call the hook scripts, with the invariant input. - for (auto& script : matchingScripts) - { - std::vector output; - int status = callHookScript (script, input, output); + for (auto& script : matchingScripts) { + std::vector output; + int status = callHookScript(script, input, output); - std::vector outputJSON; - std::vector outputFeedback; - separateOutput (output, outputJSON, outputFeedback); + std::vector outputJSON; + std::vector outputFeedback; + separateOutput(output, outputJSON, outputFeedback); - assertNTasks (outputJSON, 0, script); + assertNTasks(outputJSON, 0, script); - if (status == 0) - { - for (auto& message : outputFeedback) - Context::getContext ().footnote (message); - } - else - { - assertFeedback (outputFeedback, script); - for (auto& message : outputFeedback) - Context::getContext ().error (message); + if (status == 0) { + for (auto& message : outputFeedback) Context::getContext().footnote(message); + } else { + assertFeedback(outputFeedback, script); + for (auto& message : outputFeedback) Context::getContext().error(message); throw 0; // This is how hooks silently terminate processing. } } } - Context::getContext ().time_hooks_us += timer.total_us (); + Context::getContext().time_hooks_us += timer.total_us(); } //////////////////////////////////////////////////////////////////////////////// @@ -245,57 +219,48 @@ void Hooks::onExit () const // - all emitted non-JSON lines are considered feedback or error messages // depending on the status code. // -void Hooks::onAdd (Task& task) const -{ - if (! _enabled) - return; +void Hooks::onAdd(Task& task) const { + if (!_enabled) return; Timer timer; - std::vector matchingScripts = scripts ("on-add"); - if (matchingScripts.size ()) - { + std::vector matchingScripts = scripts("on-add"); + if (matchingScripts.size()) { // Convert task to a vector of strings. - std::vector input; - input.push_back (task.composeJSON ()); + std::vector input; + input.push_back(task.composeJSON()); // Call the hook scripts. - for (auto& script : matchingScripts) - { - std::vector output; - int status = callHookScript (script, input, output); + for (auto& script : matchingScripts) { + std::vector output; + int status = callHookScript(script, input, output); - std::vector outputJSON; - std::vector outputFeedback; - separateOutput (output, outputJSON, outputFeedback); + std::vector outputJSON; + std::vector outputFeedback; + separateOutput(output, outputJSON, outputFeedback); - if (status == 0) - { - assertNTasks (outputJSON, 1, script); - assertValidJSON (outputJSON, script); - assertSameTask (outputJSON, task, script); + if (status == 0) { + assertNTasks(outputJSON, 1, script); + assertValidJSON(outputJSON, script); + assertSameTask(outputJSON, task, script); // Propagate forward to the next script. input[0] = outputJSON[0]; - for (auto& message : outputFeedback) - Context::getContext ().footnote (message); - } - else - { - assertFeedback (outputFeedback, script); - for (auto& message : outputFeedback) - Context::getContext ().error (message); + for (auto& message : outputFeedback) Context::getContext().footnote(message); + } else { + assertFeedback(outputFeedback, script); + for (auto& message : outputFeedback) Context::getContext().error(message); throw 0; // This is how hooks silently terminate processing. } } // Transfer the modified task back to the original task. - task = Task (input[0]); + task = Task(input[0]); } - Context::getContext ().time_hooks_us += timer.total_us (); + Context::getContext().time_hooks_us += timer.total_us(); } //////////////////////////////////////////////////////////////////////////////// @@ -311,76 +276,60 @@ void Hooks::onAdd (Task& task) const // - all emitted non-JSON lines are considered feedback or error messages // depending on the status code. // -void Hooks::onModify (const Task& before, Task& after) const -{ - if (! _enabled) - return; +void Hooks::onModify(const Task& before, Task& after) const { + if (!_enabled) return; Timer timer; - std::vector matchingScripts = scripts ("on-modify"); - if (matchingScripts.size ()) - { + std::vector matchingScripts = scripts("on-modify"); + if (matchingScripts.size()) { // Convert vector of tasks to a vector of strings. - std::vector input; - input.push_back (before.composeJSON ()); // [line 0] original, never changes - input.push_back (after.composeJSON ()); // [line 1] modified + std::vector input; + input.push_back(before.composeJSON()); // [line 0] original, never changes + input.push_back(after.composeJSON()); // [line 1] modified // Call the hook scripts. - for (auto& script : matchingScripts) - { - std::vector output; - int status = callHookScript (script, input, output); + for (auto& script : matchingScripts) { + std::vector output; + int status = callHookScript(script, input, output); - std::vector outputJSON; - std::vector outputFeedback; - separateOutput (output, outputJSON, outputFeedback); + std::vector outputJSON; + std::vector outputFeedback; + separateOutput(output, outputJSON, outputFeedback); - if (status == 0) - { - assertNTasks (outputJSON, 1, script); - assertValidJSON (outputJSON, script); - assertSameTask (outputJSON, before, script); + if (status == 0) { + assertNTasks(outputJSON, 1, script); + assertValidJSON(outputJSON, script); + assertSameTask(outputJSON, before, script); // Propagate accepted changes forward to the next script. input[1] = outputJSON[0]; - for (auto& message : outputFeedback) - Context::getContext ().footnote (message); - } - else - { - assertFeedback (outputFeedback, script); - for (auto& message : outputFeedback) - Context::getContext ().error (message); + for (auto& message : outputFeedback) Context::getContext().footnote(message); + } else { + assertFeedback(outputFeedback, script); + for (auto& message : outputFeedback) Context::getContext().error(message); throw 0; // This is how hooks silently terminate processing. } } - after = Task (input[1]); + after = Task(input[1]); } - Context::getContext ().time_hooks_us += timer.total_us (); + Context::getContext().time_hooks_us += timer.total_us(); } //////////////////////////////////////////////////////////////////////////////// -std::vector Hooks::list () const -{ - return _scripts; -} +std::vector Hooks::list() const { return _scripts; } //////////////////////////////////////////////////////////////////////////////// -std::vector Hooks::scripts (const std::string& event) const -{ - std::vector matching; - for (const auto& i : _scripts) - { - if (i.find ("/" + event) != std::string::npos) - { - File script (i); - if (script.executable ()) - matching.push_back (i); +std::vector Hooks::scripts(const std::string& event) const { + std::vector matching; + for (const auto& i : _scripts) { + if (i.find("/" + event) != std::string::npos) { + File script(i); + if (script.executable()) matching.push_back(i); } } @@ -388,124 +337,95 @@ std::vector Hooks::scripts (const std::string& event) const } //////////////////////////////////////////////////////////////////////////////// -void Hooks::separateOutput ( - const std::vector & output, - std::vector & json, - std::vector & feedback) const -{ - for (auto& i : output) - { - if (isJSON (i)) - json.push_back (i); +void Hooks::separateOutput(const std::vector& output, std::vector& json, + std::vector& feedback) const { + for (auto& i : output) { + if (isJSON(i)) + json.push_back(i); else - feedback.push_back (i); + feedback.push_back(i); } } //////////////////////////////////////////////////////////////////////////////// -bool Hooks::isJSON (const std::string& input) const -{ - return input.length () > 2 && - input[0] == '{' && - input[input.length () - 1] == '}'; +bool Hooks::isJSON(const std::string& input) const { + return input.length() > 2 && input[0] == '{' && input[input.length() - 1] == '}'; } //////////////////////////////////////////////////////////////////////////////// -void Hooks::assertValidJSON ( - const std::vector & input, - const std::string& script) const -{ - for (auto& i : input) - { - if (i.length () < 3 || - i[0] != '{' || - i[i.length () - 1] != '}') - { - Context::getContext ().error (format (STRING_HOOK_ERROR_OBJECT, Path (script).name ())); +void Hooks::assertValidJSON(const std::vector& input, + const std::string& script) const { + for (auto& i : input) { + if (i.length() < 3 || i[0] != '{' || i[i.length() - 1] != '}') { + Context::getContext().error(format(STRING_HOOK_ERROR_OBJECT, Path(script).name())); throw 0; } - try - { - json::value* root = json::parse (i); - if (root->type () != json::j_object) - { - Context::getContext ().error (format (STRING_HOOK_ERROR_OBJECT, Path (script).name ())); + try { + json::value* root = json::parse(i); + if (root->type() != json::j_object) { + Context::getContext().error(format(STRING_HOOK_ERROR_OBJECT, Path(script).name())); throw 0; } - if (((json::object*)root)->_data.find ("description") == ((json::object*)root)->_data.end ()) - { - Context::getContext ().error (format (STRING_HOOK_ERROR_NODESC, Path (script).name ())); + if (((json::object*)root)->_data.find("description") == ((json::object*)root)->_data.end()) { + Context::getContext().error(format(STRING_HOOK_ERROR_NODESC, Path(script).name())); throw 0; } - if (((json::object*)root)->_data.find ("uuid") == ((json::object*)root)->_data.end ()) - { - Context::getContext ().error (format (STRING_HOOK_ERROR_NOUUID, Path (script).name ())); + if (((json::object*)root)->_data.find("uuid") == ((json::object*)root)->_data.end()) { + Context::getContext().error(format(STRING_HOOK_ERROR_NOUUID, Path(script).name())); throw 0; } delete root; } - catch (const std::string& e) - { - Context::getContext ().error (format (STRING_HOOK_ERROR_SYNTAX, i)); - if (_debug) - Context::getContext ().error (STRING_HOOK_ERROR_JSON + e); + catch (const std::string& e) { + Context::getContext().error(format(STRING_HOOK_ERROR_SYNTAX, i)); + if (_debug) Context::getContext().error(STRING_HOOK_ERROR_JSON + e); throw 0; } - catch (...) - { - Context::getContext ().error (STRING_HOOK_ERROR_NOPARSE + i); + catch (...) { + Context::getContext().error(STRING_HOOK_ERROR_NOPARSE + i); throw 0; } } } //////////////////////////////////////////////////////////////////////////////// -void Hooks::assertNTasks ( - const std::vector & input, - unsigned int n, - const std::string& script) const -{ - if (input.size () != n) - { - Context::getContext ().error (format (STRING_HOOK_ERROR_BAD_NUM, n, (int) input.size (), Path (script).name ())); +void Hooks::assertNTasks(const std::vector& input, unsigned int n, + const std::string& script) const { + if (input.size() != n) { + Context::getContext().error( + format(STRING_HOOK_ERROR_BAD_NUM, n, (int)input.size(), Path(script).name())); throw 0; } } //////////////////////////////////////////////////////////////////////////////// -void Hooks::assertSameTask ( - const std::vector & input, - const Task& task, - const std::string& script) const -{ - std::string uuid = task.get ("uuid"); +void Hooks::assertSameTask(const std::vector& input, const Task& task, + const std::string& script) const { + std::string uuid = task.get("uuid"); - for (auto& i : input) - { - auto root_obj = (json::object*)json::parse (i); + for (auto& i : input) { + auto root_obj = (json::object*)json::parse(i); // If there is no UUID at all. - auto u = root_obj->_data.find ("uuid"); - if (u == root_obj->_data.end () || - u->second->type () != json::j_string) - { - Context::getContext ().error (format (STRING_HOOK_ERROR_SAME1, uuid, Path (script).name ())); + auto u = root_obj->_data.find("uuid"); + if (u == root_obj->_data.end() || u->second->type() != json::j_string) { + Context::getContext().error(format(STRING_HOOK_ERROR_SAME1, uuid, Path(script).name())); throw 0; } - auto up = (json::string*) u->second; - auto text = up->dump (); - Lexer::dequote (text); - std::string json_uuid = json::decode (text); - if (json_uuid != uuid) - { - Context::getContext ().error (format (STRING_HOOK_ERROR_SAME2, uuid, json_uuid, Path (script).name ())); + auto up = (json::string*)u->second; + auto text = up->dump(); + Lexer::dequote(text); + std::string json_uuid = json::decode(text); + if (json_uuid != uuid) { + Context::getContext().error( + format(STRING_HOOK_ERROR_SAME2, uuid, json_uuid, Path(script).name())); throw 0; } @@ -514,101 +434,82 @@ void Hooks::assertSameTask ( } //////////////////////////////////////////////////////////////////////////////// -void Hooks::assertFeedback ( - const std::vector & input, - const std::string& script) const -{ +void Hooks::assertFeedback(const std::vector& input, const std::string& script) const { bool foundSomething = false; for (auto& i : input) - if (nontrivial (i)) - foundSomething = true; + if (nontrivial(i)) foundSomething = true; - if (! foundSomething) - { - Context::getContext ().error (format (STRING_HOOK_ERROR_NOFEEDBACK, Path (script).name ())); + if (!foundSomething) { + Context::getContext().error(format(STRING_HOOK_ERROR_NOFEEDBACK, Path(script).name())); throw 0; } } //////////////////////////////////////////////////////////////////////////////// -std::vector & Hooks::buildHookScriptArgs (std::vector & args) const -{ +std::vector& Hooks::buildHookScriptArgs(std::vector& args) const { Variant v; // Hooks API version. - args.push_back ("api:2"); + args.push_back("api:2"); // Command line Taskwarrior was called with. - getDOM ("context.args", v); - args.push_back ("args:" + std::string (v)); + getDOM("context.args", v); + args.push_back("args:" + std::string(v)); // Command to be executed. - args.push_back ("command:" + Context::getContext ().cli2.getCommand ()); + args.push_back("command:" + Context::getContext().cli2.getCommand()); // rc file used after applying all overrides. - args.push_back ("rc:" + Context::getContext ().rc_file._data); + args.push_back("rc:" + Context::getContext().rc_file._data); // Directory containing *.data files. - args.push_back ("data:" + Context::getContext ().data_dir._data); + args.push_back("data:" + Context::getContext().data_dir._data); // Taskwarrior version, same as returned by "task --version" - args.push_back ("version:" + std::string(VERSION)); + args.push_back("version:" + std::string(VERSION)); return args; } //////////////////////////////////////////////////////////////////////////////// -int Hooks::callHookScript ( - const std::string& script, - const std::vector & input, - std::vector & output) const -{ - if (_debug >= 1) - Context::getContext ().debug ("Hook: Calling " + script); +int Hooks::callHookScript(const std::string& script, const std::vector& input, + std::vector& output) const { + if (_debug >= 1) Context::getContext().debug("Hook: Calling " + script); - if (_debug >= 2) - { - Context::getContext ().debug ("Hook: input"); - for (const auto& i : input) - Context::getContext ().debug (" " + i); + if (_debug >= 2) { + Context::getContext().debug("Hook: input"); + for (const auto& i : input) Context::getContext().debug(" " + i); } std::string inputStr; - for (const auto& i : input) - inputStr += i + "\n"; + for (const auto& i : input) inputStr += i + "\n"; - std::vector args; - buildHookScriptArgs (args); - if (_debug >= 2) - { - Context::getContext ().debug ("Hooks: args"); - for (const auto& arg: args) - Context::getContext ().debug (" " + arg); + std::vector args; + buildHookScriptArgs(args); + if (_debug >= 2) { + Context::getContext().debug("Hooks: args"); + for (const auto& arg : args) Context::getContext().debug(" " + arg); } // Measure time for each hook if running in debug int status; std::string outputStr; - if (_debug >= 2) - { + if (_debug >= 2) { Timer timer; - status = execute (script, args, inputStr, outputStr); - Context::getContext ().debugTiming (format ("Hooks::execute ({1})", script), timer); - } - else - status = execute (script, args, inputStr, outputStr); + status = execute(script, args, inputStr, outputStr); + Context::getContext().debugTiming(format("Hooks::execute ({1})", script), timer); + } else + status = execute(script, args, inputStr, outputStr); - output = split (outputStr, '\n'); + output = split(outputStr, '\n'); - if (_debug >= 2) - { - Context::getContext ().debug ("Hook: output"); + if (_debug >= 2) { + Context::getContext().debug("Hook: output"); for (const auto& i : output) - if (i != "") - Context::getContext ().debug (" " + i); + if (i != "") Context::getContext().debug(" " + i); - Context::getContext ().debug (format ("Hook: Completed with status {1}", status)); - Context::getContext ().debug (" "); // Blank line + Context::getContext().debug(format("Hook: Completed with status {1}", status)); + Context::getContext().debug(" "); // Blank line } return status; diff --git a/src/Hooks.h b/src/Hooks.h index a65bc9ff3..6496674b7 100644 --- a/src/Hooks.h +++ b/src/Hooks.h @@ -27,37 +27,39 @@ #ifndef INCLUDED_HOOKS #define INCLUDED_HOOKS -#include -#include #include -class Hooks -{ -public: - Hooks () = default; - void initialize (); - bool enable (bool); - void onLaunch () const; - void onExit () const; - void onAdd (Task&) const; - void onModify (const Task&, Task&) const; - std::vector list () const; +#include +#include -private: - std::vector scripts (const std::string&) const; - void separateOutput (const std::vector &, std::vector &, std::vector &) const; - bool isJSON (const std::string&) const; - void assertValidJSON (const std::vector &, const std::string&) const; - void assertNTasks (const std::vector &, unsigned int, const std::string&) const; - void assertSameTask (const std::vector &, const Task&, const std::string&) const; - void assertFeedback (const std::vector &, const std::string&) const; - std::vector & buildHookScriptArgs (std::vector &) const; - int callHookScript (const std::string&, const std::vector &, std::vector &) const; +class Hooks { + public: + Hooks() = default; + void initialize(); + bool enable(bool); + void onLaunch() const; + void onExit() const; + void onAdd(Task&) const; + void onModify(const Task&, Task&) const; + std::vector list() const; -private: - bool _enabled {true}; - int _debug {0}; - std::vector _scripts {}; + private: + std::vector scripts(const std::string&) const; + void separateOutput(const std::vector&, std::vector&, + std::vector&) const; + bool isJSON(const std::string&) const; + void assertValidJSON(const std::vector&, const std::string&) const; + void assertNTasks(const std::vector&, unsigned int, const std::string&) const; + void assertSameTask(const std::vector&, const Task&, const std::string&) const; + void assertFeedback(const std::vector&, const std::string&) const; + std::vector& buildHookScriptArgs(std::vector&) const; + int callHookScript(const std::string&, const std::vector&, + std::vector&) const; + + private: + bool _enabled{true}; + int _debug{0}; + std::vector _scripts{}; }; #endif diff --git a/src/Lexer.cpp b/src/Lexer.cpp index d11a8f335..0c3b8a38e 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -27,42 +27,34 @@ #include // cmake.h include header must come first -#include -#include -#include #include #include +#include +#include #include #include +#include + static const std::string uuid_pattern = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; static const unsigned int uuid_min_length = 8; std::string Lexer::dateFormat = ""; std::string::size_type Lexer::minimumMatchLength = 3; -std::map Lexer::attributes; - +std::map Lexer::attributes; //////////////////////////////////////////////////////////////////////////////// -Lexer::Lexer (const std::string& text) -: _text (text) -, _cursor (0) -, _eos (text.size ()) -{ -} +Lexer::Lexer(const std::string& text) : _text(text), _cursor(0), _eos(text.size()) {} //////////////////////////////////////////////////////////////////////////////// // When a Lexer object is constructed with a string, this method walks through // the stream of low-level tokens. -bool Lexer::token (std::string& token, Lexer::Type& type) -{ +bool Lexer::token(std::string& token, Lexer::Type& type) { // Eat white space. - while (unicodeWhitespace (_text[_cursor])) - utf8_next_char (_text, _cursor); + while (unicodeWhitespace(_text[_cursor])) utf8_next_char(_text, _cursor); // Terminate at EOS. - if (isEOS ()) - return false; + if (isEOS()) return false; // The sequence is specific, and must follow these rules: // - date < duration < uuid < identifier @@ -74,24 +66,12 @@ bool Lexer::token (std::string& token, Lexer::Type& type) // - path < substitution < pattern // - set < number // - word last - if (isString (token, type, "'\"") || - isDate (token, type) || - isDuration (token, type) || - isURL (token, type) || - isPair (token, type) || - isUUID (token, type, true) || - isSet (token, type) || - isDOM (token, type) || - isHexNumber (token, type) || - isNumber (token, type) || - isSeparator (token, type) || - isTag (token, type) || - isPath (token, type) || - isSubstitution (token, type) || - isPattern (token, type) || - isOperator (token, type) || - isIdentifier (token, type) || - isWord (token, type)) + if (isString(token, type, "'\"") || isDate(token, type) || isDuration(token, type) || + isURL(token, type) || isPair(token, type) || isUUID(token, type, true) || + isSet(token, type) || isDOM(token, type) || isHexNumber(token, type) || + isNumber(token, type) || isSeparator(token, type) || isTag(token, type) || + isPath(token, type) || isSubstitution(token, type) || isPattern(token, type) || + isOperator(token, type) || isIdentifier(token, type) || isWord(token, type)) return true; return false; @@ -99,70 +79,78 @@ bool Lexer::token (std::string& token, Lexer::Type& type) //////////////////////////////////////////////////////////////////////////////// // This static method tokenizes the input, but discards the type information. -std::vector Lexer::split (const std::string& text) -{ - std::vector all; +std::vector Lexer::split(const std::string& text) { + std::vector all; std::string token; Lexer::Type ignored; - Lexer l (text); - while (l.token (token, ignored)) - all.push_back (token); + Lexer l(text); + while (l.token(token, ignored)) all.push_back(token); return all; } //////////////////////////////////////////////////////////////////////////////// // No L10N - these are for internal purposes. -const std::string Lexer::typeName (const Lexer::Type& type) -{ - switch (type) - { - case Lexer::Type::uuid: return "uuid"; - case Lexer::Type::number: return "number"; - case Lexer::Type::hex: return "hex"; - case Lexer::Type::string: return "string"; - case Lexer::Type::url: return "url"; - case Lexer::Type::pair: return "pair"; - case Lexer::Type::set: return "set"; - case Lexer::Type::separator: return "separator"; - case Lexer::Type::tag: return "tag"; - case Lexer::Type::path: return "path"; - case Lexer::Type::substitution: return "substitution"; - case Lexer::Type::pattern: return "pattern"; - case Lexer::Type::op: return "op"; - case Lexer::Type::dom: return "dom"; - case Lexer::Type::identifier: return "identifier"; - case Lexer::Type::word: return "word"; - case Lexer::Type::date: return "date"; - case Lexer::Type::duration: return "duration"; +const std::string Lexer::typeName(const Lexer::Type& type) { + switch (type) { + case Lexer::Type::uuid: + return "uuid"; + case Lexer::Type::number: + return "number"; + case Lexer::Type::hex: + return "hex"; + case Lexer::Type::string: + return "string"; + case Lexer::Type::url: + return "url"; + case Lexer::Type::pair: + return "pair"; + case Lexer::Type::set: + return "set"; + case Lexer::Type::separator: + return "separator"; + case Lexer::Type::tag: + return "tag"; + case Lexer::Type::path: + return "path"; + case Lexer::Type::substitution: + return "substitution"; + case Lexer::Type::pattern: + return "pattern"; + case Lexer::Type::op: + return "op"; + case Lexer::Type::dom: + return "dom"; + case Lexer::Type::identifier: + return "identifier"; + case Lexer::Type::word: + return "word"; + case Lexer::Type::date: + return "date"; + case Lexer::Type::duration: + return "duration"; } return "unknown"; } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isIdentifierStart (int c) -{ - return c && // Include null character check. - ! unicodeWhitespace (c) && - ! unicodeLatinDigit (c) && - ! isSingleCharOperator (c) && - ! isPunctuation (c); +bool Lexer::isIdentifierStart(int c) { + return c && // Include null character check. + !unicodeWhitespace(c) && !unicodeLatinDigit(c) && !isSingleCharOperator(c) && + !isPunctuation(c); } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isIdentifierNext (int c) -{ - return c && // Include null character check. - c != ':' && // Used in isPair. - c != '=' && // Used in isPair. - ! unicodeWhitespace (c) && - ! isSingleCharOperator (c); +bool Lexer::isIdentifierNext(int c) { + return c && // Include null character check. + c != ':' && // Used in isPair. + c != '=' && // Used in isPair. + !unicodeWhitespace(c) && !isSingleCharOperator(c); } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isSingleCharOperator (int c) -{ +bool Lexer::isSingleCharOperator(int c) { return c == '+' || // Addition c == '-' || // Subtraction or unary minus = ambiguous c == '*' || // Multiplication @@ -179,129 +167,95 @@ bool Lexer::isSingleCharOperator (int c) } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isDoubleCharOperator (int c0, int c1, int c2) -{ - return (c0 == '=' && c1 == '=') || - (c0 == '!' && c1 == '=') || - (c0 == '<' && c1 == '=') || - (c0 == '>' && c1 == '=') || - (c0 == 'o' && c1 == 'r' && isBoundary (c1, c2)) || - (c0 == '|' && c1 == '|') || - (c0 == '&' && c1 == '&') || - (c0 == '!' && c1 == '~'); +bool Lexer::isDoubleCharOperator(int c0, int c1, int c2) { + return (c0 == '=' && c1 == '=') || (c0 == '!' && c1 == '=') || (c0 == '<' && c1 == '=') || + (c0 == '>' && c1 == '=') || (c0 == 'o' && c1 == 'r' && isBoundary(c1, c2)) || + (c0 == '|' && c1 == '|') || (c0 == '&' && c1 == '&') || (c0 == '!' && c1 == '~'); } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isTripleCharOperator (int c0, int c1, int c2, int c3) -{ - return (c0 == 'a' && c1 == 'n' && c2 == 'd' && isBoundary (c2, c3)) || - (c0 == 'x' && c1 == 'o' && c2 == 'r' && isBoundary (c2, c3)) || +bool Lexer::isTripleCharOperator(int c0, int c1, int c2, int c3) { + return (c0 == 'a' && c1 == 'n' && c2 == 'd' && isBoundary(c2, c3)) || + (c0 == 'x' && c1 == 'o' && c2 == 'r' && isBoundary(c2, c3)) || (c0 == '!' && c1 == '=' && c2 == '='); } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isBoundary (int left, int right) -{ +bool Lexer::isBoundary(int left, int right) { // EOS - if (right == '\0') return true; + if (right == '\0') return true; // XOR - if (unicodeLatinAlpha (left) != unicodeLatinAlpha (right)) return true; - if (unicodeLatinDigit (left) != unicodeLatinDigit (right)) return true; - if (unicodeWhitespace (left) != unicodeWhitespace (right)) return true; + if (unicodeLatinAlpha(left) != unicodeLatinAlpha(right)) return true; + if (unicodeLatinDigit(left) != unicodeLatinDigit(right)) return true; + if (unicodeWhitespace(left) != unicodeWhitespace(right)) return true; // OR - if (isPunctuation (left) || isPunctuation (right)) return true; + if (isPunctuation(left) || isPunctuation(right)) return true; return false; } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isHardBoundary (int left, int right) -{ +bool Lexer::isHardBoundary(int left, int right) { // EOS - if (right == '\0') - return true; + if (right == '\0') return true; // FILTER operators that don't need to be surrounded by whitespace. - if (left == '(' || - left == ')' || - right == '(' || - right == ')') - return true; + if (left == '(' || left == ')' || right == '(' || right == ')') return true; return false; } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isPunctuation (int c) -{ - return isprint (c) && - c != ' ' && - c != '@' && - c != '#' && - c != '$' && - c != '_' && - ! unicodeLatinDigit (c) && - ! unicodeLatinAlpha (c); +bool Lexer::isPunctuation(int c) { + return isprint(c) && c != ' ' && c != '@' && c != '#' && c != '$' && c != '_' && + !unicodeLatinDigit(c) && !unicodeLatinAlpha(c); } //////////////////////////////////////////////////////////////////////////////// // Assumes that quotes is a string containing a non-trivial set of quote // characters. -void Lexer::dequote (std::string& input, const std::string& quotes) -{ +void Lexer::dequote(std::string& input, const std::string& quotes) { int quote = input[0]; - if (quotes.find (quote) != std::string::npos) - { - size_t len = input.length (); - if (quote == input[len - 1]) - input = input.substr (1, len - 2); + if (quotes.find(quote) != std::string::npos) { + size_t len = input.length(); + if (quote == input[len - 1]) input = input.substr(1, len - 2); } } //////////////////////////////////////////////////////////////////////////////// // Detects characters in an input string that indicate quotes were required, or // escapes, to get them past the shell. -bool Lexer::wasQuoted (const std::string& input) -{ - if (input.find_first_of (" \t()<>&~") != std::string::npos) - return true; +bool Lexer::wasQuoted(const std::string& input) { + if (input.find_first_of(" \t()<>&~") != std::string::npos) return true; return false; } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isEOS () const -{ - return _cursor >= _eos; -} +bool Lexer::isEOS() const { return _cursor >= _eos; } //////////////////////////////////////////////////////////////////////////////// // Converts '0' -> 0 // '9' -> 9 // 'a'/'A' -> 10 // 'f'/'F' -> 15 -int Lexer::hexToInt (int c) -{ - if (c >= '0' && c <= '9') return (c - '0'); - else if (c >= 'a' && c <= 'f') return (c - 'a' + 10); - else return (c - 'A' + 10); +int Lexer::hexToInt(int c) { + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return (c - 'A' + 10); } //////////////////////////////////////////////////////////////////////////////// -int Lexer::hexToInt (int c0, int c1) -{ - return (hexToInt (c0) << 4) + hexToInt (c1); -} +int Lexer::hexToInt(int c0, int c1) { return (hexToInt(c0) << 4) + hexToInt(c1); } //////////////////////////////////////////////////////////////////////////////// -int Lexer::hexToInt (int c0, int c1, int c2, int c3) -{ - return (hexToInt (c0) << 12) + - (hexToInt (c1) << 8) + - (hexToInt (c2) << 4) + - hexToInt (c3); +int Lexer::hexToInt(int c0, int c1, int c2, int c3) { + return (hexToInt(c0) << 12) + (hexToInt(c1) << 8) + (hexToInt(c2) << 4) + hexToInt(c3); } //////////////////////////////////////////////////////////////////////////////// @@ -310,16 +264,10 @@ int Lexer::hexToInt (int c0, int c1, int c2, int c3) // left: wonderful // right: wonderbread // returns: ^ 6 -std::string::size_type Lexer::commonLength ( - const std::string& left, - const std::string& right) -{ +std::string::size_type Lexer::commonLength(const std::string& left, const std::string& right) { std::string::size_type l = 0; std::string::size_type r = 0; - while (left[l] == right[r] && - utf8_next_char (left, l) && - utf8_next_char (right, r)) - ; + while (left[l] == right[r] && utf8_next_char(left, l) && utf8_next_char(right, r)); return l; } @@ -332,138 +280,106 @@ std::string::size_type Lexer::commonLength ( // right: prowonderbread // r: ^ // returns: ^ 6 -std::string::size_type Lexer::commonLength ( - const std::string& left, - std::string::size_type l, - const std::string& right, - std::string::size_type r) -{ - while (left[l] == right[r] && - utf8_next_char (left, l) && - utf8_next_char (right, r)) - ; +std::string::size_type Lexer::commonLength(const std::string& left, std::string::size_type l, + const std::string& right, std::string::size_type r) { + while (left[l] == right[r] && utf8_next_char(left, l) && utf8_next_char(right, r)); return l; } //////////////////////////////////////////////////////////////////////////////// -std::string Lexer::commify (const std::string& data) -{ +std::string Lexer::commify(const std::string& data) { // First scan for decimal point and end of digits. int decimalPoint = -1; - int end = -1; + int end = -1; int i; - for (int i = 0; i < (int) data.length (); ++i) - { - if (unicodeLatinDigit (data[i])) - end = i; + for (int i = 0; i < (int)data.length(); ++i) { + if (unicodeLatinDigit(data[i])) end = i; - if (data[i] == '.') - decimalPoint = i; + if (data[i] == '.') decimalPoint = i; } std::string result; - if (decimalPoint != -1) - { + if (decimalPoint != -1) { // In reverse order, transfer all digits up to, and including the decimal // point. - for (i = (int) data.length () - 1; i >= decimalPoint; --i) - result += data[i]; + for (i = (int)data.length() - 1; i >= decimalPoint; --i) result += data[i]; int consecutiveDigits = 0; - for (; i >= 0; --i) - { - if (unicodeLatinDigit (data[i])) - { + for (; i >= 0; --i) { + if (unicodeLatinDigit(data[i])) { result += data[i]; - if (++consecutiveDigits == 3 && i && unicodeLatinDigit (data[i - 1])) - { + if (++consecutiveDigits == 3 && i && unicodeLatinDigit(data[i - 1])) { result += ','; consecutiveDigits = 0; } - } - else + } else result += data[i]; } - } - else - { + } else { // In reverse order, transfer all digits up to, but not including the last // digit. - for (i = (int) data.length () - 1; i > end; --i) - result += data[i]; + for (i = (int)data.length() - 1; i > end; --i) result += data[i]; int consecutiveDigits = 0; - for (; i >= 0; --i) - { - if (unicodeLatinDigit (data[i])) - { + for (; i >= 0; --i) { + if (unicodeLatinDigit(data[i])) { result += data[i]; - if (++consecutiveDigits == 3 && i && unicodeLatinDigit (data[i - 1])) - { + if (++consecutiveDigits == 3 && i && unicodeLatinDigit(data[i - 1])) { result += ','; consecutiveDigits = 0; } - } - else + } else result += data[i]; } } // reverse result into data. std::string done; - for (int i = (int) result.length () - 1; i >= 0; --i) - done += result[i]; + for (int i = (int)result.length() - 1; i >= 0; --i) done += result[i]; return done; } //////////////////////////////////////////////////////////////////////////////// -std::string Lexer::lowerCase (const std::string& input) -{ +std::string Lexer::lowerCase(const std::string& input) { std::string output = input; - std::transform (output.begin (), output.end (), output.begin (), tolower); + std::transform(output.begin(), output.end(), output.begin(), tolower); return output; } //////////////////////////////////////////////////////////////////////////////// -std::string Lexer::ucFirst (const std::string& input) -{ +std::string Lexer::ucFirst(const std::string& input) { std::string output = input; - if (output.length () > 0) - output[0] = toupper (output[0]); + if (output.length() > 0) output[0] = toupper(output[0]); return output; } //////////////////////////////////////////////////////////////////////////////// -std::string Lexer::trimLeft (const std::string& in, const std::string& t /*= " "*/) -{ - std::string::size_type ws = in.find_first_not_of (t); - if (ws > 0) - { - std::string out {in}; - return out.erase (0, ws); +std::string Lexer::trimLeft(const std::string& in, const std::string& t /*= " "*/) { + std::string::size_type ws = in.find_first_not_of(t); + if (ws > 0) { + std::string out{in}; + return out.erase(0, ws); } return in; } //////////////////////////////////////////////////////////////////////////////// -std::string Lexer::trimRight (const std::string& in, const std::string& t /*= " "*/) -{ - std::string out {in}; - return out.erase (in.find_last_not_of (t) + 1); +std::string Lexer::trimRight(const std::string& in, const std::string& t /*= " "*/) { + std::string out{in}; + return out.erase(in.find_last_not_of(t) + 1); } //////////////////////////////////////////////////////////////////////////////// -std::string Lexer::trim (const std::string& in, const std::string& t /*= " "*/) -{ - return trimLeft (trimRight (in, t), t); +std::string Lexer::trim(const std::string& in, const std::string& t /*= " "*/) { + return trimLeft(trimRight(in, t), t); } //////////////////////////////////////////////////////////////////////////////// @@ -471,11 +387,9 @@ std::string Lexer::trim (const std::string& in, const std::string& t /*= " "*/) // '|" // [ U+XXXX | \uXXXX | \" | \' | \\ | \/ | \b | \f | \n | \r | \t | . ] // '|" -bool Lexer::isString (std::string& token, Lexer::Type& type, const std::string& quotes) -{ +bool Lexer::isString(std::string& token, Lexer::Type& type, const std::string& quotes) { std::size_t marker = _cursor; - if (readWord (_text, quotes, marker, token)) - { + if (readWord(_text, quotes, marker, token)) { type = Lexer::Type::string; _cursor = marker; return true; @@ -487,15 +401,13 @@ bool Lexer::isString (std::string& token, Lexer::Type& type, const std::string& //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::date // -bool Lexer::isDate (std::string& token, Lexer::Type& type) -{ +bool Lexer::isDate(std::string& token, Lexer::Type& type) { // Try an ISO date parse. std::size_t iso_i = 0; Datetime iso; - if (iso.parse (_text.substr (_cursor), iso_i, Lexer::dateFormat)) - { + if (iso.parse(_text.substr(_cursor), iso_i, Lexer::dateFormat)) { type = Lexer::Type::date; - token = _text.substr (_cursor, iso_i); + token = _text.substr(_cursor, iso_i); _cursor += iso_i; return true; } @@ -506,24 +418,21 @@ bool Lexer::isDate (std::string& token, Lexer::Type& type) //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::duration // -bool Lexer::isDuration (std::string& token, Lexer::Type& type) -{ +bool Lexer::isDuration(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; std::string extractedToken; Lexer::Type extractedType; - if (isOperator (extractedToken, extractedType)) - { + if (isOperator(extractedToken, extractedType)) { _cursor = marker; return false; } marker = 0; Duration iso; - if (iso.parse (_text.substr (_cursor), marker)) - { + if (iso.parse(_text.substr(_cursor), marker)) { type = Lexer::Type::duration; - token = _text.substr (_cursor, marker); + token = _text.substr(_cursor, marker); _cursor += marker; return true; } @@ -543,30 +452,22 @@ bool Lexer::isDuration (std::string& token, Lexer::Type& type) // XXXXXXXX- // XXXXXXXX // Followed only by EOS, whitespace, or single character operator. -bool Lexer::isUUID (std::string& token, Lexer::Type& type, bool endBoundary) -{ +bool Lexer::isUUID(std::string& token, Lexer::Type& type, bool endBoundary) { std::size_t marker = _cursor; // Greedy. std::size_t i = 0; - for (; i < 36 && marker + i < _eos; i++) - { - if (uuid_pattern[i] == 'x') - { - if (! unicodeHexDigit (_text[marker + i])) - break; - } - else if (uuid_pattern[i] != _text[marker + i]) + for (; i < 36 && marker + i < _eos; i++) { + if (uuid_pattern[i] == 'x') { + if (!unicodeHexDigit(_text[marker + i])) break; + } else if (uuid_pattern[i] != _text[marker + i]) break; } - if (i >= uuid_min_length && - (! endBoundary || - ! _text[marker + i] || - unicodeWhitespace (_text[marker + i]) || - isSingleCharOperator (_text[marker + i]))) - { - token = _text.substr (_cursor, i); + if (i >= uuid_min_length && + (!endBoundary || !_text[marker + i] || unicodeWhitespace(_text[marker + i]) || + isSingleCharOperator(_text[marker + i]))) { + token = _text.substr(_cursor, i); type = Lexer::Type::uuid; _cursor += i; return true; @@ -578,22 +479,16 @@ bool Lexer::isUUID (std::string& token, Lexer::Type& type, bool endBoundary) //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::hex // 0xX+ -bool Lexer::isHexNumber (std::string& token, Lexer::Type& type) -{ +bool Lexer::isHexNumber(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; - if (_eos - marker >= 3 && - _text[marker + 0] == '0' && - _text[marker + 1] == 'x') - { + if (_eos - marker >= 3 && _text[marker + 0] == '0' && _text[marker + 1] == 'x') { marker += 2; - while (unicodeHexDigit (_text[marker])) - ++marker; + while (unicodeHexDigit(_text[marker])) ++marker; - if (marker - _cursor > 2) - { - token = _text.substr (_cursor, marker - _cursor); + if (marker - _cursor > 2) { + token = _text.substr(_cursor, marker - _cursor); type = Lexer::Type::hex; _cursor = marker; return true; @@ -610,57 +505,41 @@ bool Lexer::isHexNumber (std::string& token, Lexer::Type& type) // [ . \d+ ] // [ e|E [ +|- ] \d+ [ . \d+ ] ] // not followed by non-operator. -bool Lexer::isNumber (std::string& token, Lexer::Type& type) -{ +bool Lexer::isNumber(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; bool leading_zero = (_text[marker] == '0'); - if (unicodeLatinDigit (_text[marker])) - { + if (unicodeLatinDigit(_text[marker])) { ++marker; // Two (or more) digit number with a leading zero are not allowed - if (leading_zero && unicodeLatinDigit (_text[marker])) - return false; + if (leading_zero && unicodeLatinDigit(_text[marker])) return false; - while (unicodeLatinDigit (_text[marker])) - utf8_next_char (_text, marker); + while (unicodeLatinDigit(_text[marker])) utf8_next_char(_text, marker); - if (_text[marker] == '.') - { + if (_text[marker] == '.') { ++marker; - if (unicodeLatinDigit (_text[marker])) - { + if (unicodeLatinDigit(_text[marker])) { ++marker; - while (unicodeLatinDigit (_text[marker])) - utf8_next_char (_text, marker); + while (unicodeLatinDigit(_text[marker])) utf8_next_char(_text, marker); } } - if (_text[marker] == 'e' || - _text[marker] == 'E') - { + if (_text[marker] == 'e' || _text[marker] == 'E') { ++marker; - if (_text[marker] == '+' || - _text[marker] == '-') - ++marker; + if (_text[marker] == '+' || _text[marker] == '-') ++marker; - if (unicodeLatinDigit (_text[marker])) - { + if (unicodeLatinDigit(_text[marker])) { ++marker; - while (unicodeLatinDigit (_text[marker])) - utf8_next_char (_text, marker); + while (unicodeLatinDigit(_text[marker])) utf8_next_char(_text, marker); - if (_text[marker] == '.') - { + if (_text[marker] == '.') { ++marker; - if (unicodeLatinDigit (_text[marker])) - { + if (unicodeLatinDigit(_text[marker])) { ++marker; - while (unicodeLatinDigit (_text[marker])) - utf8_next_char (_text, marker); + while (unicodeLatinDigit(_text[marker])) utf8_next_char(_text, marker); } } } @@ -668,12 +547,10 @@ bool Lexer::isNumber (std::string& token, Lexer::Type& type) // Lookahead: ! | ! // If there is an immediately consecutive character, that is not an operator, fail. - if (_eos > marker && - ! unicodeWhitespace (_text[marker]) && - ! isSingleCharOperator (_text[marker])) + if (_eos > marker && !unicodeWhitespace(_text[marker]) && !isSingleCharOperator(_text[marker])) return false; - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); type = Lexer::Type::number; _cursor = marker; return true; @@ -687,23 +564,19 @@ bool Lexer::isNumber (std::string& token, Lexer::Type& type) // 0 // [1-9]\d* // Integers do not start with a leading 0, unless they are zero. -bool Lexer::isInteger (std::string& token, Lexer::Type& type) -{ +bool Lexer::isInteger(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; bool leading_zero = (_text[marker] == '0'); - if (unicodeLatinDigit (_text[marker])) - { + if (unicodeLatinDigit(_text[marker])) { ++marker; - while (unicodeLatinDigit (_text[marker])) - utf8_next_char (_text, marker); + while (unicodeLatinDigit(_text[marker])) utf8_next_char(_text, marker); // Leading zero is only allowed in the case of number 0 - if (leading_zero and marker - _cursor > 1) - return false; + if (leading_zero and marker - _cursor > 1) return false; - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); type = Lexer::Type::number; _cursor = marker; return true; @@ -715,12 +588,8 @@ bool Lexer::isInteger (std::string& token, Lexer::Type& type) //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::separator // -- -bool Lexer::isSeparator (std::string& token, Lexer::Type& type) -{ - if (_eos - _cursor >= 2 && - _text[_cursor] == '-' && - _text[_cursor + 1] == '-') - { +bool Lexer::isSeparator(std::string& token, Lexer::Type& type) { + if (_eos - _cursor >= 2 && _text[_cursor] == '-' && _text[_cursor + 1] == '-') { _cursor += 2; type = Lexer::Type::separator; token = "--"; @@ -733,31 +602,23 @@ bool Lexer::isSeparator (std::string& token, Lexer::Type& type) //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::url // http [s] :// ... -bool Lexer::isURL (std::string& token, Lexer::Type& type) -{ +bool Lexer::isURL(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; - if (_eos - _cursor > 9 && // length 'https://*' + if (_eos - _cursor > 9 && // length 'https://*' (_text[marker + 0] == 'h' || _text[marker + 0] == 'H') && (_text[marker + 1] == 't' || _text[marker + 1] == 'T') && (_text[marker + 2] == 't' || _text[marker + 2] == 'T') && - (_text[marker + 3] == 'p' || _text[marker + 3] == 'P')) - { + (_text[marker + 3] == 'p' || _text[marker + 3] == 'P')) { marker += 4; - if (_text[marker + 0] == 's' || _text[marker + 0] == 'S') - ++marker; + if (_text[marker + 0] == 's' || _text[marker + 0] == 'S') ++marker; - if (_text[marker + 0] == ':' && - _text[marker + 1] == '/' && - _text[marker + 2] == '/') - { + if (_text[marker + 0] == ':' && _text[marker + 1] == '/' && _text[marker + 2] == '/') { marker += 3; - while (marker < _eos && - ! unicodeWhitespace (_text[marker])) - utf8_next_char (_text, marker); + while (marker < _eos && !unicodeWhitespace(_text[marker])) utf8_next_char(_text, marker); - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); type = Lexer::Type::url; _cursor = marker; return true; @@ -771,33 +632,27 @@ bool Lexer::isURL (std::string& token, Lexer::Type& type) // Lexer::Type::pair // [ | ] // separator '::' | ':=' | ':' | '=' -bool Lexer::isPair (std::string& token, Lexer::Type& type) -{ +bool Lexer::isPair(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; std::string ignoredToken; Lexer::Type ignoredType; - if (isIdentifier (ignoredToken, ignoredType)) - { + if (isIdentifier(ignoredToken, ignoredType)) { // Look for a valid separator. - std::string separator = _text.substr (_cursor, 2); + std::string separator = _text.substr(_cursor, 2); if (separator == "::" || separator == ":=") _cursor += 2; else if (separator[0] == ':' || separator[0] == '=') _cursor++; - else - { + else { _cursor = marker; return false; } // String, word or nothing are all valid. - if (readWord (_text, "'\"", _cursor, ignoredToken) || - readWord (_text, _cursor, ignoredToken) || - isEOS () || - unicodeWhitespace (_text[_cursor])) - { - token = _text.substr (marker, _cursor - marker); + if (readWord(_text, "'\"", _cursor, ignoredToken) || readWord(_text, _cursor, ignoredToken) || + isEOS() || unicodeWhitespace(_text[_cursor])) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::pair; return true; } @@ -815,81 +670,62 @@ bool Lexer::isPair (std::string& token, Lexer::Type& type) // or a combination: 1,3,5-10 // // [ - ] [ , [ - ] ] ... -bool Lexer::isSet (std::string& token, Lexer::Type& type) -{ +bool Lexer::isSet(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; int count = 0; std::string dummyToken; Lexer::Type dummyType; - do - { - if (isInteger (dummyToken, dummyType)) - { + do { + if (isInteger(dummyToken, dummyType)) { ++count; - if (isLiteral ("-", false, false)) - { - if (isInteger (dummyToken, dummyType)) + if (isLiteral("-", false, false)) { + if (isInteger(dummyToken, dummyType)) ++count; - else - { + else { _cursor = marker; return false; } } - } - else - { + } else { _cursor = marker; return false; } - } - while (isLiteral (",", false, false)); + } while (isLiteral(",", false, false)); // Success is multiple numbers, matching the pattern. - if (count > 1 && - (isEOS () || - unicodeWhitespace (_text[_cursor]) || - isHardBoundary (_text[_cursor], _text[_cursor + 1]))) - { - token = _text.substr (marker, _cursor - marker); + if (count > 1 && (isEOS() || unicodeWhitespace(_text[_cursor]) || + isHardBoundary(_text[_cursor], _text[_cursor + 1]))) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::set; return true; } _cursor = marker; return false; - } //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::tag // ^ | '(' | ')' | // [ +|- ] [ ]* -bool Lexer::isTag (std::string& token, Lexer::Type& type) -{ +bool Lexer::isTag(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; // Lookbehind: Assert ^ or preceded by whitespace, (, or ). - if (marker > 0 && - ! unicodeWhitespace (_text[marker - 1]) && - _text[marker - 1] != '(' && + if (marker > 0 && !unicodeWhitespace(_text[marker - 1]) && _text[marker - 1] != '(' && _text[marker - 1] != ')') return false; - if (_text[marker] == '+' || - _text[marker] == '-') - { + if (_text[marker] == '+' || _text[marker] == '-') { ++marker; - if (isIdentifierStart (_text[marker])) - { - utf8_next_char (_text, marker); + if (isIdentifierStart(_text[marker])) { + utf8_next_char(_text, marker); - while (isIdentifierNext (_text[marker])) - utf8_next_char (_text, marker); + while (isIdentifierNext(_text[marker])) utf8_next_char(_text, marker); - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); type = Lexer::Type::tag; _cursor = marker; return true; @@ -902,40 +738,28 @@ bool Lexer::isTag (std::string& token, Lexer::Type& type) //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::path // ( / )+ -bool Lexer::isPath (std::string& token, Lexer::Type& type) -{ +bool Lexer::isPath(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; int slashCount = 0; - while (true) - { - if (_text[marker] == '/') - { + while (true) { + if (_text[marker] == '/') { ++marker; ++slashCount; - } - else + } else break; - if (_text[marker] && - ! unicodeWhitespace (_text[marker]) && - _text[marker] != '/') - { - utf8_next_char (_text, marker); - while (_text[marker] && - ! unicodeWhitespace (_text[marker]) && - _text[marker] != '/') - utf8_next_char (_text, marker); - } - else + if (_text[marker] && !unicodeWhitespace(_text[marker]) && _text[marker] != '/') { + utf8_next_char(_text, marker); + while (_text[marker] && !unicodeWhitespace(_text[marker]) && _text[marker] != '/') + utf8_next_char(_text, marker); + } else break; } - if (marker > _cursor && - slashCount > 3) - { + if (marker > _cursor && slashCount > 3) { type = Lexer::Type::path; - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); _cursor = marker; return true; } @@ -946,25 +770,19 @@ bool Lexer::isPath (std::string& token, Lexer::Type& type) //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::substitution // / / / [g] | -bool Lexer::isSubstitution (std::string& token, Lexer::Type& type) -{ +bool Lexer::isSubstitution(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; std::string word; - if (readWord (_text, "/", _cursor, word)) - { + if (readWord(_text, "/", _cursor, word)) { --_cursor; // Step backwards over the '/'. - if (readWord (_text, "/", _cursor, word)) - { - if (_text[_cursor] == 'g') - ++_cursor; + if (readWord(_text, "/", _cursor, word)) { + if (_text[_cursor] == 'g') ++_cursor; // Lookahread: | - if (_text[_cursor] == '\0' || - unicodeWhitespace (_text[_cursor])) - { - token = _text.substr (marker, _cursor - marker); + if (_text[_cursor] == '\0' || unicodeWhitespace(_text[_cursor])) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::substitution; return true; } @@ -978,16 +796,12 @@ bool Lexer::isSubstitution (std::string& token, Lexer::Type& type) //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::pattern // / / | -bool Lexer::isPattern (std::string& token, Lexer::Type& type) -{ +bool Lexer::isPattern(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; std::string word; - if (readWord (_text, "/", _cursor, word) && - (isEOS () || - unicodeWhitespace (_text[_cursor]))) - { - token = _text.substr (marker, _cursor - marker); + if (readWord(_text, "/", _cursor, word) && (isEOS() || unicodeWhitespace(_text[_cursor]))) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::pattern; return true; } @@ -1002,68 +816,60 @@ bool Lexer::isPattern (std::string& token, Lexer::Type& type) // | // | // | -bool Lexer::isOperator (std::string& token, Lexer::Type& type) -{ +bool Lexer::isOperator(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; - if (_eos - marker >= 8 && _text.substr (marker, 8) == "_hastag_") - { + if (_eos - marker >= 8 && _text.substr(marker, 8) == "_hastag_") { marker += 8; type = Lexer::Type::op; - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); _cursor = marker; return true; } - else if (_eos - marker >= 7 && _text.substr (marker, 7) == "_notag_") - { + else if (_eos - marker >= 7 && _text.substr(marker, 7) == "_notag_") { marker += 7; type = Lexer::Type::op; - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); _cursor = marker; return true; } - else if (_eos - marker >= 5 && _text.substr (marker, 5) == "_neg_") - { + else if (_eos - marker >= 5 && _text.substr(marker, 5) == "_neg_") { marker += 5; type = Lexer::Type::op; - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); _cursor = marker; return true; } - else if (_eos - marker >= 5 && _text.substr (marker, 5) == "_pos_") - { + else if (_eos - marker >= 5 && _text.substr(marker, 5) == "_pos_") { marker += 5; type = Lexer::Type::op; - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); _cursor = marker; return true; } - else if (_eos - marker >= 3 && - isTripleCharOperator (_text[marker], _text[marker + 1], _text[marker + 2], _text[marker + 3])) - { + else if (_eos - marker >= 3 && isTripleCharOperator(_text[marker], _text[marker + 1], + _text[marker + 2], _text[marker + 3])) { marker += 3; type = Lexer::Type::op; - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); _cursor = marker; return true; } else if (_eos - marker >= 2 && - isDoubleCharOperator (_text[marker], _text[marker + 1], _text[marker + 2])) - { + isDoubleCharOperator(_text[marker], _text[marker + 1], _text[marker + 2])) { marker += 2; type = Lexer::Type::op; - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); _cursor = marker; return true; } - else if (isSingleCharOperator (_text[marker])) - { + else if (isSingleCharOperator(_text[marker])) { token = _text[marker]; type = Lexer::Type::op; _cursor = ++marker; @@ -1117,38 +923,25 @@ bool Lexer::isOperator (std::string& token, Lexer::Type& type) // annotations..entry // annotations..description // -bool Lexer::isDOM (std::string& token, Lexer::Type& type) -{ +bool Lexer::isDOM(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; // rc. ... std::string partialToken; Lexer::Type partialType; - if (isLiteral ("rc.", false, false) && - isWord (partialToken, partialType)) - { - token = _text.substr (marker, _cursor - marker); + if (isLiteral("rc.", false, false) && isWord(partialToken, partialType)) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::dom; return true; - } - else + } else _cursor = marker; // Literals - if (isOneOf ({"tw.syncneeded", - "tw.program", - "tw.args", - "tw.width", - "tw.height", - "tw.version", - "context.program", - "context.args", - "context.width", - "context.height", - "system.version", - "system.os"}, false, true)) - { - token = _text.substr (marker, _cursor - marker); + if (isOneOf({"tw.syncneeded", "tw.program", "tw.args", "tw.width", "tw.height", "tw.version", + "context.program", "context.args", "context.width", "context.height", + "system.version", "system.os"}, + false, true)) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::dom; return true; } @@ -1158,11 +951,8 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type) // . std::string extractedToken; Lexer::Type extractedType; - if (isUUID (extractedToken, extractedType, false) || - isInteger (extractedToken, extractedType)) - { - if (! isLiteral (".", false, false)) - { + if (isUUID(extractedToken, extractedType, false) || isInteger(extractedToken, extractedType)) { + if (!isLiteral(".", false, false)) { _cursor = marker; return false; } @@ -1172,40 +962,31 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type) std::size_t checkpoint = _cursor; // [prefix]tags. - if (isLiteral ("tags", false, false) && - isLiteral (".", false, false) && - isWord (partialToken, partialType)) - { - token = _text.substr (marker, _cursor - marker); + if (isLiteral("tags", false, false) && isLiteral(".", false, false) && + isWord(partialToken, partialType)) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::dom; return true; - } - else + } else _cursor = checkpoint; // [prefix]attribute (bounded) - if (isOneOf (attributes, false, true)) - { - token = _text.substr (marker, _cursor - marker); + if (isOneOf(attributes, false, true)) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::dom; return true; } // [prefix]attribute. (unbounded) - if (isOneOf (attributes, false, false)) - { - if (isLiteral (".", false, false)) - { - std::string attribute = _text.substr (checkpoint, _cursor - checkpoint - 1); + if (isOneOf(attributes, false, false)) { + if (isLiteral(".", false, false)) { + std::string attribute = _text.substr(checkpoint, _cursor - checkpoint - 1); // if attribute type is 'date', then it has sub-elements. if (attributes[attribute] == "date" && - isOneOf ({"year", "month", "day", - "week", "weekday", - "julian", - "hour", "minute", "second"}, false, true)) - { - token = _text.substr (marker, _cursor - marker); + isOneOf({"year", "month", "day", "week", "weekday", "julian", "hour", "minute", "second"}, + false, true)) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::dom; return true; } @@ -1214,9 +995,8 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type) } // Lookahead: ! - else if (! unicodeLatinAlpha (_text[marker])) - { - token = _text.substr (marker, _cursor - marker); + else if (!unicodeLatinAlpha(_text[marker])) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::dom; return true; } @@ -1225,48 +1005,35 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type) } // [prefix]annotations. - if (isLiteral ("annotations", true, false) && - isLiteral (".", false, false)) - { - if (isLiteral ("count", false, false)) - { - token = _text.substr (marker, _cursor - marker); + if (isLiteral("annotations", true, false) && isLiteral(".", false, false)) { + if (isLiteral("count", false, false)) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::dom; return true; } std::string extractedToken; Lexer::Type extractedType; - if (isInteger (extractedToken, extractedType)) - { - if (isLiteral (".", false, false)) - { - if (isLiteral ("description", false, true)) - { - token = _text.substr (marker, _cursor - marker); + if (isInteger(extractedToken, extractedType)) { + if (isLiteral(".", false, false)) { + if (isLiteral("description", false, true)) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::dom; return true; - } - else if (isLiteral ("entry", false, true)) - { - token = _text.substr (marker, _cursor - marker); + } else if (isLiteral("entry", false, true)) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::dom; return true; - } - else if (isLiteral ("entry", false, false) && - isLiteral (".", false, false) && - isOneOf ({"year", "month", "day", - "week", "weekday", - "julian", - "hour", "minute", "second"}, false, true)) - { - token = _text.substr (marker, _cursor - marker); + } else if (isLiteral("entry", false, false) && isLiteral(".", false, false) && + isOneOf({"year", "month", "day", "week", "weekday", "julian", "hour", "minute", + "second"}, + false, true)) { + token = _text.substr(marker, _cursor - marker); type = Lexer::Type::dom; return true; } } - } - else + } else _cursor = checkpoint; } @@ -1277,18 +1044,15 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type) //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::identifier // [ ]* -bool Lexer::isIdentifier (std::string& token, Lexer::Type& type) -{ +bool Lexer::isIdentifier(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; - if (isIdentifierStart (_text[marker])) - { - utf8_next_char (_text, marker); + if (isIdentifierStart(_text[marker])) { + utf8_next_char(_text, marker); - while (isIdentifierNext (_text[marker])) - utf8_next_char (_text, marker); + while (isIdentifierNext(_text[marker])) utf8_next_char(_text, marker); - token = _text.substr (_cursor, marker - _cursor); + token = _text.substr(_cursor, marker - _cursor); type = Lexer::Type::identifier; _cursor = marker; return true; @@ -1300,18 +1064,14 @@ bool Lexer::isIdentifier (std::string& token, Lexer::Type& type) //////////////////////////////////////////////////////////////////////////////// // Lexer::Type::word // [^\s]+ -bool Lexer::isWord (std::string& token, Lexer::Type& type) -{ +bool Lexer::isWord(std::string& token, Lexer::Type& type) { std::size_t marker = _cursor; - while (_text[marker] && - ! unicodeWhitespace (_text[marker]) && - ! isSingleCharOperator (_text[marker])) - utf8_next_char (_text, marker); + while (_text[marker] && !unicodeWhitespace(_text[marker]) && !isSingleCharOperator(_text[marker])) + utf8_next_char(_text, marker); - if (marker > _cursor) - { - token = _text.substr (_cursor, marker - _cursor); + if (marker > _cursor) { + token = _text.substr(_cursor, marker - _cursor); type = Lexer::Type::word; _cursor = marker; return true; @@ -1321,28 +1081,18 @@ bool Lexer::isWord (std::string& token, Lexer::Type& type) } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isLiteral ( - const std::string& literal, - bool allowAbbreviations, - bool endBoundary) -{ - auto common = commonLength (literal, 0, _text, _cursor); +bool Lexer::isLiteral(const std::string& literal, bool allowAbbreviations, bool endBoundary) { + auto common = commonLength(literal, 0, _text, _cursor); // Without abbreviations, common must equal literal length. - if (! allowAbbreviations && - common < literal.length ()) - return false; + if (!allowAbbreviations && common < literal.length()) return false; // Abbreviations must meet the minimum size. - if (allowAbbreviations && - common < minimumMatchLength) - return false; + if (allowAbbreviations && common < minimumMatchLength) return false; // End boundary conditions must be met. - if (endBoundary && - _text[_cursor + common] && - ! unicodeWhitespace (_text[_cursor + common]) && - ! Lexer::isSingleCharOperator (_text[_cursor + common])) + if (endBoundary && _text[_cursor + common] && !unicodeWhitespace(_text[_cursor + common]) && + !Lexer::isSingleCharOperator(_text[_cursor + common])) return false; _cursor += common; @@ -1350,76 +1100,81 @@ bool Lexer::isLiteral ( } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isOneOf ( - const std::vector & options, - bool allowAbbreviations, - bool endBoundary) -{ +bool Lexer::isOneOf(const std::vector& options, bool allowAbbreviations, + bool endBoundary) { for (auto& item : options) - if (isLiteral (item, allowAbbreviations, endBoundary)) - return true; + if (isLiteral(item, allowAbbreviations, endBoundary)) return true; return false; } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isOneOf ( - const std::map & options, - bool allowAbbreviations, - bool endBoundary) -{ +bool Lexer::isOneOf(const std::map& options, bool allowAbbreviations, + bool endBoundary) { for (auto& item : options) - if (isLiteral (item.first, allowAbbreviations, endBoundary)) - return true; + if (isLiteral(item.first, allowAbbreviations, endBoundary)) return true; return false; } //////////////////////////////////////////////////////////////////////////////// // Static -std::string Lexer::typeToString (Lexer::Type type) -{ - if (type == Lexer::Type::string) return std::string ("\033[38;5;7m\033[48;5;3m") + "string" + "\033[0m"; - else if (type == Lexer::Type::uuid) return std::string ("\033[38;5;7m\033[48;5;10m") + "uuid" + "\033[0m"; - else if (type == Lexer::Type::hex) return std::string ("\033[38;5;7m\033[48;5;14m") + "hex" + "\033[0m"; - else if (type == Lexer::Type::number) return std::string ("\033[38;5;7m\033[48;5;6m") + "number" + "\033[0m"; - else if (type == Lexer::Type::separator) return std::string ("\033[38;5;7m\033[48;5;4m") + "separator" + "\033[0m"; - else if (type == Lexer::Type::url) return std::string ("\033[38;5;7m\033[48;5;4m") + "url" + "\033[0m"; - else if (type == Lexer::Type::pair) return std::string ("\033[38;5;7m\033[48;5;1m") + "pair" + "\033[0m"; - else if (type == Lexer::Type::set) return std::string ("\033[38;5;15m\033[48;5;208m") + "set" + "\033[0m"; - else if (type == Lexer::Type::tag) return std::string ("\033[37;45m") + "tag" + "\033[0m"; - else if (type == Lexer::Type::path) return std::string ("\033[37;102m") + "path" + "\033[0m"; - else if (type == Lexer::Type::substitution) return std::string ("\033[37;102m") + "substitution" + "\033[0m"; - else if (type == Lexer::Type::pattern) return std::string ("\033[37;42m") + "pattern" + "\033[0m"; - else if (type == Lexer::Type::op) return std::string ("\033[38;5;7m\033[48;5;203m") + "op" + "\033[0m"; - else if (type == Lexer::Type::dom) return std::string ("\033[38;5;15m\033[48;5;244m") + "dom" + "\033[0m"; - else if (type == Lexer::Type::identifier) return std::string ("\033[38;5;15m\033[48;5;244m") + "identifier" + "\033[0m"; - else if (type == Lexer::Type::word) return std::string ("\033[38;5;15m\033[48;5;236m") + "word" + "\033[0m"; - else if (type == Lexer::Type::date) return std::string ("\033[38;5;15m\033[48;5;34m") + "date" + "\033[0m"; - else if (type == Lexer::Type::duration) return std::string ("\033[38;5;15m\033[48;5;34m") + "duration" + "\033[0m"; - else return std::string ("\033[37;41m") + "unknown" + "\033[0m"; +std::string Lexer::typeToString(Lexer::Type type) { + if (type == Lexer::Type::string) + return std::string("\033[38;5;7m\033[48;5;3m") + "string" + "\033[0m"; + else if (type == Lexer::Type::uuid) + return std::string("\033[38;5;7m\033[48;5;10m") + "uuid" + "\033[0m"; + else if (type == Lexer::Type::hex) + return std::string("\033[38;5;7m\033[48;5;14m") + "hex" + "\033[0m"; + else if (type == Lexer::Type::number) + return std::string("\033[38;5;7m\033[48;5;6m") + "number" + "\033[0m"; + else if (type == Lexer::Type::separator) + return std::string("\033[38;5;7m\033[48;5;4m") + "separator" + "\033[0m"; + else if (type == Lexer::Type::url) + return std::string("\033[38;5;7m\033[48;5;4m") + "url" + "\033[0m"; + else if (type == Lexer::Type::pair) + return std::string("\033[38;5;7m\033[48;5;1m") + "pair" + "\033[0m"; + else if (type == Lexer::Type::set) + return std::string("\033[38;5;15m\033[48;5;208m") + "set" + "\033[0m"; + else if (type == Lexer::Type::tag) + return std::string("\033[37;45m") + "tag" + "\033[0m"; + else if (type == Lexer::Type::path) + return std::string("\033[37;102m") + "path" + "\033[0m"; + else if (type == Lexer::Type::substitution) + return std::string("\033[37;102m") + "substitution" + "\033[0m"; + else if (type == Lexer::Type::pattern) + return std::string("\033[37;42m") + "pattern" + "\033[0m"; + else if (type == Lexer::Type::op) + return std::string("\033[38;5;7m\033[48;5;203m") + "op" + "\033[0m"; + else if (type == Lexer::Type::dom) + return std::string("\033[38;5;15m\033[48;5;244m") + "dom" + "\033[0m"; + else if (type == Lexer::Type::identifier) + return std::string("\033[38;5;15m\033[48;5;244m") + "identifier" + "\033[0m"; + else if (type == Lexer::Type::word) + return std::string("\033[38;5;15m\033[48;5;236m") + "word" + "\033[0m"; + else if (type == Lexer::Type::date) + return std::string("\033[38;5;15m\033[48;5;34m") + "date" + "\033[0m"; + else if (type == Lexer::Type::duration) + return std::string("\033[38;5;15m\033[48;5;34m") + "duration" + "\033[0m"; + else + return std::string("\033[37;41m") + "unknown" + "\033[0m"; } //////////////////////////////////////////////////////////////////////////////// -bool Lexer::isAllDigits (const std::string& text) -{ - return text.length () && - text.find_first_not_of ("0123456789") == std::string::npos; +bool Lexer::isAllDigits(const std::string& text) { + return text.length() && text.find_first_not_of("0123456789") == std::string::npos; } //////////////////////////////////////////////////////////////////////////////// // This is intentionally looking for a single token. -bool Lexer::isDOM (const std::string& text) -{ - Lexer lex (text); +bool Lexer::isDOM(const std::string& text) { + Lexer lex(text); int count = 0; std::string token; Lexer::Type type; - while (lex.token (token, type)) - ++count; + while (lex.token(token, type)) ++count; - return count == 1 && - type == Lexer::Type::dom; + return count == 1 && type == Lexer::Type::dom; } //////////////////////////////////////////////////////////////////////////////// @@ -1430,80 +1185,92 @@ bool Lexer::isDOM (const std::string& text) // "\"" // 'one two' // Result includes the quotes. -bool Lexer::readWord ( - const std::string& text, - const std::string& quotes, - std::string::size_type& cursor, - std::string& word) -{ - if (quotes.find (text[cursor]) == std::string::npos) - return false; +bool Lexer::readWord(const std::string& text, const std::string& quotes, + std::string::size_type& cursor, std::string& word) { + if (quotes.find(text[cursor]) == std::string::npos) return false; - std::string::size_type eos = text.length (); + std::string::size_type eos = text.length(); int quote = text[cursor++]; word = quote; int c; - while ((c = text[cursor])) - { + while ((c = text[cursor])) { // Quoted word ends on a quote. - if (quote && quote == c) - { - word += utf8_character (utf8_next_char (text, cursor)); + if (quote && quote == c) { + word += utf8_character(utf8_next_char(text, cursor)); break; } // Unicode U+XXXX or \uXXXX codepoint. else if (eos - cursor >= 6 && - ((text[cursor + 0] == 'U' && text[cursor + 1] == '+') || + ((text[cursor + 0] == 'U' && text[cursor + 1] == '+') || (text[cursor + 0] == '\\' && text[cursor + 1] == 'u')) && - unicodeHexDigit (text[cursor + 2]) && - unicodeHexDigit (text[cursor + 3]) && - unicodeHexDigit (text[cursor + 4]) && - unicodeHexDigit (text[cursor + 5])) - { - word += utf8_character ( - hexToInt ( - text[cursor + 2], - text[cursor + 3], - text[cursor + 4], - text[cursor + 5])); + unicodeHexDigit(text[cursor + 2]) && unicodeHexDigit(text[cursor + 3]) && + unicodeHexDigit(text[cursor + 4]) && unicodeHexDigit(text[cursor + 5])) { + word += utf8_character( + hexToInt(text[cursor + 2], text[cursor + 3], text[cursor + 4], text[cursor + 5])); cursor += 6; } // An escaped thing. - else if (c == '\\') - { + else if (c == '\\') { c = text[++cursor]; - switch (c) - { - case '"': word += (char) 0x22; ++cursor; break; - case '\'': word += (char) 0x27; ++cursor; break; - case '\\': word += (char) 0x5C; ++cursor; break; - case 'b': word += (char) 0x08; ++cursor; break; - case 'f': word += (char) 0x0C; ++cursor; break; - case 'n': word += (char) 0x0A; ++cursor; break; - case 'r': word += (char) 0x0D; ++cursor; break; - case 't': word += (char) 0x09; ++cursor; break; - case 'v': word += (char) 0x0B; ++cursor; break; + switch (c) { + case '"': + word += (char)0x22; + ++cursor; + break; + case '\'': + word += (char)0x27; + ++cursor; + break; + case '\\': + word += (char)0x5C; + ++cursor; + break; + case 'b': + word += (char)0x08; + ++cursor; + break; + case 'f': + word += (char)0x0C; + ++cursor; + break; + case 'n': + word += (char)0x0A; + ++cursor; + break; + case 'r': + word += (char)0x0D; + ++cursor; + break; + case 't': + word += (char)0x09; + ++cursor; + break; + case 'v': + word += (char)0x0B; + ++cursor; + break; - // This pass-through default case means that anything can be escaped - // harmlessly. In particular 'quote' is included, if it not one of the - // above characters. - default: word += (char) c; ++cursor; break; + // This pass-through default case means that anything can be escaped + // harmlessly. In particular 'quote' is included, if it not one of the + // above characters. + default: + word += (char)c; + ++cursor; + break; } } // Ordinary character. else - word += utf8_character (utf8_next_char (text, cursor)); + word += utf8_character(utf8_next_char(text, cursor)); } // Verify termination. - return word[0] == quote && - word[word.length () - 1] == quote && - word.length () >= 2; + return word[0] == quote && word[word.length() - 1] == quote && word.length() >= 2; } //////////////////////////////////////////////////////////////////////////////// @@ -1517,12 +1284,8 @@ bool Lexer::readWord ( // Lexer::isEOS // unicodeWhitespace // Lexer::isHardBoundary -bool Lexer::readWord ( - const std::string& text, - std::string::size_type& cursor, - std::string& word) -{ - std::string::size_type eos = text.length (); +bool Lexer::readWord(const std::string& text, std::string::size_type& cursor, std::string& word) { + std::string::size_type eos = text.length(); word = ""; int c; @@ -1530,139 +1293,140 @@ bool Lexer::readWord ( while ((c = text[cursor])) // Handles EOS. { // Unquoted word ends on white space. - if (unicodeWhitespace (c)) - break; + if (unicodeWhitespace(c)) break; // Parentheses mostly. - if (prev && Lexer::isHardBoundary (prev, c)) - break; + if (prev && Lexer::isHardBoundary(prev, c)) break; // Unicode U+XXXX or \uXXXX codepoint. else if (eos - cursor >= 6 && - ((text[cursor + 0] == 'U' && text[cursor + 1] == '+') || + ((text[cursor + 0] == 'U' && text[cursor + 1] == '+') || (text[cursor + 0] == '\\' && text[cursor + 1] == 'u')) && - unicodeHexDigit (text[cursor + 2]) && - unicodeHexDigit (text[cursor + 3]) && - unicodeHexDigit (text[cursor + 4]) && - unicodeHexDigit (text[cursor + 5])) - { - word += utf8_character ( - hexToInt ( - text[cursor + 2], - text[cursor + 3], - text[cursor + 4], - text[cursor + 5])); + unicodeHexDigit(text[cursor + 2]) && unicodeHexDigit(text[cursor + 3]) && + unicodeHexDigit(text[cursor + 4]) && unicodeHexDigit(text[cursor + 5])) { + word += utf8_character( + hexToInt(text[cursor + 2], text[cursor + 3], text[cursor + 4], text[cursor + 5])); cursor += 6; } // An escaped thing. - else if (c == '\\') - { + else if (c == '\\') { c = text[++cursor]; - switch (c) - { - case '"': word += (char) 0x22; ++cursor; break; - case '\'': word += (char) 0x27; ++cursor; break; - case '\\': word += (char) 0x5C; ++cursor; break; - case 'b': word += (char) 0x08; ++cursor; break; - case 'f': word += (char) 0x0C; ++cursor; break; - case 'n': word += (char) 0x0A; ++cursor; break; - case 'r': word += (char) 0x0D; ++cursor; break; - case 't': word += (char) 0x09; ++cursor; break; - case 'v': word += (char) 0x0B; ++cursor; break; + switch (c) { + case '"': + word += (char)0x22; + ++cursor; + break; + case '\'': + word += (char)0x27; + ++cursor; + break; + case '\\': + word += (char)0x5C; + ++cursor; + break; + case 'b': + word += (char)0x08; + ++cursor; + break; + case 'f': + word += (char)0x0C; + ++cursor; + break; + case 'n': + word += (char)0x0A; + ++cursor; + break; + case 'r': + word += (char)0x0D; + ++cursor; + break; + case 't': + word += (char)0x09; + ++cursor; + break; + case 'v': + word += (char)0x0B; + ++cursor; + break; - // This pass-through default case means that anything can be escaped - // harmlessly. In particular 'quote' is included, if it not one of the - // above characters. - default: word += (char) c; ++cursor; break; + // This pass-through default case means that anything can be escaped + // harmlessly. In particular 'quote' is included, if it not one of the + // above characters. + default: + word += (char)c; + ++cursor; + break; } } // Ordinary character. else - word += utf8_character (utf8_next_char (text, cursor)); + word += utf8_character(utf8_next_char(text, cursor)); prev = c; } - return word.length () > 0 ? true : false; + return word.length() > 0 ? true : false; } //////////////////////////////////////////////////////////////////////////////// // [. ] <: | = | :: | :=> [] -bool Lexer::decomposePair ( - const std::string& text, - std::string& name, - std::string& modifier, - std::string& separator, - std::string& value) -{ +bool Lexer::decomposePair(const std::string& text, std::string& name, std::string& modifier, + std::string& separator, std::string& value) { // Look for the required elements. - std::string::size_type dot = text.find ('.'); - std::string::size_type sep_defer = text.find ("::"); - std::string::size_type sep_eval = text.find (":="); - std::string::size_type sep_colon = text.find (':'); - std::string::size_type sep_equal = text.find ('='); + std::string::size_type dot = text.find('.'); + std::string::size_type sep_defer = text.find("::"); + std::string::size_type sep_eval = text.find(":="); + std::string::size_type sep_colon = text.find(':'); + std::string::size_type sep_equal = text.find('='); // Determine which separator is dominant, which would be the first one seen, // taking into consideration the overlapping : characters. - std::string::size_type sep = std::string::npos; + std::string::size_type sep = std::string::npos; std::string::size_type sep_end = std::string::npos; - if (sep_defer != std::string::npos && - (sep_eval == std::string::npos || sep_defer <= sep_eval) && + if (sep_defer != std::string::npos && (sep_eval == std::string::npos || sep_defer <= sep_eval) && (sep_colon == std::string::npos || sep_defer <= sep_colon) && - (sep_equal == std::string::npos || sep_defer <= sep_equal)) - { - sep = sep_defer; + (sep_equal == std::string::npos || sep_defer <= sep_equal)) { + sep = sep_defer; sep_end = sep_defer + 2; - } - else if (sep_eval != std::string::npos && - (sep_defer == std::string::npos || sep_eval <= sep_defer) && - (sep_colon == std::string::npos || sep_eval <= sep_colon) && - (sep_equal == std::string::npos || sep_eval <= sep_equal)) - { - sep = sep_eval; + } else if (sep_eval != std::string::npos && + (sep_defer == std::string::npos || sep_eval <= sep_defer) && + (sep_colon == std::string::npos || sep_eval <= sep_colon) && + (sep_equal == std::string::npos || sep_eval <= sep_equal)) { + sep = sep_eval; sep_end = sep_eval + 2; - } - else if (sep_colon != std::string::npos && - (sep_defer == std::string::npos || sep_colon <= sep_defer) && - (sep_eval == std::string::npos || sep_colon <= sep_eval) && - (sep_equal == std::string::npos || sep_colon <= sep_equal)) - { - sep = sep_colon; + } else if (sep_colon != std::string::npos && + (sep_defer == std::string::npos || sep_colon <= sep_defer) && + (sep_eval == std::string::npos || sep_colon <= sep_eval) && + (sep_equal == std::string::npos || sep_colon <= sep_equal)) { + sep = sep_colon; sep_end = sep_colon + 1; - } - else if (sep_equal != std::string::npos && - (sep_defer == std::string::npos || sep_equal <= sep_defer) && - (sep_eval == std::string::npos || sep_equal <= sep_eval) && - (sep_colon == std::string::npos || sep_equal <= sep_colon)) - { - sep = sep_equal; + } else if (sep_equal != std::string::npos && + (sep_defer == std::string::npos || sep_equal <= sep_defer) && + (sep_eval == std::string::npos || sep_equal <= sep_eval) && + (sep_colon == std::string::npos || sep_equal <= sep_colon)) { + sep = sep_equal; sep_end = sep_equal + 1; } // If sep is known, all is well. - if (sep != std::string::npos) - { + if (sep != std::string::npos) { // Now the only unknown is whethere there is a modifier. - if (dot != std::string::npos && dot < sep) - { - name = text.substr (0, dot); - modifier = text.substr (dot + 1, sep - dot - 1); - } - else - { - name = text.substr (0, sep); + if (dot != std::string::npos && dot < sep) { + name = text.substr(0, dot); + modifier = text.substr(dot + 1, sep - dot - 1); + } else { + name = text.substr(0, sep); modifier = ""; } - separator = text.substr (sep, sep_end - sep); - value = text.substr (sep_end); + separator = text.substr(sep, sep_end - sep); + value = text.substr(sep_end); // An empty name is an error. - if (name.length ()) - return true; + if (name.length()) return true; } return false; @@ -1670,29 +1434,21 @@ bool Lexer::decomposePair ( //////////////////////////////////////////////////////////////////////////////// // / / / [] -bool Lexer::decomposeSubstitution ( - const std::string& text, - std::string& from, - std::string& to, - std::string& flags) -{ +bool Lexer::decomposeSubstitution(const std::string& text, std::string& from, std::string& to, + std::string& flags) { std::string parsed_from; std::string::size_type cursor = 0; - if (readWord (text, "/", cursor, parsed_from) && - parsed_from.length ()) - { + if (readWord(text, "/", cursor, parsed_from) && parsed_from.length()) { --cursor; std::string parsed_to; - if (readWord (text, "/", cursor, parsed_to)) - { - std::string parsed_flags = text.substr (cursor); - if (parsed_flags.find ('/') == std::string::npos) - { - dequote (parsed_from, "/"); - dequote (parsed_to, "/"); + if (readWord(text, "/", cursor, parsed_to)) { + std::string parsed_flags = text.substr(cursor); + if (parsed_flags.find('/') == std::string::npos) { + dequote(parsed_from, "/"); + dequote(parsed_to, "/"); - from = parsed_from; - to = parsed_to; + from = parsed_from; + to = parsed_to; flags = parsed_flags; return true; } @@ -1704,21 +1460,14 @@ bool Lexer::decomposeSubstitution ( //////////////////////////////////////////////////////////////////////////////// // / / [] -bool Lexer::decomposePattern ( - const std::string& text, - std::string& pattern, - std::string& flags) -{ +bool Lexer::decomposePattern(const std::string& text, std::string& pattern, std::string& flags) { std::string ignored; std::string::size_type cursor = 0; - if (readWord (text, "/", cursor, ignored) && - ignored.length ()) - { - auto parsed_flags = text.substr (cursor); - if (parsed_flags.find ('/') == std::string::npos) - { - flags = parsed_flags; - pattern = text.substr (1, cursor - 2 - flags.length ()); + if (readWord(text, "/", cursor, ignored) && ignored.length()) { + auto parsed_flags = text.substr(cursor); + if (parsed_flags.find('/') == std::string::npos) { + flags = parsed_flags; + pattern = text.substr(1, cursor - 2 - flags.length()); return true; } } diff --git a/src/Lexer.h b/src/Lexer.h index 790502eff..f7cfe219d 100644 --- a/src/Lexer.h +++ b/src/Lexer.h @@ -27,95 +27,108 @@ #ifndef INCLUDED_LEXER #define INCLUDED_LEXER -#include -#include -#include #include +#include +#include +#include // Lexer: A UTF8 lexical analyzer for every construct used on the Taskwarrior // command line, with additional recognized types for disambiguation. -class Lexer -{ -public: +class Lexer { + public: // These are overridable. static std::string dateFormat; static std::string::size_type minimumMatchLength; - static std::map attributes; + static std::map attributes; - enum class Type { uuid, number, hex, - string, - url, pair, set, separator, - tag, - path, - substitution, pattern, - op, - dom, identifier, word, - date, duration }; + enum class Type { + uuid, + number, + hex, + string, + url, + pair, + set, + separator, + tag, + path, + substitution, + pattern, + op, + dom, + identifier, + word, + date, + duration + }; - Lexer (const std::string&); - ~Lexer () = default; - bool token (std::string&, Lexer::Type&); - static std::vector split (const std::string&); - static std::string typeToString (Lexer::Type); + Lexer(const std::string&); + ~Lexer() = default; + bool token(std::string&, Lexer::Type&); + static std::vector split(const std::string&); + static std::string typeToString(Lexer::Type); // Static helpers. - static const std::string typeName (const Lexer::Type&); - static bool isIdentifierStart (int); - static bool isIdentifierNext (int); - static bool isSingleCharOperator (int); - static bool isDoubleCharOperator (int, int, int); - static bool isTripleCharOperator (int, int, int, int); - static bool isBoundary (int, int); - static bool isHardBoundary (int, int); - static bool isPunctuation (int); - static bool isAllDigits (const std::string&); - static bool isDOM (const std::string&); - static void dequote (std::string&, const std::string& quotes = "'\""); - static bool wasQuoted (const std::string&); - static bool readWord (const std::string&, const std::string&, std::string::size_type&, std::string&); - static bool readWord (const std::string&, std::string::size_type&, std::string&); - static bool decomposePair (const std::string&, std::string&, std::string&, std::string&, std::string&); - static bool decomposeSubstitution (const std::string&, std::string&, std::string&, std::string&); - static bool decomposePattern (const std::string&, std::string&, std::string&); - static int hexToInt (int); - static int hexToInt (int, int); - static int hexToInt (int, int, int, int); - static std::string::size_type commonLength (const std::string&, const std::string&); - static std::string::size_type commonLength (const std::string&, std::string::size_type, const std::string&, std::string::size_type); - static std::string commify (const std::string&); - static std::string lowerCase (const std::string&); - static std::string ucFirst (const std::string&); - static std::string trimLeft (const std::string& in, const std::string& t = " "); - static std::string trimRight (const std::string& in, const std::string& t = " "); - static std::string trim (const std::string& in, const std::string& t = " "); + static const std::string typeName(const Lexer::Type&); + static bool isIdentifierStart(int); + static bool isIdentifierNext(int); + static bool isSingleCharOperator(int); + static bool isDoubleCharOperator(int, int, int); + static bool isTripleCharOperator(int, int, int, int); + static bool isBoundary(int, int); + static bool isHardBoundary(int, int); + static bool isPunctuation(int); + static bool isAllDigits(const std::string&); + static bool isDOM(const std::string&); + static void dequote(std::string&, const std::string& quotes = "'\""); + static bool wasQuoted(const std::string&); + static bool readWord(const std::string&, const std::string&, std::string::size_type&, + std::string&); + static bool readWord(const std::string&, std::string::size_type&, std::string&); + static bool decomposePair(const std::string&, std::string&, std::string&, std::string&, + std::string&); + static bool decomposeSubstitution(const std::string&, std::string&, std::string&, std::string&); + static bool decomposePattern(const std::string&, std::string&, std::string&); + static int hexToInt(int); + static int hexToInt(int, int); + static int hexToInt(int, int, int, int); + static std::string::size_type commonLength(const std::string&, const std::string&); + static std::string::size_type commonLength(const std::string&, std::string::size_type, + const std::string&, std::string::size_type); + static std::string commify(const std::string&); + static std::string lowerCase(const std::string&); + static std::string ucFirst(const std::string&); + static std::string trimLeft(const std::string& in, const std::string& t = " "); + static std::string trimRight(const std::string& in, const std::string& t = " "); + static std::string trim(const std::string& in, const std::string& t = " "); // Stream Classifiers. - bool isEOS () const; - bool isString (std::string&, Lexer::Type&, const std::string&); - bool isDate (std::string&, Lexer::Type&); - bool isDuration (std::string&, Lexer::Type&); - bool isUUID (std::string&, Lexer::Type&, bool); - bool isNumber (std::string&, Lexer::Type&); - bool isInteger (std::string&, Lexer::Type&); - bool isHexNumber (std::string&, Lexer::Type&); - bool isSeparator (std::string&, Lexer::Type&); - bool isURL (std::string&, Lexer::Type&); - bool isPair (std::string&, Lexer::Type&); - bool isSet (std::string&, Lexer::Type&); - bool isTag (std::string&, Lexer::Type&); - bool isPath (std::string&, Lexer::Type&); - bool isSubstitution (std::string&, Lexer::Type&); - bool isPattern (std::string&, Lexer::Type&); - bool isOperator (std::string&, Lexer::Type&); - bool isDOM (std::string&, Lexer::Type&); - bool isIdentifier (std::string&, Lexer::Type&); - bool isWord (std::string&, Lexer::Type&); - bool isLiteral (const std::string&, bool, bool); - bool isOneOf (const std::vector &, bool, bool); - bool isOneOf (const std::map &, bool, bool); + bool isEOS() const; + bool isString(std::string&, Lexer::Type&, const std::string&); + bool isDate(std::string&, Lexer::Type&); + bool isDuration(std::string&, Lexer::Type&); + bool isUUID(std::string&, Lexer::Type&, bool); + bool isNumber(std::string&, Lexer::Type&); + bool isInteger(std::string&, Lexer::Type&); + bool isHexNumber(std::string&, Lexer::Type&); + bool isSeparator(std::string&, Lexer::Type&); + bool isURL(std::string&, Lexer::Type&); + bool isPair(std::string&, Lexer::Type&); + bool isSet(std::string&, Lexer::Type&); + bool isTag(std::string&, Lexer::Type&); + bool isPath(std::string&, Lexer::Type&); + bool isSubstitution(std::string&, Lexer::Type&); + bool isPattern(std::string&, Lexer::Type&); + bool isOperator(std::string&, Lexer::Type&); + bool isDOM(std::string&, Lexer::Type&); + bool isIdentifier(std::string&, Lexer::Type&); + bool isWord(std::string&, Lexer::Type&); + bool isLiteral(const std::string&, bool, bool); + bool isOneOf(const std::vector&, bool, bool); + bool isOneOf(const std::map&, bool, bool); -private: + private: std::string _text; std::size_t _cursor; std::size_t _eos; diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 185f86e29..d175a61ff 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -27,64 +27,63 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include #include +#include #include -#include #include #include +#include +#include +#include #include + +#include +#include +#include +#include +#include +#include + #include "tc/Server.h" #include "tc/util.h" bool TDB2::debug_mode = false; -static void dependency_scan (std::vector &); +static void dependency_scan(std::vector&); //////////////////////////////////////////////////////////////////////////////// -TDB2::TDB2 () -: replica {tc::Replica()} // in-memory Replica -, _working_set {} -{ -} +TDB2::TDB2() + : replica{tc::Replica()} // in-memory Replica + , + _working_set{} {} //////////////////////////////////////////////////////////////////////////////// -void TDB2::open_replica (const std::string& location, bool create_if_missing) -{ +void TDB2::open_replica(const std::string& location, bool create_if_missing) { replica = tc::Replica(location, create_if_missing); } //////////////////////////////////////////////////////////////////////////////// // Add the new task to the replica. -void TDB2::add (Task& task) -{ +void TDB2::add(Task& task) { // Ensure the task is consistent, and provide defaults if necessary. // bool argument to validate() is "applyDefault", to apply default values for // properties not otherwise given. - task.validate (true); + task.validate(true); - std::string uuid = task.get ("uuid"); + std::string uuid = task.get("uuid"); changes[uuid] = task; // run hooks for this new task - Context::getContext ().hooks.onAdd (task); + Context::getContext().hooks.onAdd(task); - auto innertask = replica.import_task_with_uuid (uuid); + auto innertask = replica.import_task_with_uuid(uuid); { auto guard = replica.mutate_task(innertask); // add the task attributes - for (auto& attr : task.all ()) { + for (auto& attr : task.all()) { // TaskChampion does not store uuid or id in the taskmap if (attr == "uuid" || attr == "id") { continue; @@ -93,35 +92,34 @@ void TDB2::add (Task& task) // Use `set_status` for the task status, to get expected behavior // with respect to the working set. else if (attr == "status") { - innertask.set_status (Task::status2tc (Task::textToStatus (task.get (attr)))); + innertask.set_status(Task::status2tc(Task::textToStatus(task.get(attr)))); } // use `set_modified` to set the modified timestamp, avoiding automatic // updates to this field by TaskChampion. else if (attr == "modified") { - auto mod = (time_t) std::stoi (task.get (attr)); - innertask.set_modified (mod); + auto mod = (time_t)std::stoi(task.get(attr)); + innertask.set_modified(mod); } // otherwise, just set the k/v map value else { - innertask.set_value (attr, std::make_optional (task.get (attr))); + innertask.set_value(attr, std::make_optional(task.get(attr))); } } } - auto ws = replica.working_set (); + auto ws = replica.working_set(); // get the ID that was assigned to this task - auto id = ws.by_uuid (uuid); + auto id = ws.by_uuid(uuid); // update the cached working set with the new information - _working_set = std::make_optional (std::move (ws)); + _working_set = std::make_optional(std::move(ws)); - if (id.has_value ()) { - task.id = id.value(); + if (id.has_value()) { + task.id = id.value(); } - } //////////////////////////////////////////////////////////////////////////////// @@ -139,31 +137,30 @@ void TDB2::add (Task& task) // this method. In this case, this method throws an error that will make sense // to the user. This is especially unlikely since tasks are only deleted when // they have been unmodified for a long time. -void TDB2::modify (Task& task) -{ +void TDB2::modify(Task& task) { // All locally modified tasks are timestamped, implicitly overwriting any // changes the user or hooks tried to apply to the "modified" attribute. - task.setAsNow ("modified"); - task.validate (false); - auto uuid = task.get ("uuid"); + task.setAsNow("modified"); + task.validate(false); + auto uuid = task.get("uuid"); changes[uuid] = task; - // invoke the hook and allow it to modify the task before updating + // invoke the hook and allow it to modify the task before updating Task original; - get (uuid, original); - Context::getContext ().hooks.onModify (original, task); + get(uuid, original); + Context::getContext().hooks.onModify(original, task); - auto maybe_tctask = replica.get_task (uuid); - if (!maybe_tctask.has_value ()) { - throw std::string ("task no longer exists"); + auto maybe_tctask = replica.get_task(uuid); + if (!maybe_tctask.has_value()) { + throw std::string("task no longer exists"); } - auto tctask = std::move (maybe_tctask.value ()); + auto tctask = std::move(maybe_tctask.value()); auto guard = replica.mutate_task(tctask); - auto tctask_map = tctask.get_taskmap (); + auto tctask_map = tctask.get_taskmap(); std::unordered_set seen; - for (auto k : task.all ()) { + for (auto k : task.all()) { // ignore task keys that aren't stored if (k == "uuid") { continue; @@ -183,38 +180,35 @@ void TDB2::modify (Task& task) if (v_new == "") { tctask.set_value(k, {}); } else { - tctask.set_value(k, make_optional (v_new)); + tctask.set_value(k, make_optional(v_new)); } } } // we've now added and updated properties; but must find any deleted properties for (auto kv : tctask_map) { - if (seen.find (kv.first) == seen.end ()) { - tctask.set_value (kv.first, {}); + if (seen.find(kv.first) == seen.end()) { + tctask.set_value(kv.first, {}); } } } //////////////////////////////////////////////////////////////////////////////// -void TDB2::purge (Task& task) -{ - auto uuid = task.get ("uuid"); - replica.delete_task (uuid); +void TDB2::purge(Task& task) { + auto uuid = task.get("uuid"); + replica.delete_task(uuid); } //////////////////////////////////////////////////////////////////////////////// -const tc::WorkingSet &TDB2::working_set () -{ - if (!_working_set.has_value ()) { - _working_set = std::make_optional (replica.working_set ()); +const tc::WorkingSet& TDB2::working_set() { + if (!_working_set.has_value()) { + _working_set = std::make_optional(replica.working_set()); } - return _working_set.value (); + return _working_set.value(); } //////////////////////////////////////////////////////////////////////////////// -void TDB2::get_changes (std::vector & changes) -{ +void TDB2::get_changes(std::vector& changes) { std::map& changes_map = this->changes; changes.clear(); std::transform(changes_map.begin(), changes_map.end(), std::back_inserter(changes), @@ -222,8 +216,7 @@ void TDB2::get_changes (std::vector & changes) } //////////////////////////////////////////////////////////////////////////////// -void TDB2::revert () -{ +void TDB2::revert() { auto undo_ops = replica.get_undo_ops(); if (undo_ops.len == 0) { std::cout << "No operations to undo."; @@ -235,19 +228,18 @@ void TDB2::revert () } else { replica.free_replica_ops(undo_ops); } - replica.rebuild_working_set (false); + replica.rebuild_working_set(false); } //////////////////////////////////////////////////////////////////////////////// -bool TDB2::confirm_revert (struct tc::ffi::TCReplicaOpList undo_ops) -{ +bool TDB2::confirm_revert(struct tc::ffi::TCReplicaOpList undo_ops) { // TODO Use show_diff rather than this basic listing of operations, though // this might be a worthy undo.style itself. std::cout << "The following " << undo_ops.len << " operations would be reverted:\n"; for (size_t i = 0; i < undo_ops.len; i++) { std::cout << "- "; tc::ffi::TCReplicaOp op = undo_ops.items[i]; - switch(op.operation_type) { + switch (op.operation_type) { case tc::ffi::TCReplicaOpType::Create: std::cout << "Create " << replica.get_op_uuid(op); break; @@ -256,114 +248,100 @@ bool TDB2::confirm_revert (struct tc::ffi::TCReplicaOpList undo_ops) break; case tc::ffi::TCReplicaOpType::Update: std::cout << "Update " << replica.get_op_uuid(op) << "\n"; - std::cout << " " << replica.get_op_property(op) << ": " << option_string(replica.get_op_old_value(op)) << " -> " << option_string(replica.get_op_value(op)); + std::cout << " " << replica.get_op_property(op) << ": " + << option_string(replica.get_op_old_value(op)) << " -> " + << option_string(replica.get_op_value(op)); break; case tc::ffi::TCReplicaOpType::UndoPoint: - throw std::string ("Can't undo UndoPoint."); + throw std::string("Can't undo UndoPoint."); break; default: - throw std::string ("Can't undo non-operation."); + throw std::string("Can't undo non-operation."); break; } std::cout << "\n"; } - return ! Context::getContext ().config.getBoolean ("confirmation") || - confirm ("The undo command is not reversible. Are you sure you want to revert to the previous state?"); + return !Context::getContext().config.getBoolean("confirmation") || + confirm( + "The undo command is not reversible. Are you sure you want to revert to the previous " + "state?"); } //////////////////////////////////////////////////////////////////////////////// -std::string TDB2::option_string(std::string input) { - return input == "" ? "" : input; -} +std::string TDB2::option_string(std::string input) { return input == "" ? "" : input; } //////////////////////////////////////////////////////////////////////////////// -void TDB2::show_diff ( - const std::string& current, - const std::string& prior, - const std::string& when) -{ - Datetime lastChange (strtoll (when.c_str (), nullptr, 10)); +void TDB2::show_diff(const std::string& current, const std::string& prior, + const std::string& when) { + Datetime lastChange(strtoll(when.c_str(), nullptr, 10)); // Set the colors. - Color color_red (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.before") : ""); - Color color_green (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.after") : ""); + Color color_red( + Context::getContext().color() ? Context::getContext().config.get("color.undo.before") : ""); + Color color_green( + Context::getContext().color() ? Context::getContext().config.get("color.undo.after") : ""); auto before = prior == "" ? Task() : Task(prior); auto after = Task(current); - if (Context::getContext ().config.get ("undo.style") == "side") - { + if (Context::getContext().config.get("undo.style") == "side") { Table view = before.diffForUndoSide(after); std::cout << '\n' - << format ("The last modification was made {1}", lastChange.toString ()) + << format("The last modification was made {1}", lastChange.toString()) << '\n' << '\n' - << '\n' - << view.render () - << '\n'; + << view.render() << '\n'; } - else if (Context::getContext ().config.get ("undo.style") == "diff") - { + else if (Context::getContext().config.get("undo.style") == "diff") { Table view = before.diffForUndoPatch(after, lastChange); - std::cout << '\n' - << view.render () - << '\n'; + std::cout << '\n' << view.render() << '\n'; } } //////////////////////////////////////////////////////////////////////////////// -void TDB2::gc () -{ +void TDB2::gc() { Timer timer; // Allowed as an override, but not recommended. - if (Context::getContext ().config.getBoolean ("gc")) - { - replica.rebuild_working_set (true); + if (Context::getContext().config.getBoolean("gc")) { + replica.rebuild_working_set(true); } - Context::getContext ().time_gc_us += timer.total_us (); + Context::getContext().time_gc_us += timer.total_us(); } //////////////////////////////////////////////////////////////////////////////// -void TDB2::expire_tasks () -{ - replica.expire_tasks (); -} +void TDB2::expire_tasks() { replica.expire_tasks(); } //////////////////////////////////////////////////////////////////////////////// // Latest ID is that of the last pending task. -int TDB2::latest_id () -{ - const tc::WorkingSet &ws = working_set (); - return (int)ws.largest_index (); +int TDB2::latest_id() { + const tc::WorkingSet& ws = working_set(); + return (int)ws.largest_index(); } //////////////////////////////////////////////////////////////////////////////// -const std::vector TDB2::all_tasks () -{ +const std::vector TDB2::all_tasks() { auto all_tctasks = replica.all_tasks(); - std::vector all; - for (auto& tctask : all_tctasks) - all.push_back (Task (std::move (tctask))); + std::vector all; + for (auto& tctask : all_tctasks) all.push_back(Task(std::move(tctask))); return all; } //////////////////////////////////////////////////////////////////////////////// -const std::vector TDB2::pending_tasks () -{ - const tc::WorkingSet &ws = working_set (); - auto largest_index = ws.largest_index (); +const std::vector TDB2::pending_tasks() { + const tc::WorkingSet& ws = working_set(); + auto largest_index = ws.largest_index(); - std::vector result; + std::vector result; for (size_t i = 0; i <= largest_index; i++) { - auto maybe_uuid = ws.by_index (i); - if (maybe_uuid.has_value ()) { - auto maybe_task = replica.get_task (maybe_uuid.value ()); - if (maybe_task.has_value ()) { - result.push_back (Task (std::move (maybe_task.value ()))); + auto maybe_uuid = ws.by_index(i); + if (maybe_uuid.has_value()) { + auto maybe_task = replica.get_task(maybe_uuid.value()); + if (maybe_task.has_value()) { + result.push_back(Task(std::move(maybe_task.value()))); } } } @@ -374,16 +352,15 @@ const std::vector TDB2::pending_tasks () } //////////////////////////////////////////////////////////////////////////////// -const std::vector TDB2::completed_tasks () -{ +const std::vector TDB2::completed_tasks() { auto all_tctasks = replica.all_tasks(); - const tc::WorkingSet &ws = working_set (); + const tc::WorkingSet& ws = working_set(); - std::vector result; + std::vector result; for (auto& tctask : all_tctasks) { // if this task is _not_ in the working set, return it. - if (!ws.by_uuid (tctask.get_uuid ())) { - result.push_back (Task (std::move (tctask))); + if (!ws.by_uuid(tctask.get_uuid())) { + result.push_back(Task(std::move(tctask))); } } @@ -392,10 +369,9 @@ const std::vector TDB2::completed_tasks () //////////////////////////////////////////////////////////////////////////////// // Locate task by ID, wherever it is. -bool TDB2::get (int id, Task& task) -{ - const tc::WorkingSet &ws = working_set (); - const auto maybe_uuid = ws.by_index (id); +bool TDB2::get(int id, Task& task) { + const tc::WorkingSet& ws = working_set(); + const auto maybe_uuid = ws.by_index(id); if (maybe_uuid) { auto maybe_task = replica.get_task(*maybe_uuid); if (maybe_task) { @@ -409,25 +385,24 @@ bool TDB2::get (int id, Task& task) //////////////////////////////////////////////////////////////////////////////// // Locate task by UUID, including by partial ID, wherever it is. -bool TDB2::get (const std::string& uuid, Task& task) -{ +bool TDB2::get(const std::string& uuid, Task& task) { // try by raw uuid, if the length is right - if (uuid.size () == 36) { + if (uuid.size() == 36) { try { - auto maybe_task = replica.get_task (uuid); + auto maybe_task = replica.get_task(uuid); if (maybe_task) { - task = Task{std::move (*maybe_task)}; + task = Task{std::move(*maybe_task)}; return true; } - } catch (const std::string &err) { + } catch (const std::string& err) { return false; } } // Nothing to do but iterate over all tasks and check whether it's closeEnough - for (auto& tctask : replica.all_tasks ()) { - if (closeEnough (tctask.get_uuid (), uuid, uuid.length ())) { - task = Task{std::move (tctask)}; + for (auto& tctask : replica.all_tasks()) { + if (closeEnough(tctask.get_uuid(), uuid, uuid.length())) { + task = Task{std::move(tctask)}; return true; } } @@ -437,34 +412,25 @@ bool TDB2::get (const std::string& uuid, Task& task) //////////////////////////////////////////////////////////////////////////////// // Locate task by UUID, wherever it is. -bool TDB2::has (const std::string& uuid) -{ +bool TDB2::has(const std::string& uuid) { Task task; return get(uuid, task); } //////////////////////////////////////////////////////////////////////////////// -const std::vector TDB2::siblings (Task& task) -{ - std::vector results; - if (task.has ("parent")) - { - std::string parent = task.get ("parent"); +const std::vector TDB2::siblings(Task& task) { + std::vector results; + if (task.has("parent")) { + std::string parent = task.get("parent"); - for (auto& i : this->pending_tasks()) - { + for (auto& i : this->pending_tasks()) { // Do not include self in results. - if (i.id != task.id) - { + if (i.id != task.id) { // Do not include completed or deleted tasks. - if (i.getStatus () != Task::completed && - i.getStatus () != Task::deleted) - { + if (i.getStatus() != Task::completed && i.getStatus() != Task::deleted) { // If task has the same parent, it is a sibling. - if (i.has ("parent") && - i.get ("parent") == parent) - { - results.push_back (i); + if (i.has("parent") && i.get("parent") == parent) { + results.push_back(i); } } } @@ -475,41 +441,40 @@ const std::vector TDB2::siblings (Task& task) } //////////////////////////////////////////////////////////////////////////////// -const std::vector TDB2::children (Task& parent) -{ +const std::vector TDB2::children(Task& parent) { // scan _pending_ tasks for those with `parent` equal to this task - std::vector results; - std::string this_uuid = parent.get ("uuid"); + std::vector results; + std::string this_uuid = parent.get("uuid"); - const tc::WorkingSet &ws = working_set (); - size_t end_idx = ws.largest_index (); + const tc::WorkingSet& ws = working_set(); + size_t end_idx = ws.largest_index(); for (size_t i = 0; i <= end_idx; i++) { - auto uuid_opt = ws.by_index (i); + auto uuid_opt = ws.by_index(i); if (!uuid_opt) { continue; } - auto uuid = uuid_opt.value (); + auto uuid = uuid_opt.value(); // skip self-references if (uuid == this_uuid) { continue; } - auto task_opt = replica.get_task (uuid_opt.value ()); + auto task_opt = replica.get_task(uuid_opt.value()); if (!task_opt) { continue; } - auto task = std::move (task_opt.value ()); + auto task = std::move(task_opt.value()); - auto parent_uuid_opt = task.get_value ("parent"); + auto parent_uuid_opt = task.get_value("parent"); if (!parent_uuid_opt) { continue; } - auto parent_uuid = parent_uuid_opt.value (); + auto parent_uuid = parent_uuid_opt.value(); if (parent_uuid == this_uuid) { - results.push_back (Task (std::move (task))); + results.push_back(Task(std::move(task))); } } @@ -517,40 +482,30 @@ const std::vector TDB2::children (Task& parent) } //////////////////////////////////////////////////////////////////////////////// -std::string TDB2::uuid (int id) -{ - const tc::WorkingSet &ws = working_set (); - return ws.by_index ((size_t)id).value_or (""); +std::string TDB2::uuid(int id) { + const tc::WorkingSet& ws = working_set(); + return ws.by_index((size_t)id).value_or(""); } //////////////////////////////////////////////////////////////////////////////// -int TDB2::id (const std::string& uuid) -{ - const tc::WorkingSet &ws = working_set (); - return (int)ws.by_uuid (uuid).value_or (0); +int TDB2::id(const std::string& uuid) { + const tc::WorkingSet& ws = working_set(); + return (int)ws.by_uuid(uuid).value_or(0); } //////////////////////////////////////////////////////////////////////////////// -int TDB2::num_local_changes () -{ - return (int)replica.num_local_operations (); -} +int TDB2::num_local_changes() { return (int)replica.num_local_operations(); } //////////////////////////////////////////////////////////////////////////////// -int TDB2::num_reverts_possible () -{ - return (int)replica.num_undo_points (); -} +int TDB2::num_reverts_possible() { return (int)replica.num_undo_points(); } //////////////////////////////////////////////////////////////////////////////// -void TDB2::sync (tc::Server server, bool avoid_snapshots) -{ +void TDB2::sync(tc::Server server, bool avoid_snapshots) { replica.sync(std::move(server), avoid_snapshots); } //////////////////////////////////////////////////////////////////////////////// -void TDB2::dump () -{ +void TDB2::dump() { // TODO } @@ -558,24 +513,16 @@ void TDB2::dump () // For any task that has depenencies, follow the chain of dependencies until the // end. Along the way, update the Task::is_blocked and Task::is_blocking data // cache. -static void dependency_scan (std::vector &tasks) -{ - for (auto& left : tasks) - { - for (auto& dep : left.getDependencyUUIDs ()) - { - for (auto& right : tasks) - { - if (right.get ("uuid") == dep) - { +static void dependency_scan(std::vector& tasks) { + for (auto& left : tasks) { + for (auto& dep : left.getDependencyUUIDs()) { + for (auto& right : tasks) { + if (right.get("uuid") == dep) { // GC hasn't run yet, check both tasks for their current status - Task::status lstatus = left.getStatus (); - Task::status rstatus = right.getStatus (); - if (lstatus != Task::completed && - lstatus != Task::deleted && - rstatus != Task::completed && - rstatus != Task::deleted) - { + Task::status lstatus = left.getStatus(); + Task::status rstatus = right.getStatus(); + if (lstatus != Task::completed && lstatus != Task::deleted && + rstatus != Task::completed && rstatus != Task::deleted) { left.is_blocked = true; right.is_blocking = true; } diff --git a/src/TDB2.h b/src/TDB2.h index 87bfa325d..03c8dca5b 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -27,71 +27,71 @@ #ifndef INCLUDED_TDB2 #define INCLUDED_TDB2 -#include -#include -#include -#include -#include -#include #include #include -#include +#include #include +#include + +#include +#include +#include +#include +#include namespace tc { class Server; } // TDB2 Class represents all the files in the task database. -class TDB2 -{ -public: +class TDB2 { + public: static bool debug_mode; - TDB2 (); + TDB2(); - void open_replica (const std::string&, bool create_if_missing); - void add (Task&); - void modify (Task&); - void purge (Task&); - void get_changes (std::vector &); - void revert (); - void gc (); - void expire_tasks (); - int latest_id (); + void open_replica(const std::string &, bool create_if_missing); + void add(Task &); + void modify(Task &); + void purge(Task &); + void get_changes(std::vector &); + void revert(); + void gc(); + void expire_tasks(); + int latest_id(); // Generalized task accessors. - const std::vector all_tasks (); - const std::vector pending_tasks (); - const std::vector completed_tasks (); - bool get (int, Task&); - bool get (const std::string&, Task&); - bool has (const std::string&); - const std::vector siblings (Task&); - const std::vector children (Task&); + const std::vector all_tasks(); + const std::vector pending_tasks(); + const std::vector completed_tasks(); + bool get(int, Task &); + bool get(const std::string &, Task &); + bool has(const std::string &); + const std::vector siblings(Task &); + const std::vector children(Task &); // ID <--> UUID mapping. - std::string uuid (int); - int id (const std::string&); + std::string uuid(int); + int id(const std::string &); - int num_local_changes (); - int num_reverts_possible (); + int num_local_changes(); + int num_reverts_possible(); - void dump (); + void dump(); - void sync (tc::Server server, bool avoid_snapshots); + void sync(tc::Server server, bool avoid_snapshots); bool confirm_revert(struct tc::ffi::TCReplicaOpList); -private: + private: tc::Replica replica; std::optional _working_set; // UUID -> Task containing all tasks modified in this invocation. std::map changes; - const tc::WorkingSet &working_set (); - static std::string option_string (std::string input); - static void show_diff (const std::string&, const std::string&, const std::string&); + const tc::WorkingSet &working_set(); + static std::string option_string(std::string input); + static void show_diff(const std::string &, const std::string &, const std::string &); }; #endif diff --git a/src/Task.cpp b/src/Task.cpp index d483fdd52..a62533cf7 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -28,66 +28,66 @@ // cmake.h include header must come first #include -#include -#include #include +#include + +#include #include #ifdef PRODUCT_TASKWARRIOR -#include #include +#include #endif -#include -#include #include + +#include +#include #ifdef PRODUCT_TASKWARRIOR #include #include #endif -#include #include +#include #ifdef PRODUCT_TASKWARRIOR #include #endif -#include #include +#include #include #ifdef PRODUCT_TASKWARRIOR +#include +#include +#include #include -#include -#include -#include - - -#define APPROACHING_INFINITY 1000 // Close enough. This isn't rocket surgery. +#define APPROACHING_INFINITY 1000 // Close enough. This isn't rocket surgery. static const float epsilon = 0.000001; #endif -std::string Task::defaultProject = ""; -std::string Task::defaultDue = ""; +std::string Task::defaultProject = ""; +std::string Task::defaultDue = ""; std::string Task::defaultScheduled = ""; -bool Task::searchCaseSensitive = true; -bool Task::regex = false; -std::map Task::attributes; +bool Task::searchCaseSensitive = true; +bool Task::regex = false; +std::map Task::attributes; -std::map Task::coefficients; -float Task::urgencyProjectCoefficient = 0.0; -float Task::urgencyActiveCoefficient = 0.0; -float Task::urgencyScheduledCoefficient = 0.0; -float Task::urgencyWaitingCoefficient = 0.0; -float Task::urgencyBlockedCoefficient = 0.0; +std::map Task::coefficients; +float Task::urgencyProjectCoefficient = 0.0; +float Task::urgencyActiveCoefficient = 0.0; +float Task::urgencyScheduledCoefficient = 0.0; +float Task::urgencyWaitingCoefficient = 0.0; +float Task::urgencyBlockedCoefficient = 0.0; float Task::urgencyAnnotationsCoefficient = 0.0; -float Task::urgencyTagsCoefficient = 0.0; -float Task::urgencyDueCoefficient = 0.0; -float Task::urgencyBlockingCoefficient = 0.0; -float Task::urgencyAgeCoefficient = 0.0; -float Task::urgencyAgeMax = 0.0; +float Task::urgencyTagsCoefficient = 0.0; +float Task::urgencyDueCoefficient = 0.0; +float Task::urgencyBlockingCoefficient = 0.0; +float Task::urgencyAgeCoefficient = 0.0; +float Task::urgencyAgeMax = 0.0; -std::map > Task::customOrder; +std::map> Task::customOrder; -static const std::string dummy (""); +static const std::string dummy(""); //////////////////////////////////////////////////////////////////////////////// // The uuid and id attributes must be exempt from comparison. @@ -101,86 +101,84 @@ static const std::string dummy (""); // These two conditions are necessary. They are also sufficient, since there // can be no extra data attribute in the second set, due to the same attribute // set sizes. -bool Task::operator== (const Task& other) -{ - if (data.size () != other.data.size ()) - return false; +bool Task::operator==(const Task& other) { + if (data.size() != other.data.size()) return false; for (const auto& i : data) - if (i.first != "uuid" && - i.second != other.get (i.first)) - return false; + if (i.first != "uuid" && i.second != other.get(i.first)) return false; return true; } //////////////////////////////////////////////////////////////////////////////// -bool Task::operator!= (const Task& other) -{ - return !(*this == other); -} +bool Task::operator!=(const Task& other) { return !(*this == other); } //////////////////////////////////////////////////////////////////////////////// -Task::Task (const std::string& input) -{ - id = 0; - urgency_value = 0.0; - recalc_urgency = true; - is_blocked = false; - is_blocking = false; +Task::Task(const std::string& input) { + id = 0; + urgency_value = 0.0; + recalc_urgency = true; + is_blocked = false; + is_blocking = false; annotation_count = 0; - parse (input); + parse(input); } //////////////////////////////////////////////////////////////////////////////// -Task::Task (const json::object* obj) -{ - id = 0; - urgency_value = 0.0; - recalc_urgency = true; - is_blocked = false; - is_blocking = false; +Task::Task(const json::object* obj) { + id = 0; + urgency_value = 0.0; + recalc_urgency = true; + is_blocked = false; + is_blocking = false; annotation_count = 0; - parseJSON (obj); + parseJSON(obj); } //////////////////////////////////////////////////////////////////////////////// -Task::Task (tc::Task obj) -{ - id = 0; - urgency_value = 0.0; - recalc_urgency = true; - is_blocked = false; - is_blocking = false; +Task::Task(tc::Task obj) { + id = 0; + urgency_value = 0.0; + recalc_urgency = true; + is_blocked = false; + is_blocking = false; annotation_count = 0; - parseTC (obj); + parseTC(obj); } //////////////////////////////////////////////////////////////////////////////// -Task::status Task::textToStatus (const std::string& input) -{ - if (input[0] == 'p') return Task::pending; - else if (input[0] == 'c') return Task::completed; - else if (input[0] == 'd') return Task::deleted; - else if (input[0] == 'r') return Task::recurring; +Task::status Task::textToStatus(const std::string& input) { + if (input[0] == 'p') + return Task::pending; + else if (input[0] == 'c') + return Task::completed; + else if (input[0] == 'd') + return Task::deleted; + else if (input[0] == 'r') + return Task::recurring; // for compatibility, parse `w` as pending; Task::getStatus will // apply the virtual waiting status if appropriate - else if (input[0] == 'w') return Task::pending; + else if (input[0] == 'w') + return Task::pending; - throw format ("The status '{1}' is not valid.", input); + throw format("The status '{1}' is not valid.", input); } //////////////////////////////////////////////////////////////////////////////// -std::string Task::statusToText (Task::status s) -{ - if (s == Task::pending) return "pending"; - else if (s == Task::recurring) return "recurring"; - else if (s == Task::waiting) return "waiting"; - else if (s == Task::completed) return "completed"; - else if (s == Task::deleted) return "deleted"; +std::string Task::statusToText(Task::status s) { + if (s == Task::pending) + return "pending"; + else if (s == Task::recurring) + return "recurring"; + else if (s == Task::waiting) + return "waiting"; + else if (s == Task::completed) + return "completed"; + else if (s == Task::deleted) + return "deleted"; return "pending"; } @@ -188,161 +186,133 @@ std::string Task::statusToText (Task::status s) //////////////////////////////////////////////////////////////////////////////// // Returns a proper handle to the task. Tasks should not be referenced by UUIDs // as long as they have non-zero ID. -const std::string Task::identifier (bool shortened /* = false */) const -{ +const std::string Task::identifier(bool shortened /* = false */) const { if (id != 0) - return format (id); + return format(id); else if (shortened) - return get ("uuid").substr (0, 8); + return get("uuid").substr(0, 8); else - return get ("uuid"); + return get("uuid"); } //////////////////////////////////////////////////////////////////////////////// -void Task::setAsNow (const std::string& att) -{ +void Task::setAsNow(const std::string& att) { char now[22]; - snprintf (now, 22, "%lli", (long long int) time (nullptr)); - set (att, now); + snprintf(now, 22, "%lli", (long long int)time(nullptr)); + set(att, now); recalc_urgency = true; } //////////////////////////////////////////////////////////////////////////////// -bool Task::has (const std::string& name) const -{ - if (data.find (name) != data.end ()) - return true; +bool Task::has(const std::string& name) const { + if (data.find(name) != data.end()) return true; return false; } //////////////////////////////////////////////////////////////////////////////// -std::vector Task::all () const -{ - std::vector all; - for (const auto& i : data) - all.push_back (i.first); +std::vector Task::all() const { + std::vector all; + for (const auto& i : data) all.push_back(i.first); return all; } //////////////////////////////////////////////////////////////////////////////// -const std::string Task::get (const std::string& name) const -{ - auto i = data.find (name); - if (i != data.end ()) - return i->second; +const std::string Task::get(const std::string& name) const { + auto i = data.find(name); + if (i != data.end()) return i->second; return ""; } //////////////////////////////////////////////////////////////////////////////// -const std::string& Task::get_ref (const std::string& name) const -{ - auto i = data.find (name); - if (i != data.end ()) - return i->second; +const std::string& Task::get_ref(const std::string& name) const { + auto i = data.find(name); + if (i != data.end()) return i->second; return dummy; } //////////////////////////////////////////////////////////////////////////////// -int Task::get_int (const std::string& name) const -{ - auto i = data.find (name); - if (i != data.end ()) - return strtol (i->second.c_str (), nullptr, 10); +int Task::get_int(const std::string& name) const { + auto i = data.find(name); + if (i != data.end()) return strtol(i->second.c_str(), nullptr, 10); return 0; } //////////////////////////////////////////////////////////////////////////////// -unsigned long Task::get_ulong (const std::string& name) const -{ - auto i = data.find (name); - if (i != data.end ()) - return strtoul (i->second.c_str (), nullptr, 10); +unsigned long Task::get_ulong(const std::string& name) const { + auto i = data.find(name); + if (i != data.end()) return strtoul(i->second.c_str(), nullptr, 10); return 0; } //////////////////////////////////////////////////////////////////////////////// -float Task::get_float (const std::string& name) const -{ - auto i = data.find (name); - if (i != data.end ()) - return strtof (i->second.c_str (), nullptr); +float Task::get_float(const std::string& name) const { + auto i = data.find(name); + if (i != data.end()) return strtof(i->second.c_str(), nullptr); return 0.0; } //////////////////////////////////////////////////////////////////////////////// -time_t Task::get_date (const std::string& name) const -{ - auto i = data.find (name); - if (i != data.end ()) - return (time_t) strtoul (i->second.c_str (), nullptr, 10); +time_t Task::get_date(const std::string& name) const { + auto i = data.find(name); + if (i != data.end()) return (time_t)strtoul(i->second.c_str(), nullptr, 10); return 0; } //////////////////////////////////////////////////////////////////////////////// -void Task::set (const std::string& name, const std::string& value) -{ +void Task::set(const std::string& name, const std::string& value) { data[name] = value; - if (isAnnotationAttr (name)) - ++annotation_count; + if (isAnnotationAttr(name)) ++annotation_count; recalc_urgency = true; } //////////////////////////////////////////////////////////////////////////////// -void Task::set (const std::string& name, long long value) -{ - data[name] = format (value); +void Task::set(const std::string& name, long long value) { + data[name] = format(value); recalc_urgency = true; } //////////////////////////////////////////////////////////////////////////////// -void Task::remove (const std::string& name) -{ - if (data.erase (name)) - recalc_urgency = true; +void Task::remove(const std::string& name) { + if (data.erase(name)) recalc_urgency = true; - if (isAnnotationAttr (name)) - --annotation_count; + if (isAnnotationAttr(name)) --annotation_count; } //////////////////////////////////////////////////////////////////////////////// -Task::status Task::getStatus () const -{ - if (! has ("status")) - return Task::pending; +Task::status Task::getStatus() const { + if (!has("status")) return Task::pending; - auto status = textToStatus (get ("status")); + auto status = textToStatus(get("status")); // Implement the "virtual" Task::waiting status, which is not stored on-disk // but is defined as a pending task with a `wait` attribute in the future. // This is workaround for 2.6.0, remove in 3.0.0. - if (status == Task::pending && is_waiting ()) { - return Task::waiting; + if (status == Task::pending && is_waiting()) { + return Task::waiting; } return status; } //////////////////////////////////////////////////////////////////////////////// -void Task::setStatus (Task::status status) -{ +void Task::setStatus(Task::status status) { // the 'waiting' status is a virtual version of 'pending', so translate // that back to 'pending' here - if (status == Task::waiting) - status = Task::pending; + if (status == Task::waiting) status = Task::pending; - set ("status", statusToText (status)); + set("status", statusToText(status)); recalc_urgency = true; } @@ -350,33 +320,27 @@ void Task::setStatus (Task::status status) #ifdef PRODUCT_TASKWARRIOR //////////////////////////////////////////////////////////////////////////////// // Determines status of a date attribute. -Task::dateState Task::getDateState (const std::string& name) const -{ - std::string value = get (name); - if (value.length ()) - { - Datetime reference (value); +Task::dateState Task::getDateState(const std::string& name) const { + std::string value = get(name); + if (value.length()) { + Datetime reference(value); Datetime now; - Datetime today ("today"); + Datetime today("today"); - if (reference < today) - return dateBeforeToday; + if (reference < today) return dateBeforeToday; - if (reference.sameDay (now)) - { + if (reference.sameDay(now)) { if (reference < now) return dateEarlierToday; else return dateLaterToday; } - int imminentperiod = Context::getContext ().config.getInteger ("due"); - if (imminentperiod == 0) - return dateAfterToday; + int imminentperiod = Context::getContext().config.getInteger("due"); + if (imminentperiod == 0) return dateAfterToday; Datetime imminentDay = today + imminentperiod * 86400; - if (reference < imminentDay) - return dateAfterToday; + if (reference < imminentDay) return dateAfterToday; } return dateNotDue; @@ -385,36 +349,24 @@ Task::dateState Task::getDateState (const std::string& name) const //////////////////////////////////////////////////////////////////////////////// // An empty task is typically a "dummy", such as in DOM evaluation, which may or // may not occur in the context of a task. -bool Task::is_empty () const -{ - return data.size () == 0; -} +bool Task::is_empty() const { return data.size() == 0; } //////////////////////////////////////////////////////////////////////////////// // Ready means pending, not blocked and either not scheduled or scheduled before // now. -bool Task::is_ready () const -{ - return getStatus () == Task::pending && - ! is_blocked && - (! has ("scheduled") || - Datetime ("now").operator> (get_date ("scheduled"))); +bool Task::is_ready() const { + return getStatus() == Task::pending && !is_blocked && + (!has("scheduled") || Datetime("now").operator>(get_date("scheduled"))); } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_due () const -{ - if (has ("due")) - { - Task::status status = getStatus (); +bool Task::is_due() const { + if (has("due")) { + Task::status status = getStatus(); - if (status != Task::completed && - status != Task::deleted) - { - Task::dateState state = getDateState ("due"); - if (state == dateAfterToday || - state == dateEarlierToday || - state == dateLaterToday) + if (status != Task::completed && status != Task::deleted) { + Task::dateState state = getDateState("due"); + if (state == dateAfterToday || state == dateEarlierToday || state == dateLaterToday) return true; } } @@ -423,17 +375,12 @@ bool Task::is_due () const } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_dueyesterday () const -{ - if (has ("due")) - { - Task::status status = getStatus (); +bool Task::is_dueyesterday() const { + if (has("due")) { + Task::status status = getStatus(); - if (status != Task::completed && - status != Task::deleted) - { - if (Datetime ("yesterday").sameDay (get_date ("due"))) - return true; + if (status != Task::completed && status != Task::deleted) { + if (Datetime("yesterday").sameDay(get_date("due"))) return true; } } @@ -441,19 +388,13 @@ bool Task::is_dueyesterday () const } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_duetoday () const -{ - if (has ("due")) - { - Task::status status = getStatus (); +bool Task::is_duetoday() const { + if (has("due")) { + Task::status status = getStatus(); - if (status != Task::completed && - status != Task::deleted) - { - Task::dateState state = getDateState ("due"); - if (state == dateEarlierToday || - state == dateLaterToday) - return true; + if (status != Task::completed && status != Task::deleted) { + Task::dateState state = getDateState("due"); + if (state == dateEarlierToday || state == dateLaterToday) return true; } } @@ -461,17 +402,12 @@ bool Task::is_duetoday () const } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_duetomorrow () const -{ - if (has ("due")) - { - Task::status status = getStatus (); +bool Task::is_duetomorrow() const { + if (has("due")) { + Task::status status = getStatus(); - if (status != Task::completed && - status != Task::deleted) - { - if (Datetime ("tomorrow").sameDay (get_date ("due"))) - return true; + if (status != Task::completed && status != Task::deleted) { + if (Datetime("tomorrow").sameDay(get_date("due"))) return true; } } @@ -479,19 +415,13 @@ bool Task::is_duetomorrow () const } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_dueweek () const -{ - if (has ("due")) - { - Task::status status = getStatus (); +bool Task::is_dueweek() const { + if (has("due")) { + Task::status status = getStatus(); - if (status != Task::completed && - status != Task::deleted) - { - Datetime due (get_date ("due")); - if (due >= Datetime ("sow") && - due <= Datetime ("eow")) - return true; + if (status != Task::completed && status != Task::deleted) { + Datetime due(get_date("due")); + if (due >= Datetime("sow") && due <= Datetime("eow")) return true; } } @@ -499,19 +429,13 @@ bool Task::is_dueweek () const } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_duemonth () const -{ - if (has ("due")) - { - Task::status status = getStatus (); +bool Task::is_duemonth() const { + if (has("due")) { + Task::status status = getStatus(); - if (status != Task::completed && - status != Task::deleted) - { - Datetime due (get_date ("due")); - if (due >= Datetime ("som") && - due <= Datetime ("eom")) - return true; + if (status != Task::completed && status != Task::deleted) { + Datetime due(get_date("due")); + if (due >= Datetime("som") && due <= Datetime("eom")) return true; } } @@ -519,19 +443,13 @@ bool Task::is_duemonth () const } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_duequarter () const -{ - if (has ("due")) - { - Task::status status = getStatus (); +bool Task::is_duequarter() const { + if (has("due")) { + Task::status status = getStatus(); - if (status != Task::completed && - status != Task::deleted) - { - Datetime due (get_date ("due")); - if (due >= Datetime ("soq") && - due <= Datetime ("eoq")) - return true; + if (status != Task::completed && status != Task::deleted) { + Datetime due(get_date("due")); + if (due >= Datetime("soq") && due <= Datetime("eoq")) return true; } } @@ -539,19 +457,13 @@ bool Task::is_duequarter () const } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_dueyear () const -{ - if (has ("due")) - { - Task::status status = getStatus (); +bool Task::is_dueyear() const { + if (has("due")) { + Task::status status = getStatus(); - if (status != Task::completed && - status != Task::deleted) - { - Datetime due (get_date ("due")); - if (due >= Datetime ("soy") && - due <= Datetime ("eoy")) - return true; + if (status != Task::completed && status != Task::deleted) { + Datetime due(get_date("due")); + if (due >= Datetime("soy") && due <= Datetime("eoy")) return true; } } @@ -559,44 +471,31 @@ bool Task::is_dueyear () const } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_udaPresent () const -{ - for (auto& col : Context::getContext ().columns) - if (col.second->is_uda () && - has (col.first)) - return true; +bool Task::is_udaPresent() const { + for (auto& col : Context::getContext().columns) + if (col.second->is_uda() && has(col.first)) return true; return false; } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_orphanPresent () const -{ +bool Task::is_orphanPresent() const { for (auto& att : data) - if (! isAnnotationAttr (att.first) && - ! isTagAttr (att.first) && - ! isDepAttr (att.first) && - Context::getContext ().columns.find (att.first) == Context::getContext ().columns.end ()) + if (!isAnnotationAttr(att.first) && !isTagAttr(att.first) && !isDepAttr(att.first) && + Context::getContext().columns.find(att.first) == Context::getContext().columns.end()) return true; return false; } //////////////////////////////////////////////////////////////////////////////// -bool Task::is_overdue () const -{ - if (has ("due")) - { - Task::status status = getStatus (); +bool Task::is_overdue() const { + if (has("due")) { + Task::status status = getStatus(); - if (status != Task::completed && - status != Task::deleted && - status != Task::recurring) - { - Task::dateState state = getDateState ("due"); - if (state == dateEarlierToday || - state == dateBeforeToday) - return true; + if (status != Task::completed && status != Task::deleted && status != Task::recurring) { + Task::dateState state = getDateState("due"); + if (state == dateEarlierToday || state == dateBeforeToday) return true; } } @@ -610,14 +509,11 @@ bool Task::is_overdue () const // While this is not consistent with other attribute-based virtual tags, such // as +BLOCKED, it is more backwards compatible with how +WAITING virtual tag // behaved in the past, when waiting had a dedicated status value. -bool Task::is_waiting () const -{ - if (has ("wait") && get ("status") == "pending") - { +bool Task::is_waiting() const { + if (has("wait") && get("status") == "pending") { Datetime now; - Datetime wait (get_date ("wait")); - if (wait > now) - return true; + Datetime wait(get_date("wait")); + if (wait > now) return true; } return false; @@ -625,12 +521,11 @@ bool Task::is_waiting () const //////////////////////////////////////////////////////////////////////////////// // Try a JSON parse. -void Task::parse (const std::string& input) -{ - parseJSON (input); +void Task::parse(const std::string& input) { + parseJSON(input); // for compatibility, include all tags in `tags` as `tag_..` attributes - if (data.find ("tags") != data.end ()) { + if (data.find("tags") != data.end()) { for (auto& tag : split(data["tags"], ',')) { data[tag2Attr(tag)] = "x"; } @@ -639,7 +534,7 @@ void Task::parse (const std::string& input) fixTagsAttribute(); // same for `depends` / `dep_..` - if (data.find ("depends") != data.end ()) { + if (data.find("depends") != data.end()) { for (auto& dep : split(data["depends"], ',')) { data[dep2Attr(dep)] = "x"; } @@ -651,27 +546,21 @@ void Task::parse (const std::string& input) //////////////////////////////////////////////////////////////////////////////// // Note that all fields undergo encode/decode. -void Task::parseJSON (const std::string& line) -{ +void Task::parseJSON(const std::string& line) { // Parse the whole thing. - json::value* root = json::parse (line); - if (root && - root->type () == json::j_object) - parseJSON ((json::object*) root); + json::value* root = json::parse(line); + if (root && root->type() == json::j_object) parseJSON((json::object*)root); delete root; } //////////////////////////////////////////////////////////////////////////////// -void Task::parseJSON (const json::object* root_obj) -{ +void Task::parseJSON(const json::object* root_obj) { // For each object element... - for (auto& i : root_obj->_data) - { + for (auto& i : root_obj->_data) { // If the attribute is a recognized column. std::string type = Task::attributes[i.first]; - if (type != "") - { + if (type != "") { // Any specified id is ignored. if (i.first == "id") ; @@ -681,51 +570,44 @@ void Task::parseJSON (const json::object* root_obj) ; // TW-1274 Standardization. - else if (i.first == "modification") - { - auto text = i.second->dump (); - Lexer::dequote (text); - Datetime d (text); - set ("modified", d.toEpochString ()); + else if (i.first == "modification") { + auto text = i.second->dump(); + Lexer::dequote(text); + Datetime d(text); + set("modified", d.toEpochString()); } // Dates are converted from ISO to epoch. - else if (type == "date") - { - auto text = i.second->dump (); - Lexer::dequote (text); - Datetime d (text); - set (i.first, text == "" ? "" : d.toEpochString ()); + else if (type == "date") { + auto text = i.second->dump(); + Lexer::dequote(text); + Datetime d(text); + set(i.first, text == "" ? "" : d.toEpochString()); } // Tags are an array of JSON strings. - else if (i.first == "tags" && i.second->type() == json::j_array) - { + else if (i.first == "tags" && i.second->type() == json::j_array) { auto tags = (json::array*)i.second; - for (auto& t : tags->_data) - { + for (auto& t : tags->_data) { auto tag = (json::string*)t; - addTag (tag->_data); + addTag(tag->_data); } } // Dependencies can be exported as an array of strings. // 2016-02-21: This will be the only option in future releases. // See other 2016-02-21 comments for details. - else if (i.first == "depends" && i.second->type() == json::j_array) - { + else if (i.first == "depends" && i.second->type() == json::j_array) { auto deps = (json::array*)i.second; - for (auto& t : deps->_data) - { + for (auto& t : deps->_data) { auto dep = (json::string*)t; - addDependency (dep->_data); + addDependency(dep->_data); } } // Dependencies can be exported as a single comma-separated string. // 2016-02-21: Deprecated - see other 2016-02-21 comments for details. - else if (i.first == "depends" && i.second->type() == json::j_string) - { + else if (i.first == "depends" && i.second->type() == json::j_string) { auto deps = (json::string*)i.second; // Fix for issue#2689: taskserver sometimes encodes the depends @@ -734,64 +616,57 @@ void Task::parseJSON (const json::object* root_obj) // it invalid JSON. Since we know the characters we're looking for, // we'll just filter out everything else. std::string deps_str = deps->_data; - if (deps_str.front () == '[' && deps_str.back () == ']') { + if (deps_str.front() == '[' && deps_str.back() == ']') { std::string filtered; - for (auto &c: deps_str) { - if ((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'f') || - c == ',' || c == '-') { - filtered.push_back(c); - } - } - deps_str = filtered; + for (auto& c : deps_str) { + if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || c == ',' || c == '-') { + filtered.push_back(c); + } + } + deps_str = filtered; } - auto uuids = split (deps_str, ','); + auto uuids = split(deps_str, ','); - for (const auto& uuid : uuids) - addDependency (uuid); + for (const auto& uuid : uuids) addDependency(uuid); } // Strings are decoded. - else if (type == "string") - { - auto text = i.second->dump (); - Lexer::dequote (text); - set (i.first, json::decode (text)); + else if (type == "string") { + auto text = i.second->dump(); + Lexer::dequote(text); + set(i.first, json::decode(text)); } // Other types are simply added. - else - { - auto text = i.second->dump (); - Lexer::dequote (text); - set (i.first, text); + else { + auto text = i.second->dump(); + Lexer::dequote(text); + set(i.first, text); } } // UDA orphans and annotations do not have columns. - else - { + else { // Annotations are an array of JSON objects with 'entry' and // 'description' values and must be converted. - if (i.first == "annotations") - { - std::map annos; + if (i.first == "annotations") { + std::map annos; // Fail if 'annotations' is not an array if (i.second->type() != json::j_array) { - throw format ("Annotations is malformed: {1}", i.second->dump ()); + throw format("Annotations is malformed: {1}", i.second->dump()); } auto atts = (json::array*)i.second; - for (auto& annotations : atts->_data) - { + for (auto& annotations : atts->_data) { auto annotation = (json::object*)annotations; // Extract description. Fail if not present. auto what = (json::string*)annotation->_data["description"]; - if (! what) { - annotation->_data.erase ("description"); // Erase NULL description inserted by failed lookup above - throw format ("Annotation is missing a description: {1}", annotation->dump ()); + if (!what) { + annotation->_data.erase( + "description"); // Erase NULL description inserted by failed lookup above + throw format("Annotation is missing a description: {1}", annotation->dump()); } // Extract 64-bit annotation entry value @@ -801,10 +676,10 @@ void Task::parseJSON (const json::object* root_obj) // Extract entry. Use current time if not present. auto when = (json::string*)annotation->_data["entry"]; if (when) - ann_timestamp = (long long) (Datetime (when->_data).toEpoch ()); + ann_timestamp = (long long)(Datetime(when->_data).toEpoch()); else { - annotation->_data.erase ("entry"); // Erase NULL entry inserted by failed lookup above - ann_timestamp = (long long) (Datetime ().toEpoch ()); + annotation->_data.erase("entry"); // Erase NULL entry inserted by failed lookup above + ann_timestamp = (long long)(Datetime().toEpoch()); } std::stringstream name; @@ -812,34 +687,29 @@ void Task::parseJSON (const json::object* root_obj) // Increment the entry timestamp in case of a conflict. Same // behaviour as CmdAnnotate. - while (annos.find(name.str ()) != annos.end ()) - { - name.str (""); // Clear - ann_timestamp++; - name << "annotation_" << ann_timestamp; + while (annos.find(name.str()) != annos.end()) { + name.str(""); // Clear + ann_timestamp++; + name << "annotation_" << ann_timestamp; } - annos.emplace (name.str (), json::decode (what->_data)); + annos.emplace(name.str(), json::decode(what->_data)); } - setAnnotations (annos); + setAnnotations(annos); } // UDA Orphan - must be preserved. - else - { + else { #ifdef PRODUCT_TASKWARRIOR std::stringstream message; - message << "Task::parseJSON found orphan '" - << i.first - << "' with value '" - << i.second + message << "Task::parseJSON found orphan '" << i.first << "' with value '" << i.second << "' --> preserved\n"; - Context::getContext ().debug (message.str ()); + Context::getContext().debug(message.str()); #endif - auto text = i.second->dump (); - Lexer::dequote (text); - set (i.first, json::decode (text)); + auto text = i.second->dump(); + Lexer::dequote(text); + set(i.first, json::decode(text)); } } } @@ -847,22 +717,19 @@ void Task::parseJSON (const json::object* root_obj) //////////////////////////////////////////////////////////////////////////////// // Note that all fields undergo encode/decode. -void Task::parseTC (const tc::Task& task) -{ - data = task.get_taskmap (); +void Task::parseTC(const tc::Task& task) { + data = task.get_taskmap(); // count annotations annotation_count = 0; - for (auto i : data) - { - if (isAnnotationAttr (i.first)) - { + for (auto i : data) { + if (isAnnotationAttr(i.first)) { ++annotation_count; } } - data["uuid"] = task.get_uuid (); - id = Context::getContext ().tdb2.id (data["uuid"]); + data["uuid"] = task.get_uuid(); + id = Context::getContext().tdb2.id(data["uuid"]); is_blocking = task.is_blocking(); is_blocked = task.is_blocked(); @@ -870,111 +737,96 @@ void Task::parseTC (const tc::Task& task) //////////////////////////////////////////////////////////////////////////////// // No legacy formats are currently supported as of 2.4.0. -void Task::parseLegacy (const std::string& line) -{ - switch (determineVersion (line)) - { - // File format version 1, from 2006-11-27 - 2007-12-31, v0.x+ - v0.9.3 - case 1: throw std::string ("Taskwarrior no longer supports file format 1, originally used between 27 November 2006 and 31 December 2007."); +void Task::parseLegacy(const std::string& line) { + switch (determineVersion(line)) { + // File format version 1, from 2006-11-27 - 2007-12-31, v0.x+ - v0.9.3 + case 1: + throw std::string( + "Taskwarrior no longer supports file format 1, originally used between 27 November 2006 " + "and 31 December 2007."); - // File format version 2, from 2008-1-1 - 2009-3-23, v0.9.3 - v1.5.0 - case 2: throw std::string ("Taskwarrior no longer supports file format 2, originally used between 1 January 2008 and 12 April 2009."); + // File format version 2, from 2008-1-1 - 2009-3-23, v0.9.3 - v1.5.0 + case 2: + throw std::string( + "Taskwarrior no longer supports file format 2, originally used between 1 January 2008 " + "and 12 April 2009."); - // File format version 3, from 2009-3-23 - 2009-05-16, v1.6.0 - v1.7.1 - case 3: throw std::string ("Taskwarrior no longer supports file format 3, originally used between 23 March 2009 and 16 May 2009."); + // File format version 3, from 2009-3-23 - 2009-05-16, v1.6.0 - v1.7.1 + case 3: + throw std::string( + "Taskwarrior no longer supports file format 3, originally used between 23 March 2009 and " + "16 May 2009."); - // File format version 4, from 2009-05-16 - today, v1.7.1+ - case 4: - break; + // File format version 4, from 2009-05-16 - today, v1.7.1+ + case 4: + break; - default: + default: #ifdef PRODUCT_TASKWARRIOR - std::stringstream message; - message << "Invalid fileformat at line '" - << line - << '\''; - Context::getContext ().debug (message.str ()); + std::stringstream message; + message << "Invalid fileformat at line '" << line << '\''; + Context::getContext().debug(message.str()); #endif - throw std::string ("Unrecognized Taskwarrior file format or blank line in data."); - break; + throw std::string("Unrecognized Taskwarrior file format or blank line in data."); + break; } recalc_urgency = true; } //////////////////////////////////////////////////////////////////////////////// -std::string Task::composeJSON (bool decorate /*= false*/) const -{ +std::string Task::composeJSON(bool decorate /*= false*/) const { std::stringstream out; out << '{'; // ID inclusion is optional, but not a good idea, because it remains correct // only until the next gc. - if (decorate) - out << "\"id\":" << id << ','; + if (decorate) out << "\"id\":" << id << ','; // First the non-annotations. int attributes_written = 0; - for (auto& i : data) - { + for (auto& i : data) { // Annotations are not written out here. - if (! i.first.compare (0, 11, "annotation_", 11)) - continue; + if (!i.first.compare(0, 11, "annotation_", 11)) continue; // Tags and dependencies are handled below - if (i.first == "tags" || isTagAttr (i.first)) - continue; - if (i.first == "depends" || isDepAttr (i.first)) - continue; + if (i.first == "tags" || isTagAttr(i.first)) continue; + if (i.first == "depends" || isDepAttr(i.first)) continue; // If value is an empty string, do not ever output it - if (i.second == "") - continue; + if (i.second == "") continue; - if (attributes_written) - out << ','; + if (attributes_written) out << ','; std::string type = Task::attributes[i.first]; - if (type == "") - type = "string"; + if (type == "") type = "string"; // Date fields are written as ISO 8601. - if (type == "date") - { - Datetime d (i.second); - out << '"' - << (i.first == "modification" ? "modified" : i.first) + if (type == "date") { + Datetime d(i.second); + out << '"' << (i.first == "modification" ? "modified" : i.first) << "\":\"" // Date was deleted, do not export parsed empty string - << (i.second == "" ? "" : d.toISO ()) - << '"'; + << (i.second == "" ? "" : d.toISO()) << '"'; ++attributes_written; } -/* - else if (type == "duration") - { - // TODO Emit Datetime - } -*/ - else if (type == "numeric") - { - out << '"' - << i.first - << "\":" - << i.second; + /* + else if (type == "duration") + { + // TODO Emit Datetime + } + */ + else if (type == "numeric") { + out << '"' << i.first << "\":" << i.second; ++attributes_written; } // Everything else is a quoted value. - else - { - out << '"' - << i.first - << "\":\"" - << (type == "string" ? json::encode (i.second) : i.second) + else { + out << '"' << i.first << "\":\"" << (type == "string" ? json::encode(i.second) : i.second) << '"'; ++attributes_written; @@ -982,24 +834,16 @@ std::string Task::composeJSON (bool decorate /*= false*/) const } // Now the annotations, if any. - if (annotation_count) - { - out << ',' - << "\"annotations\":["; + if (annotation_count) { + out << ',' << "\"annotations\":["; int annotations_written = 0; - for (auto& i : data) - { - if (! i.first.compare (0, 11, "annotation_", 11)) - { - if (annotations_written) - out << ','; + for (auto& i : data) { + if (!i.first.compare(0, 11, "annotation_", 11)) { + if (annotations_written) out << ','; - Datetime d (i.first.substr (11)); - out << R"({"entry":")" - << d.toISO () - << R"(","description":")" - << json::encode (i.second) + Datetime d(i.first.substr(11)); + out << R"({"entry":")" << d.toISO() << R"(","description":")" << json::encode(i.second) << "\"}"; ++annotations_written; @@ -1010,16 +854,12 @@ std::string Task::composeJSON (bool decorate /*= false*/) const } auto tags = getTags(); - if (tags.size() > 0) - { - out << ',' - << "\"tags\":["; + if (tags.size() > 0) { + out << ',' << "\"tags\":["; int count = 0; - for (const auto& tag : tags) - { - if (count++) - out << ','; + for (const auto& tag : tags) { + if (count++) out << ','; out << '"' << tag << '"'; } @@ -1028,17 +868,13 @@ std::string Task::composeJSON (bool decorate /*= false*/) const ++attributes_written; } - auto depends = getDependencyUUIDs (); - if (depends.size() > 0) - { - out << ',' - << "\"depends\":["; + auto depends = getDependencyUUIDs(); + if (depends.size() > 0) { + out << ',' << "\"depends\":["; int count = 0; - for (const auto& dep : depends) - { - if (count++) - out << ','; + for (const auto& dep : depends) { + if (count++) out << ','; out << '"' << dep << '"'; } @@ -1049,32 +885,24 @@ std::string Task::composeJSON (bool decorate /*= false*/) const #ifdef PRODUCT_TASKWARRIOR // Include urgency. - if (decorate) - out << ',' - << "\"urgency\":" - << urgency_c (); + if (decorate) out << ',' << "\"urgency\":" << urgency_c(); #endif out << '}'; - return out.str (); + return out.str(); } //////////////////////////////////////////////////////////////////////////////// -int Task::getAnnotationCount () const -{ +int Task::getAnnotationCount() const { int count = 0; for (auto& ann : data) - if (! ann.first.compare (0, 11, "annotation_", 11)) - ++count; + if (!ann.first.compare(0, 11, "annotation_", 11)) ++count; return count; } //////////////////////////////////////////////////////////////////////////////// -bool Task::hasAnnotations () const -{ - return annotation_count ? true : false; -} +bool Task::hasAnnotations() const { return annotation_count ? true : false; } //////////////////////////////////////////////////////////////////////////////// // The timestamp is part of the name: @@ -1082,36 +910,29 @@ bool Task::hasAnnotations () const // // Note that the time is incremented (one second) in order to find a unique // timestamp. -void Task::addAnnotation (const std::string& description) -{ - time_t now = time (nullptr); +void Task::addAnnotation(const std::string& description) { + time_t now = time(nullptr); std::string key; - do - { - key = "annotation_" + format ((long long int) now); + do { + key = "annotation_" + format((long long int)now); ++now; - } - while (has (key)); + } while (has(key)); - data[key] = json::decode (description); + data[key] = json::decode(description); ++annotation_count; recalc_urgency = true; } //////////////////////////////////////////////////////////////////////////////// -void Task::removeAnnotations () -{ +void Task::removeAnnotations() { // Erase old annotations. - auto i = data.begin (); - while (i != data.end ()) - { - if (! i->first.compare (0, 11, "annotation_", 11)) - { + auto i = data.begin(); + while (i != data.end()) { + if (!i->first.compare(0, 11, "annotation_", 11)) { --annotation_count; - data.erase (i++); - } - else + data.erase(i++); + } else i++; } @@ -1119,44 +940,37 @@ void Task::removeAnnotations () } //////////////////////////////////////////////////////////////////////////////// -std::map Task::getAnnotations () const -{ - std::map a; +std::map Task::getAnnotations() const { + std::map a; for (auto& ann : data) - if (! ann.first.compare (0, 11, "annotation_", 11)) - a.insert (ann); + if (!ann.first.compare(0, 11, "annotation_", 11)) a.insert(ann); return a; } //////////////////////////////////////////////////////////////////////////////// -void Task::setAnnotations (const std::map & annotations) -{ +void Task::setAnnotations(const std::map& annotations) { // Erase old annotations. - removeAnnotations (); + removeAnnotations(); - for (auto& anno : annotations) - data.insert (anno); + for (auto& anno : annotations) data.insert(anno); - annotation_count = annotations.size (); + annotation_count = annotations.size(); recalc_urgency = true; } #ifdef PRODUCT_TASKWARRIOR //////////////////////////////////////////////////////////////////////////////// -void Task::addDependency (int depid) -{ +void Task::addDependency(int depid) { // Check that id is resolvable. - std::string uuid = Context::getContext ().tdb2.uuid (depid); - if (uuid == "") - throw format ("Could not create a dependency on task {1} - not found.", depid); + std::string uuid = Context::getContext().tdb2.uuid(depid); + if (uuid == "") throw format("Could not create a dependency on task {1} - not found.", depid); // the addDependency(&std::string) overload will check this, too, but here we // can give an more natural error message containing the id the user // provided. - if (hasDependency (uuid)) - { - Context::getContext ().footnote (format ("Task {1} already depends on task {2}.", id, depid)); + if (hasDependency(uuid)) { + Context::getContext().footnote(format("Task {1} already depends on task {2}.", id, depid)); return; } @@ -1165,26 +979,24 @@ void Task::addDependency (int depid) #endif //////////////////////////////////////////////////////////////////////////////// -void Task::addDependency (const std::string& uuid) -{ - if (uuid == get ("uuid")) - throw std::string ("A task cannot be dependent on itself."); +void Task::addDependency(const std::string& uuid) { + if (uuid == get("uuid")) throw std::string("A task cannot be dependent on itself."); - if (hasDependency (uuid)) - { + if (hasDependency(uuid)) { #ifdef PRODUCT_TASKWARRIOR - Context::getContext ().footnote (format ("Task {1} already depends on task {2}.", get ("uuid"), uuid)); + Context::getContext().footnote( + format("Task {1} already depends on task {2}.", get("uuid"), uuid)); #endif return; } // Store the dependency. - set (dep2Attr (uuid), "x"); + set(dep2Attr(uuid), "x"); // Prevent circular dependencies. #ifdef PRODUCT_TASKWARRIOR - if (dependencyIsCircular (*this)) - throw std::string ("Circular dependency detected and disallowed."); + if (dependencyIsCircular(*this)) + throw std::string("Circular dependency detected and disallowed."); #endif recalc_urgency = true; @@ -1193,106 +1005,94 @@ void Task::addDependency (const std::string& uuid) #ifdef PRODUCT_TASKWARRIOR //////////////////////////////////////////////////////////////////////////////// -void Task::removeDependency (int id) -{ - std::string uuid = Context::getContext ().tdb2.uuid (id); +void Task::removeDependency(int id) { + std::string uuid = Context::getContext().tdb2.uuid(id); // The removeDependency(std::string&) method will check this too, but here we // can give a more natural error message containing the id provided by the user - if (uuid == "" || !has (dep2Attr (uuid))) - throw format ("Could not delete a dependency on task {1} - not found.", id); - removeDependency (uuid); + if (uuid == "" || !has(dep2Attr(uuid))) + throw format("Could not delete a dependency on task {1} - not found.", id); + removeDependency(uuid); } //////////////////////////////////////////////////////////////////////////////// -void Task::removeDependency (const std::string& uuid) -{ - auto depattr = dep2Attr (uuid); - if (has (depattr)) - remove (depattr); +void Task::removeDependency(const std::string& uuid) { + auto depattr = dep2Attr(uuid); + if (has(depattr)) + remove(depattr); else - throw format ("Could not delete a dependency on task {1} - not found.", uuid); + throw format("Could not delete a dependency on task {1} - not found.", uuid); recalc_urgency = true; fixDependsAttribute(); } //////////////////////////////////////////////////////////////////////////////// -bool Task::hasDependency (const std::string& uuid) const -{ - auto depattr = dep2Attr (uuid); - return has (depattr); +bool Task::hasDependency(const std::string& uuid) const { + auto depattr = dep2Attr(uuid); + return has(depattr); } //////////////////////////////////////////////////////////////////////////////// -std::vector Task::getDependencyIDs () const -{ - std::vector ids; - for (auto& attr : all ()) { - if (!isDepAttr (attr)) - continue; - auto dep = attr2Dep (attr); - ids.push_back (Context::getContext ().tdb2.id (dep)); +std::vector Task::getDependencyIDs() const { + std::vector ids; + for (auto& attr : all()) { + if (!isDepAttr(attr)) continue; + auto dep = attr2Dep(attr); + ids.push_back(Context::getContext().tdb2.id(dep)); } return ids; } //////////////////////////////////////////////////////////////////////////////// -std::vector Task::getDependencyUUIDs () const -{ - std::vector uuids; - for (auto& attr : all ()) { - if (!isDepAttr (attr)) - continue; - auto dep = attr2Dep (attr); - uuids.push_back (dep); +std::vector Task::getDependencyUUIDs() const { + std::vector uuids; + for (auto& attr : all()) { + if (!isDepAttr(attr)) continue; + auto dep = attr2Dep(attr); + uuids.push_back(dep); } return uuids; } //////////////////////////////////////////////////////////////////////////////// -std::vector Task::getDependencyTasks () const -{ - auto uuids = getDependencyUUIDs (); +std::vector Task::getDependencyTasks() const { + auto uuids = getDependencyUUIDs(); // NOTE: this may seem inefficient, but note that `TDB2::get` performs a // linear search on each invocation, so scanning *once* is quite a bit more // efficient. - std::vector blocking; + std::vector blocking; if (uuids.size() > 0) - for (auto& it : Context::getContext ().tdb2.pending_tasks ()) - if (it.getStatus () != Task::completed && - it.getStatus () != Task::deleted && - std::find (uuids.begin (), uuids.end (), it.get ("uuid")) != uuids.end ()) - blocking.push_back (it); + for (auto& it : Context::getContext().tdb2.pending_tasks()) + if (it.getStatus() != Task::completed && it.getStatus() != Task::deleted && + std::find(uuids.begin(), uuids.end(), it.get("uuid")) != uuids.end()) + blocking.push_back(it); return blocking; } //////////////////////////////////////////////////////////////////////////////// -std::vector Task::getBlockedTasks () const -{ - auto uuid = get ("uuid"); +std::vector Task::getBlockedTasks() const { + auto uuid = get("uuid"); - std::vector blocked; - for (auto& it : Context::getContext ().tdb2.pending_tasks ()) - if (it.getStatus () != Task::completed && - it.getStatus () != Task::deleted && - it.hasDependency (uuid)) - blocked.push_back (it); + std::vector blocked; + for (auto& it : Context::getContext().tdb2.pending_tasks()) + if (it.getStatus() != Task::completed && it.getStatus() != Task::deleted && + it.hasDependency(uuid)) + blocked.push_back(it); return blocked; } #endif //////////////////////////////////////////////////////////////////////////////// -int Task::getTagCount () const -{ +int Task::getTagCount() const { auto count = 0; for (auto& attr : data) { - if (isTagAttr (attr.first)) { + if (isTagAttr(attr.first)) { count++; } } @@ -1310,229 +1110,203 @@ int Task::getTagCount () const // due:1month - - - - - - - ? // due:1year - - - - - - - - // -bool Task::hasTag (const std::string& tag) const -{ +bool Task::hasTag(const std::string& tag) const { // Synthetic tags - dynamically generated, but do not occupy storage space. // Note: This list must match that in CmdInfo::execute. // Note: This list must match that in ::feedback_reserved_tags. - if (isupper (tag[0])) - { + if (isupper(tag[0])) { // NOTE: This list should be kept synchronized with: // * the list in CmdTags.cpp for the _tags command. // * the list in CmdInfo.cpp for the info command. - if (tag == "BLOCKED") return is_blocked; + if (tag == "BLOCKED") return is_blocked; if (tag == "UNBLOCKED") return !is_blocked; - if (tag == "BLOCKING") return is_blocking; + if (tag == "BLOCKING") return is_blocking; #ifdef PRODUCT_TASKWARRIOR - if (tag == "READY") return is_ready (); - if (tag == "DUE") return is_due (); - if (tag == "DUETODAY") return is_duetoday (); // 2016-03-29: Deprecated in 2.6.0 - if (tag == "TODAY") return is_duetoday (); - if (tag == "YESTERDAY") return is_dueyesterday (); - if (tag == "TOMORROW") return is_duetomorrow (); - if (tag == "OVERDUE") return is_overdue (); - if (tag == "WEEK") return is_dueweek (); - if (tag == "MONTH") return is_duemonth (); - if (tag == "QUARTER") return is_duequarter (); - if (tag == "YEAR") return is_dueyear (); + if (tag == "READY") return is_ready(); + if (tag == "DUE") return is_due(); + if (tag == "DUETODAY") return is_duetoday(); // 2016-03-29: Deprecated in 2.6.0 + if (tag == "TODAY") return is_duetoday(); + if (tag == "YESTERDAY") return is_dueyesterday(); + if (tag == "TOMORROW") return is_duetomorrow(); + if (tag == "OVERDUE") return is_overdue(); + if (tag == "WEEK") return is_dueweek(); + if (tag == "MONTH") return is_duemonth(); + if (tag == "QUARTER") return is_duequarter(); + if (tag == "YEAR") return is_dueyear(); #endif - if (tag == "ACTIVE") return has ("start"); - if (tag == "SCHEDULED") return has ("scheduled"); - if (tag == "CHILD") return has ("parent") || has ("template"); // 2017-01-07: Deprecated in 2.6.0 - if (tag == "INSTANCE") return has ("template") || has ("parent"); - if (tag == "UNTIL") return has ("until"); - if (tag == "ANNOTATED") return hasAnnotations (); - if (tag == "TAGGED") return getTagCount() > 0; - if (tag == "PARENT") return has ("mask") || has ("last"); // 2017-01-07: Deprecated in 2.6.0 - if (tag == "TEMPLATE") return has ("last") || has ("mask"); - if (tag == "WAITING") return is_waiting (); - if (tag == "PENDING") return getStatus () == Task::pending; - if (tag == "COMPLETED") return getStatus () == Task::completed; - if (tag == "DELETED") return getStatus () == Task::deleted; + if (tag == "ACTIVE") return has("start"); + if (tag == "SCHEDULED") return has("scheduled"); + if (tag == "CHILD") return has("parent") || has("template"); // 2017-01-07: Deprecated in 2.6.0 + if (tag == "INSTANCE") return has("template") || has("parent"); + if (tag == "UNTIL") return has("until"); + if (tag == "ANNOTATED") return hasAnnotations(); + if (tag == "TAGGED") return getTagCount() > 0; + if (tag == "PARENT") return has("mask") || has("last"); // 2017-01-07: Deprecated in 2.6.0 + if (tag == "TEMPLATE") return has("last") || has("mask"); + if (tag == "WAITING") return is_waiting(); + if (tag == "PENDING") return getStatus() == Task::pending; + if (tag == "COMPLETED") return getStatus() == Task::completed; + if (tag == "DELETED") return getStatus() == Task::deleted; #ifdef PRODUCT_TASKWARRIOR - if (tag == "UDA") return is_udaPresent (); - if (tag == "ORPHAN") return is_orphanPresent (); - if (tag == "LATEST") return id == Context::getContext ().tdb2.latest_id (); + if (tag == "UDA") return is_udaPresent(); + if (tag == "ORPHAN") return is_orphanPresent(); + if (tag == "LATEST") return id == Context::getContext().tdb2.latest_id(); #endif - if (tag == "PROJECT") return has ("project"); - if (tag == "PRIORITY") return has ("priority"); + if (tag == "PROJECT") return has("project"); + if (tag == "PRIORITY") return has("priority"); } // Concrete tags. - if (has (tag2Attr (tag))) - return true; + if (has(tag2Attr(tag))) return true; return false; } //////////////////////////////////////////////////////////////////////////////// -void Task::addTag (const std::string& tag) -{ - auto attr = tag2Attr (tag); - if (!has (attr)) { - set (attr, "x"); +void Task::addTag(const std::string& tag) { + auto attr = tag2Attr(tag); + if (!has(attr)) { + set(attr, "x"); recalc_urgency = true; fixTagsAttribute(); } } //////////////////////////////////////////////////////////////////////////////// -void Task::setTags (const std::vector & tags) -{ +void Task::setTags(const std::vector& tags) { auto existing = getTags(); // edit in-place, determining which should be // added and which should be removed - std::vector toAdd; - std::vector toRemove; + std::vector toAdd; + std::vector toRemove; for (auto& tag : tags) { - if (std::find (existing.begin (), existing.end (), tag) == existing.end ()) - toAdd.push_back(tag); + if (std::find(existing.begin(), existing.end(), tag) == existing.end()) toAdd.push_back(tag); } - for (auto& tag : getTags ()) { - if (std::find (tags.begin (), tags.end (), tag) == tags.end ()) { - toRemove.push_back (tag); + for (auto& tag : getTags()) { + if (std::find(tags.begin(), tags.end(), tag) == tags.end()) { + toRemove.push_back(tag); } } for (auto& tag : toRemove) { - removeTag (tag); + removeTag(tag); } for (auto& tag : toAdd) { - addTag (tag); + addTag(tag); } // (note: addTag / removeTag took care of recalculating urgency) } //////////////////////////////////////////////////////////////////////////////// -std::vector Task::getTags () const -{ - std::vector tags; +std::vector Task::getTags() const { + std::vector tags; for (auto& attr : data) { - if (!isTagAttr (attr.first)) { + if (!isTagAttr(attr.first)) { continue; } - auto tag = attr2Tag (attr.first); - tags.push_back (tag); + auto tag = attr2Tag(attr.first); + tags.push_back(tag); } return tags; } //////////////////////////////////////////////////////////////////////////////// -void Task::removeTag (const std::string& tag) -{ - auto attr = tag2Attr (tag); - if (has (attr)) { - data.erase (attr); +void Task::removeTag(const std::string& tag) { + auto attr = tag2Attr(tag); + if (has(attr)) { + data.erase(attr); recalc_urgency = true; fixTagsAttribute(); } } //////////////////////////////////////////////////////////////////////////////// -void Task::fixTagsAttribute () -{ +void Task::fixTagsAttribute() { // Fix up the old `tags` attribute to match the `tag_..` attributes (or // remove it if there are no tags) - auto tags = getTags (); - if (tags.size () > 0) { - set ("tags", join (",", tags)); + auto tags = getTags(); + if (tags.size() > 0) { + set("tags", join(",", tags)); } else { - remove ("tags"); + remove("tags"); } } //////////////////////////////////////////////////////////////////////////////// -bool Task::isTagAttr(const std::string& attr) -{ - return attr.compare(0, 4, "tag_") == 0; -} +bool Task::isTagAttr(const std::string& attr) { return attr.compare(0, 4, "tag_") == 0; } //////////////////////////////////////////////////////////////////////////////// -const std::string Task::tag2Attr (const std::string& tag) const -{ +const std::string Task::tag2Attr(const std::string& tag) const { std::stringstream tag_attr; tag_attr << "tag_" << tag; return tag_attr.str(); } //////////////////////////////////////////////////////////////////////////////// -const std::string Task::attr2Tag (const std::string& attr) const -{ - assert (isTagAttr (attr)); +const std::string Task::attr2Tag(const std::string& attr) const { + assert(isTagAttr(attr)); return attr.substr(4); } //////////////////////////////////////////////////////////////////////////////// -void Task::fixDependsAttribute () -{ +void Task::fixDependsAttribute() { // Fix up the old `depends` attribute to match the `dep_..` attributes (or // remove it if there are no deps) - auto deps = getDependencyUUIDs (); - if (deps.size () > 0) { - set ("depends", join (",", deps)); + auto deps = getDependencyUUIDs(); + if (deps.size() > 0) { + set("depends", join(",", deps)); } else { - remove ("depends"); + remove("depends"); } } //////////////////////////////////////////////////////////////////////////////// -bool Task::isDepAttr(const std::string& attr) -{ - return attr.compare(0, 4, "dep_") == 0; -} +bool Task::isDepAttr(const std::string& attr) { return attr.compare(0, 4, "dep_") == 0; } //////////////////////////////////////////////////////////////////////////////// -const std::string Task::dep2Attr (const std::string& tag) const -{ +const std::string Task::dep2Attr(const std::string& tag) const { std::stringstream tag_attr; tag_attr << "dep_" << tag; return tag_attr.str(); } //////////////////////////////////////////////////////////////////////////////// -const std::string Task::attr2Dep (const std::string& attr) const -{ - assert (isDepAttr (attr)); +const std::string Task::attr2Dep(const std::string& attr) const { + assert(isDepAttr(attr)); return attr.substr(4); } //////////////////////////////////////////////////////////////////////////////// -bool Task::isAnnotationAttr(const std::string& attr) -{ +bool Task::isAnnotationAttr(const std::string& attr) { return attr.compare(0, 11, "annotation_") == 0; } #ifdef PRODUCT_TASKWARRIOR //////////////////////////////////////////////////////////////////////////////// // A UDA Orphan is an attribute that is not represented in context.columns. -std::vector Task::getUDAOrphans () const -{ - std::vector orphans; +std::vector Task::getUDAOrphans() const { + std::vector orphans; for (auto& it : data) - if (Context::getContext ().columns.find (it.first) == Context::getContext ().columns.end ()) - if (not (isAnnotationAttr (it.first) || isTagAttr (it.first) || isDepAttr (it.first))) - orphans.push_back (it.first); + if (Context::getContext().columns.find(it.first) == Context::getContext().columns.end()) + if (not(isAnnotationAttr(it.first) || isTagAttr(it.first) || isDepAttr(it.first))) + orphans.push_back(it.first); return orphans; } //////////////////////////////////////////////////////////////////////////////// -void Task::substitute ( - const std::string& from, - const std::string& to, - const std::string& flags) -{ - bool global = (flags.find ('g') != std::string::npos ? true : false); +void Task::substitute(const std::string& from, const std::string& to, const std::string& flags) { + bool global = (flags.find('g') != std::string::npos ? true : false); // Get the data to modify. - std::string description = get ("description"); - auto annotations = getAnnotations (); + std::string description = get("description"); + auto annotations = getAnnotations(); // Count the changes, so we know whether to proceed to annotations, after // modifying description. @@ -1540,103 +1314,94 @@ void Task::substitute ( bool done = false; // Regex support is optional. - if (Task::regex) - { + if (Task::regex) { // Create the regex. - RX rx (from, Task::searchCaseSensitive); - std::vector start; - std::vector end; + RX rx(from, Task::searchCaseSensitive); + std::vector start; + std::vector end; // Perform all subs on description. - if (rx.match (start, end, description)) - { + if (rx.match(start, end, description)) { int skew = 0; - for (unsigned int i = 0; i < start.size () && !done; ++i) - { - description.replace (start[i] + skew, end[i] - start[i], to); - skew += to.length () - (end[i] - start[i]); + for (unsigned int i = 0; i < start.size() && !done; ++i) { + description.replace(start[i] + skew, end[i] - start[i], to); + skew += to.length() - (end[i] - start[i]); ++changes; - if (!global) - done = true; + if (!global) done = true; } } - if (!done) - { + if (!done) { // Perform all subs on annotations. - for (auto& it : annotations) - { - start.clear (); - end.clear (); - if (rx.match (start, end, it.second)) - { + for (auto& it : annotations) { + start.clear(); + end.clear(); + if (rx.match(start, end, it.second)) { int skew = 0; - for (unsigned int i = 0; i < start.size () && !done; ++i) - { - it.second.replace (start[i + skew], end[i] - start[i], to); - skew += to.length () - (end[i] - start[i]); + for (unsigned int i = 0; i < start.size() && !done; ++i) { + it.second.replace(start[i + skew], end[i] - start[i], to); + skew += to.length() - (end[i] - start[i]); ++changes; - if (!global) - done = true; + if (!global) done = true; } } } } - } - else - { + } else { // Perform all subs on description. int counter = 0; std::string::size_type pos = 0; int skew = 0; - while ((pos = ::find (description, from, pos, Task::searchCaseSensitive)) != std::string::npos && !done) - { - description.replace (pos + skew, from.length (), to); - skew += to.length () - from.length (); + while ((pos = ::find(description, from, pos, Task::searchCaseSensitive)) != std::string::npos && + !done) { + description.replace(pos + skew, from.length(), to); + skew += to.length() - from.length(); - pos += to.length (); + pos += to.length(); ++changes; - if (!global) - done = true; + if (!global) done = true; if (++counter > APPROACHING_INFINITY) - throw format ("Terminated substitution because more than {1} changes were made - infinite loop protection.", APPROACHING_INFINITY); + throw format( + "Terminated substitution because more than {1} changes were made - infinite loop " + "protection.", + APPROACHING_INFINITY); } - if (!done) - { + if (!done) { // Perform all subs on annotations. counter = 0; - for (auto& anno : annotations) - { + for (auto& anno : annotations) { pos = 0; skew = 0; - while ((pos = ::find (anno.second, from, pos, Task::searchCaseSensitive)) != std::string::npos && !done) - { - anno.second.replace (pos + skew, from.length (), to); - skew += to.length () - from.length (); + while ((pos = ::find(anno.second, from, pos, Task::searchCaseSensitive)) != + std::string::npos && + !done) { + anno.second.replace(pos + skew, from.length(), to); + skew += to.length() - from.length(); - pos += to.length (); + pos += to.length(); ++changes; - if (!global) - done = true; + if (!global) done = true; if (++counter > APPROACHING_INFINITY) - throw format ("Terminated substitution because more than {1} changes were made - infinite loop protection.", APPROACHING_INFINITY); + throw format( + "Terminated substitution because more than {1} changes were made - infinite loop " + "protection.", + APPROACHING_INFINITY); } } } } - if (changes) - { - set ("description", description); - setAnnotations (annotations); + if (changes) { + set("description", description); + setAnnotations(annotations); recalc_urgency = true; } } @@ -1651,149 +1416,116 @@ void Task::substitute ( // // Critically, note that despite the name this is not a read-only function. // -void Task::validate (bool applyDefault /* = true */) -{ +void Task::validate(bool applyDefault /* = true */) { Task::status status = Task::pending; - if (get ("status") != "") - status = getStatus (); + if (get("status") != "") status = getStatus(); // 1) Provide missing attributes where possible // Provide a UUID if necessary. Validate if present. - std::string uid = get ("uuid"); - if (has ("uuid") && uid != "") - { - Lexer lex (uid); + std::string uid = get("uuid"); + if (has("uuid") && uid != "") { + Lexer lex(uid); std::string token; Lexer::Type type; - if (! lex.isUUID (token, type, true)) - throw format ("Not a valid UUID '{1}'.", uid); - } - else - set ("uuid", uuid ()); + if (!lex.isUUID(token, type, true)) throw format("Not a valid UUID '{1}'.", uid); + } else + set("uuid", uuid()); // TODO Obsolete remove for 3.0.0 // Recurring tasks get a special status. - if (status == Task::pending && - has ("due") && - has ("recur") && - (! has ("parent") || get ("parent") == "") && - (! has ("template") || get ("template") == "")) - { + if (status == Task::pending && has("due") && has("recur") && + (!has("parent") || get("parent") == "") && (!has("template") || get("template") == "")) { status = Task::recurring; } -/* - // TODO Add for 3.0.0 - if (status == Task::pending && - has ("due") && - has ("recur") && - (! has ("template") || get ("template") == "")) - { - status = Task::recurring; - } -*/ + /* + // TODO Add for 3.0.0 + if (status == Task::pending && + has ("due") && + has ("recur") && + (! has ("template") || get ("template") == "")) + { + status = Task::recurring; + } + */ // Tasks with a wait: date get a special status. - else if (status == Task::pending && - has ("wait") && - get ("wait") != "") + else if (status == Task::pending && has("wait") && get("wait") != "") status = Task::waiting; // By default, tasks are pending. - else if (! has ("status") || get ("status") == "") + else if (!has("status") || get("status") == "") status = Task::pending; // Default to 'periodic' type recurrence. - if (status == Task::recurring && - (! has ("rtype") || get ("rtype") == "")) - { - set ("rtype", "periodic"); + if (status == Task::recurring && (!has("rtype") || get("rtype") == "")) { + set("rtype", "periodic"); } // Store the derived status. - setStatus (status); + setStatus(status); #ifdef PRODUCT_TASKWARRIOR // Provide an entry date unless user already specified one. - if (! has ("entry") || get ("entry") == "") - setAsNow ("entry"); + if (!has("entry") || get("entry") == "") setAsNow("entry"); // Completed tasks need an end date, so inherit the entry date. - if ((status == Task::completed || status == Task::deleted) && - (! has ("end") || get ("end") == "")) - setAsNow ("end"); + if ((status == Task::completed || status == Task::deleted) && (!has("end") || get("end") == "")) + setAsNow("end"); // Pending tasks cannot have an end date, remove if present - if ((status == Task::pending) && (get ("end") != "")) - remove ("end"); + if ((status == Task::pending) && (get("end") != "")) remove("end"); // Provide a modified date unless user already specified one. - if (! has ("modified") || get ("modified") == "") - setAsNow ("modified"); + if (!has("modified") || get("modified") == "") setAsNow("modified"); - if (applyDefault && (! has ("parent") || get ("parent") == "")) - { + if (applyDefault && (!has("parent") || get("parent") == "")) { // Override with default.project, if not specified. - if (Task::defaultProject != "" && - ! has ("project")) - { - if (Context::getContext ().columns["project"]->validate (Task::defaultProject)) - set ("project", Task::defaultProject); + if (Task::defaultProject != "" && !has("project")) { + if (Context::getContext().columns["project"]->validate(Task::defaultProject)) + set("project", Task::defaultProject); } // Override with default.due, if not specified. - if (Task::defaultDue != "" && - ! has ("due")) - { - if (Context::getContext ().columns["due"]->validate (Task::defaultDue)) - { - Duration dur (Task::defaultDue); - if (dur.toTime_t () != 0) - set ("due", (Datetime () + dur.toTime_t ()).toEpoch ()); + if (Task::defaultDue != "" && !has("due")) { + if (Context::getContext().columns["due"]->validate(Task::defaultDue)) { + Duration dur(Task::defaultDue); + if (dur.toTime_t() != 0) + set("due", (Datetime() + dur.toTime_t()).toEpoch()); else - set ("due", Datetime (Task::defaultDue).toEpoch ()); + set("due", Datetime(Task::defaultDue).toEpoch()); } } // Override with default.scheduled, if not specified. - if (Task::defaultScheduled != "" && - ! has ("scheduled")) - { - if (Context::getContext ().columns["scheduled"]->validate (Task::defaultScheduled)) - { - Duration dur (Task::defaultScheduled); - if (dur.toTime_t () != 0) - set ("scheduled", (Datetime () + dur.toTime_t ()).toEpoch ()); + if (Task::defaultScheduled != "" && !has("scheduled")) { + if (Context::getContext().columns["scheduled"]->validate(Task::defaultScheduled)) { + Duration dur(Task::defaultScheduled); + if (dur.toTime_t() != 0) + set("scheduled", (Datetime() + dur.toTime_t()).toEpoch()); else - set ("scheduled", Datetime (Task::defaultScheduled).toEpoch ()); + set("scheduled", Datetime(Task::defaultScheduled).toEpoch()); } } // If a UDA has a default value in the configuration, // override with uda.(uda).default, if not specified. // Gather a list of all UDAs with a .default value - std::vector udas; - for (auto& var : Context::getContext ().config) - { - if (! var.first.compare (0, 4, "uda.", 4) && - var.first.find (".default") != std::string::npos) - { - auto period = var.first.find ('.', 4); - if (period != std::string::npos) - udas.push_back (var.first.substr (4, period - 4)); + std::vector udas; + for (auto& var : Context::getContext().config) { + if (!var.first.compare(0, 4, "uda.", 4) && var.first.find(".default") != std::string::npos) { + auto period = var.first.find('.', 4); + if (period != std::string::npos) udas.push_back(var.first.substr(4, period - 4)); } } - if (udas.size ()) - { + if (udas.size()) { // For each of those, setup the default value on the task now, // of course only if we don't have one on the command line already - for (auto& uda : udas) - { - std::string defVal= Context::getContext ().config.get ("uda." + uda + ".default"); + for (auto& uda : udas) { + std::string defVal = Context::getContext().config.get("uda." + uda + ".default"); // If the default is empty, or we already have a value, skip it - if (defVal != "" && get (uda) == "") - set (uda, defVal); + if (defVal != "" && get(uda) == "") set(uda, defVal); } } } @@ -1802,54 +1534,50 @@ void Task::validate (bool applyDefault /* = true */) // 2) To provide suitable warnings about odd states // Date relationships. - validate_before ("wait", "due"); - validate_before ("entry", "start"); - validate_before ("entry", "end"); - validate_before ("wait", "scheduled"); - validate_before ("scheduled", "start"); - validate_before ("scheduled", "due"); - validate_before ("scheduled", "end"); + validate_before("wait", "due"); + validate_before("entry", "start"); + validate_before("entry", "end"); + validate_before("wait", "scheduled"); + validate_before("scheduled", "start"); + validate_before("scheduled", "due"); + validate_before("scheduled", "end"); // 3) To generate errors when the inconsistencies are not fixable // There is no fixing a missing description. - if (! has ("description")) - throw std::string ("A task must have a description."); - else if (get ("description") == "") - throw std::string ("Cannot add a task that is blank."); + if (!has("description")) + throw std::string("A task must have a description."); + else if (get("description") == "") + throw std::string("Cannot add a task that is blank."); // Cannot have a recur frequency with no due date - when would it recur? - if (has ("recur") && (! has ("due") || get ("due") == "")) - throw std::string ("A recurring task must also have a 'due' date."); + if (has("recur") && (!has("due") || get("due") == "")) + throw std::string("A recurring task must also have a 'due' date."); // Recur durations must be valid. - if (has ("recur")) - { - std::string value = get ("recur"); - if (value != "") - { + if (has("recur")) { + std::string value = get("recur"); + if (value != "") { Duration p; std::string::size_type i = 0; - if (! p.parse (value, i)) + if (!p.parse(value, i)) // TODO Ideal location to map unsupported old recurrence periods to supported values. - throw format ("The recurrence value '{1}' is not valid.", value); + throw format("The recurrence value '{1}' is not valid.", value); } } } //////////////////////////////////////////////////////////////////////////////// -void Task::validate_before (const std::string& left, const std::string& right) -{ +void Task::validate_before(const std::string& left, const std::string& right) { #ifdef PRODUCT_TASKWARRIOR - if (has (left) && - has (right)) - { - Datetime date_left (get_date (left)); - Datetime date_right (get_date (right)); + if (has(left) && has(right)) { + Datetime date_left(get_date(left)); + Datetime date_right(get_date(right)); // if date is zero, then it is being removed (e.g. "due: wait:1day") - if (date_left > date_right && date_right.toEpoch () != 0) - Context::getContext ().footnote (format ("Warning: You have specified that the '{1}' date is after the '{2}' date.", left, right)); + if (date_left > date_right && date_right.toEpoch() != 0) + Context::getContext().footnote(format( + "Warning: You have specified that the '{1}' date is after the '{2}' date.", left, right)); } #endif } @@ -1858,53 +1586,58 @@ void Task::validate_before (const std::string& left, const std::string& right) // Encode values prior to serialization. // [ -> &open; // ] -> &close; -const std::string Task::encode (const std::string& value) const -{ - auto modified = str_replace (value, "[", "&open;"); - return str_replace (modified, "]", "&close;"); +const std::string Task::encode(const std::string& value) const { + auto modified = str_replace(value, "[", "&open;"); + return str_replace(modified, "]", "&close;"); } //////////////////////////////////////////////////////////////////////////////// // Decode values after parse. // [ <- &open; // ] <- &close; -const std::string Task::decode (const std::string& value) const -{ - if (value.find ('&') == std::string::npos) - return value; +const std::string Task::decode(const std::string& value) const { + if (value.find('&') == std::string::npos) return value; - auto modified = str_replace (value, "&open;", "["); - return str_replace (modified, "&close;", "]"); + auto modified = str_replace(value, "&open;", "["); + return str_replace(modified, "&close;", "]"); } //////////////////////////////////////////////////////////////////////////////// -tc::Status Task::status2tc (const Task::status status) -{ +tc::Status Task::status2tc(const Task::status status) { switch (status) { - case Task::pending: return tc::Status::Pending; - case Task::completed: return tc::Status::Completed; - case Task::deleted: return tc::Status::Deleted; - case Task::waiting: return tc::Status::Pending; // waiting is no longer a status - case Task::recurring: return tc::Status::Recurring; - default: return tc::Status::Unknown; + case Task::pending: + return tc::Status::Pending; + case Task::completed: + return tc::Status::Completed; + case Task::deleted: + return tc::Status::Deleted; + case Task::waiting: + return tc::Status::Pending; // waiting is no longer a status + case Task::recurring: + return tc::Status::Recurring; + default: + return tc::Status::Unknown; } } //////////////////////////////////////////////////////////////////////////////// -Task::status Task::tc2status (const tc::Status status) -{ +Task::status Task::tc2status(const tc::Status status) { switch (status) { - case tc::Status::Pending: return Task::pending; - case tc::Status::Completed: return Task::completed; - case tc::Status::Deleted: return Task::deleted; - case tc::Status::Recurring: return Task::recurring; - default: return Task::pending; + case tc::Status::Pending: + return Task::pending; + case tc::Status::Completed: + return Task::completed; + case tc::Status::Deleted: + return Task::deleted; + case tc::Status::Recurring: + return Task::recurring; + default: + return Task::pending; } } //////////////////////////////////////////////////////////////////////////////// -int Task::determineVersion (const std::string& line) -{ +int Task::determineVersion(const std::string& line) { // Version 2 looks like: // // uuid status [tags] [attributes] description\n @@ -1915,23 +1648,17 @@ int Task::determineVersion (const std::string& line) // // Scan for the hyphens in the uuid, the following space, and a valid status // character. - if (line[8] == '-' && - line[13] == '-' && - line[18] == '-' && - line[23] == '-' && - line[36] == ' ' && - (line[37] == '-' || line[37] == '+' || line[37] == 'X' || line[37] == 'r')) - { + if (line[8] == '-' && line[13] == '-' && line[18] == '-' && line[23] == '-' && line[36] == ' ' && + (line[37] == '-' || line[37] == '+' || line[37] == 'X' || line[37] == 'r')) { // Version 3 looks like: // // uuid status [tags] [attributes] [annotations] description\n // // Scan for the number of [] pairs. - auto tagAtts = line.find ("] [", 0); - auto attsAnno = line.find ("] [", tagAtts + 1); - auto annoDesc = line.find ("] ", attsAnno + 1); - if (tagAtts != std::string::npos && - attsAnno != std::string::npos && + auto tagAtts = line.find("] [", 0); + auto attsAnno = line.find("] [", tagAtts + 1); + auto annoDesc = line.find("] ", attsAnno + 1); + if (tagAtts != std::string::npos && attsAnno != std::string::npos && annoDesc != std::string::npos) return 3; else @@ -1943,9 +1670,8 @@ int Task::determineVersion (const std::string& line) // [name:"value" ...] // // Scan for [, ] and :". - else if (line[0] == '[' && - line[line.length () - 1] == ']' && - line.find ("uuid:\"") != std::string::npos) + else if (line[0] == '[' && line[line.length() - 1] == ']' && + line.find("uuid:\"") != std::string::npos) return 4; // Version 1 looks like: @@ -1954,10 +1680,8 @@ int Task::determineVersion (const std::string& line) // X [tags] [attributes] description\n // // Scan for the first character being either the bracket or X. - else if (line.find ("X [") == 0 || - (line[0] == '[' && - line.substr (line.length () - 1, 1) != "]" && - line.length () > 3)) + else if (line.find("X [") == 0 || + (line[0] == '[' && line.substr(line.length() - 1, 1) != "]" && line.length() > 3)) return 1; // Version 5? @@ -1990,98 +1714,97 @@ int Task::determineVersion (const std::string& line) // // See rfc31-urgency.txt for full details. // -float Task::urgency_c () const -{ +float Task::urgency_c() const { float value = 0.0; #ifdef PRODUCT_TASKWARRIOR - value += fabsf (Task::urgencyProjectCoefficient) > epsilon ? (urgency_project () * Task::urgencyProjectCoefficient) : 0.0; - value += fabsf (Task::urgencyActiveCoefficient) > epsilon ? (urgency_active () * Task::urgencyActiveCoefficient) : 0.0; - value += fabsf (Task::urgencyScheduledCoefficient) > epsilon ? (urgency_scheduled () * Task::urgencyScheduledCoefficient) : 0.0; - value += fabsf (Task::urgencyWaitingCoefficient) > epsilon ? (urgency_waiting () * Task::urgencyWaitingCoefficient) : 0.0; - value += fabsf (Task::urgencyBlockedCoefficient) > epsilon ? (urgency_blocked () * Task::urgencyBlockedCoefficient) : 0.0; - value += fabsf (Task::urgencyAnnotationsCoefficient) > epsilon ? (urgency_annotations () * Task::urgencyAnnotationsCoefficient) : 0.0; - value += fabsf (Task::urgencyTagsCoefficient) > epsilon ? (urgency_tags () * Task::urgencyTagsCoefficient) : 0.0; - value += fabsf (Task::urgencyDueCoefficient) > epsilon ? (urgency_due () * Task::urgencyDueCoefficient) : 0.0; - value += fabsf (Task::urgencyBlockingCoefficient) > epsilon ? (urgency_blocking () * Task::urgencyBlockingCoefficient) : 0.0; - value += fabsf (Task::urgencyAgeCoefficient) > epsilon ? (urgency_age () * Task::urgencyAgeCoefficient) : 0.0; + value += fabsf(Task::urgencyProjectCoefficient) > epsilon + ? (urgency_project() * Task::urgencyProjectCoefficient) + : 0.0; + value += fabsf(Task::urgencyActiveCoefficient) > epsilon + ? (urgency_active() * Task::urgencyActiveCoefficient) + : 0.0; + value += fabsf(Task::urgencyScheduledCoefficient) > epsilon + ? (urgency_scheduled() * Task::urgencyScheduledCoefficient) + : 0.0; + value += fabsf(Task::urgencyWaitingCoefficient) > epsilon + ? (urgency_waiting() * Task::urgencyWaitingCoefficient) + : 0.0; + value += fabsf(Task::urgencyBlockedCoefficient) > epsilon + ? (urgency_blocked() * Task::urgencyBlockedCoefficient) + : 0.0; + value += fabsf(Task::urgencyAnnotationsCoefficient) > epsilon + ? (urgency_annotations() * Task::urgencyAnnotationsCoefficient) + : 0.0; + value += fabsf(Task::urgencyTagsCoefficient) > epsilon + ? (urgency_tags() * Task::urgencyTagsCoefficient) + : 0.0; + value += fabsf(Task::urgencyDueCoefficient) > epsilon + ? (urgency_due() * Task::urgencyDueCoefficient) + : 0.0; + value += fabsf(Task::urgencyBlockingCoefficient) > epsilon + ? (urgency_blocking() * Task::urgencyBlockingCoefficient) + : 0.0; + value += fabsf(Task::urgencyAgeCoefficient) > epsilon + ? (urgency_age() * Task::urgencyAgeCoefficient) + : 0.0; const std::string taskProjectName = get("project"); // Tag- and project-specific coefficients. - for (auto& var : Task::coefficients) - { - if (fabs (var.second) > epsilon) - { - if (! var.first.compare (0, 13, "urgency.user.", 13)) - { + for (auto& var : Task::coefficients) { + if (fabs(var.second) > epsilon) { + if (!var.first.compare(0, 13, "urgency.user.", 13)) { // urgency.user.project..coefficient auto end = std::string::npos; - if (var.first.substr (13, 8) == "project." && - (end = var.first.find (".coefficient")) != std::string::npos) - { - std::string project = var.first.substr (21, end - 21); + if (var.first.substr(13, 8) == "project." && + (end = var.first.find(".coefficient")) != std::string::npos) { + std::string project = var.first.substr(21, end - 21); - if (taskProjectName == project || - taskProjectName.find(project + '.') == 0) - { + if (taskProjectName == project || taskProjectName.find(project + '.') == 0) { value += var.second; } } // urgency.user.tag..coefficient - if (var.first.substr (13, 4) == "tag." && - (end = var.first.find (".coefficient")) != std::string::npos) - { - std::string tag = var.first.substr (17, end - 17); + if (var.first.substr(13, 4) == "tag." && + (end = var.first.find(".coefficient")) != std::string::npos) { + std::string tag = var.first.substr(17, end - 17); - if (hasTag (tag)) - value += var.second; + if (hasTag(tag)) value += var.second; } // urgency.user.keyword..coefficient - if (var.first.substr (13, 8) == "keyword." && - (end = var.first.find (".coefficient")) != std::string::npos) - { - std::string keyword = var.first.substr (21, end - 21); + if (var.first.substr(13, 8) == "keyword." && + (end = var.first.find(".coefficient")) != std::string::npos) { + std::string keyword = var.first.substr(21, end - 21); - if (get ("description").find (keyword) != std::string::npos) - value += var.second; + if (get("description").find(keyword) != std::string::npos) value += var.second; } - } - else if (var.first.substr (0, 12) == "urgency.uda.") - { + } else if (var.first.substr(0, 12) == "urgency.uda.") { // urgency.uda..coefficient // urgency.uda...coefficient - auto end = var.first.find (".coefficient"); - if (end != std::string::npos) - { - const std::string uda = var.first.substr (12, end - 12); - auto dot = uda.find ('.'); - if (dot == std::string::npos) - { + auto end = var.first.find(".coefficient"); + if (end != std::string::npos) { + const std::string uda = var.first.substr(12, end - 12); + auto dot = uda.find('.'); + if (dot == std::string::npos) { // urgency.uda..coefficient - if (has (uda)) - value += var.second; - } - else - { + if (has(uda)) value += var.second; + } else { // urgency.uda...coefficient - if (get (uda.substr(0, dot)) == uda.substr(dot+1)) - value += var.second; + if (get(uda.substr(0, dot)) == uda.substr(dot + 1)) value += var.second; } } } } } - if (is_blocking && Context::getContext ().config.getBoolean ("urgency.inherit")) - { + if (is_blocking && Context::getContext().config.getBoolean("urgency.inherit")) { float prev = value; - value = std::max (value, urgency_inherit ()); + value = std::max(value, urgency_inherit()); // This is a hackish way of making sure parent tasks are sorted above // child tasks. For reports that hide blocked tasks, this is not needed. - if (prev <= value) - value += 0.01; + if (prev <= value) value += 0.01; } #endif @@ -2089,11 +1812,9 @@ float Task::urgency_c () const } //////////////////////////////////////////////////////////////////////////////// -float Task::urgency () -{ - if (recalc_urgency) - { - urgency_value = urgency_c (); +float Task::urgency() { + if (recalc_urgency) { + urgency_value = urgency_c(); // Return the sum of all terms. recalc_urgency = false; @@ -2103,16 +1824,14 @@ float Task::urgency () } //////////////////////////////////////////////////////////////////////////////// -float Task::urgency_inherit () const -{ +float Task::urgency_inherit() const { float v = -FLT_MAX; #ifdef PRODUCT_TASKWARRIOR // Calling getBlockedTasks is rather expensive. // It is called recursively for each dependency in the chain here. - for (auto& task : getBlockedTasks ()) - { + for (auto& task : getBlockedTasks()) { // Find highest urgency in all blocked tasks. - v = std::max (v, task.urgency ()); + v = std::max(v, task.urgency()); } #endif @@ -2120,71 +1839,64 @@ float Task::urgency_inherit () const } //////////////////////////////////////////////////////////////////////////////// -float Task::urgency_project () const -{ - if (has ("project")) - return 1.0; +float Task::urgency_project() const { + if (has("project")) return 1.0; return 0.0; } //////////////////////////////////////////////////////////////////////////////// -float Task::urgency_active () const -{ - if (has ("start")) - return 1.0; +float Task::urgency_active() const { + if (has("start")) return 1.0; return 0.0; } //////////////////////////////////////////////////////////////////////////////// -float Task::urgency_scheduled () const -{ - if (has ("scheduled") && - get_date ("scheduled") < time (nullptr)) - return 1.0; +float Task::urgency_scheduled() const { + if (has("scheduled") && get_date("scheduled") < time(nullptr)) return 1.0; return 0.0; } //////////////////////////////////////////////////////////////////////////////// -float Task::urgency_waiting () const -{ - if (is_waiting ()) - return 1.0; +float Task::urgency_waiting() const { + if (is_waiting()) return 1.0; return 0.0; } //////////////////////////////////////////////////////////////////////////////// // A task is blocked only if the task it depends upon is pending/waiting. -float Task::urgency_blocked () const -{ - if (is_blocked) +float Task::urgency_blocked() const { + if (is_blocked) return 1.0; + + return 0.0; +} + +//////////////////////////////////////////////////////////////////////////////// +float Task::urgency_annotations() const { + if (annotation_count >= 3) return 1.0; + else if (annotation_count == 2) + return 0.9; + else if (annotation_count == 1) + return 0.8; return 0.0; } //////////////////////////////////////////////////////////////////////////////// -float Task::urgency_annotations () const -{ - if (annotation_count >= 3) return 1.0; - else if (annotation_count == 2) return 0.9; - else if (annotation_count == 1) return 0.8; - - return 0.0; -} - -//////////////////////////////////////////////////////////////////////////////// -float Task::urgency_tags () const -{ - switch (getTagCount ()) - { - case 0: return 0.0; - case 1: return 0.8; - case 2: return 0.9; - default: return 1.0; +float Task::urgency_tags() const { + switch (getTagCount()) { + case 0: + return 0.0; + case 1: + return 0.8; + case 2: + return 0.9; + default: + return 1.0; } } @@ -2199,44 +1911,40 @@ float Task::urgency_tags () const // capped capped // // -float Task::urgency_due () const -{ - if (has ("due")) - { +float Task::urgency_due() const { + if (has("due")) { Datetime now; - Datetime due (get_date ("due")); + Datetime due(get_date("due")); // Map a range of 21 days to the value 0.2 - 1.0 float days_overdue = (now - due) / 86400.0; - if (days_overdue >= 7.0) return 1.0; // < 1 wk ago - else if (days_overdue >= -14.0) return ((days_overdue + 14.0) * 0.8 / 21.0) + 0.2; - else return 0.2; // > 2 wks + if (days_overdue >= 7.0) + return 1.0; // < 1 wk ago + else if (days_overdue >= -14.0) + return ((days_overdue + 14.0) * 0.8 / 21.0) + 0.2; + else + return 0.2; // > 2 wks } return 0.0; } //////////////////////////////////////////////////////////////////////////////// -float Task::urgency_age () const -{ - if (!has ("entry")) - return 1.0; +float Task::urgency_age() const { + if (!has("entry")) return 1.0; Datetime now; - Datetime entry (get_date ("entry")); + Datetime entry(get_date("entry")); int age = (now - entry) / 86400; // in days - if (Task::urgencyAgeMax == 0 || age > Task::urgencyAgeMax) - return 1.0; + if (Task::urgencyAgeMax == 0 || age > Task::urgencyAgeMax) return 1.0; return (1.0 * age / Task::urgencyAgeMax); } //////////////////////////////////////////////////////////////////////////////// -float Task::urgency_blocking () const -{ - if (is_blocking) - return 1.0; +float Task::urgency_blocking() const { + if (is_blocking) return 1.0; return 0.0; } @@ -2248,169 +1956,138 @@ float Task::urgency_blocking () const // well as reducing the object depdendencies of Task. // // It came from the Command base object, but doesn't really belong there either. -void Task::modify (modType type, bool text_required /* = false */) -{ +void Task::modify(modType type, bool text_required /* = false */) { std::string label = " MODIFICATION "; // while reading the parse tree, consider DOM references in the context of // this task - auto currentTask = Context::getContext ().withCurrentTask(this); + auto currentTask = Context::getContext().withCurrentTask(this); // Need this for later comparison. - auto originalStatus = getStatus (); + auto originalStatus = getStatus(); std::string text = ""; bool mods = false; - for (auto& a : Context::getContext ().cli2._args) - { - if (a.hasTag ("MODIFICATION")) - { - if (a._lextype == Lexer::Type::pair) - { + for (auto& a : Context::getContext().cli2._args) { + if (a.hasTag("MODIFICATION")) { + if (a._lextype == Lexer::Type::pair) { // 'canonical' is the canonical name. Needs to be said. // 'value' requires eval. - std::string name = a.attribute ("canonical"); - std::string value = a.attribute ("value"); - if (value == "" || - value == "''" || - value == "\"\"") - { + std::string name = a.attribute("canonical"); + std::string value = a.attribute("value"); + if (value == "" || value == "''" || value == "\"\"") { // Special case: Handle bulk removal of 'tags' and 'depends" virtual // attributes - if (name == "depends") - { - for (auto dep: getDependencyUUIDs ()) - removeDependency(dep); - } - else if (name == "tags") - { - for (auto tag: getTags ()) - removeTag(tag); + if (name == "depends") { + for (auto dep : getDependencyUUIDs()) removeDependency(dep); + } else if (name == "tags") { + for (auto tag : getTags()) removeTag(tag); } // ::composeF4 will skip if the value is blank, but the presence of // the attribute will prevent ::validate from applying defaults. - if ((has (name) && get (name) != "") || - (name == "due" && Context::getContext ().config.has ("default.due")) || - (name == "scheduled" && Context::getContext ().config.has ("default.scheduled")) || - (name == "project" && Context::getContext ().config.has ("default.project"))) - { + if ((has(name) && get(name) != "") || + (name == "due" && Context::getContext().config.has("default.due")) || + (name == "scheduled" && Context::getContext().config.has("default.scheduled")) || + (name == "project" && Context::getContext().config.has("default.project"))) { mods = true; - set (name, ""); + set(name, ""); } - Context::getContext ().debug (label + name + " <-- ''"); - } - else - { - Lexer::dequote (value); + Context::getContext().debug(label + name + " <-- ''"); + } else { + Lexer::dequote(value); // Get the column info. Some columns are not modifiable. - Column* column = Context::getContext ().columns[name]; - if (! column || - ! column->modifiable ()) - throw format ("The '{1}' attribute does not allow a value of '{2}'.", name, value); + Column* column = Context::getContext().columns[name]; + if (!column || !column->modifiable()) + throw format("The '{1}' attribute does not allow a value of '{2}'.", name, value); // Delegate modification to the column object or their base classes. - if (name == "depends" || - name == "tags" || - name == "recur" || - column->type () == "date" || - column->type () == "duration" || - column->type () == "numeric" || - column->type () == "string") - { - column->modify (*this, value); + if (name == "depends" || name == "tags" || name == "recur" || column->type() == "date" || + column->type() == "duration" || column->type() == "numeric" || + column->type() == "string") { + column->modify(*this, value); mods = true; } else - throw format ("Unrecognized column type '{1}' for column '{2}'", column->type (), name); + throw format("Unrecognized column type '{1}' for column '{2}'", column->type(), name); } } // Perform description/annotation substitution. - else if (a._lextype == Lexer::Type::substitution) - { - Context::getContext ().debug (label + "substitute " + a.attribute ("raw")); - substitute (a.attribute ("from"), - a.attribute ("to"), - a.attribute ("flags")); + else if (a._lextype == Lexer::Type::substitution) { + Context::getContext().debug(label + "substitute " + a.attribute("raw")); + substitute(a.attribute("from"), a.attribute("to"), a.attribute("flags")); mods = true; } // Tags need special handling because they are essentially a vector stored // in a single string, therefore Task::{add,remove}Tag must be called as // appropriate. - else if (a._lextype == Lexer::Type::tag) - { - std::string tag = a.attribute ("name"); - feedback_reserved_tags (tag); + else if (a._lextype == Lexer::Type::tag) { + std::string tag = a.attribute("name"); + feedback_reserved_tags(tag); - if (a.attribute ("sign") == "+") - { - Context::getContext ().debug (label + "tags <-- add '" + tag + '\''); - addTag (tag); - feedback_special_tags (*this, tag); - } - else - { - Context::getContext ().debug (label + "tags <-- remove '" + tag + '\''); - removeTag (tag); + if (a.attribute("sign") == "+") { + Context::getContext().debug(label + "tags <-- add '" + tag + '\''); + addTag(tag); + feedback_special_tags(*this, tag); + } else { + Context::getContext().debug(label + "tags <-- remove '" + tag + '\''); + removeTag(tag); } mods = true; } // Unknown args are accumulated as though they were WORDs. - else - { - if (text != "") - text += ' '; - text += a.attribute ("raw"); + else { + if (text != "") text += ' '; + text += a.attribute("raw"); } } } // Task::modType determines what happens to the WORD arguments, if there are // any. - if (text != "") - { - Lexer::dequote (text); + if (text != "") { + Lexer::dequote(text); - switch (type) - { - case modReplace: - Context::getContext ().debug (label + "description <-- '" + text + '\''); - set ("description", text); - break; + switch (type) { + case modReplace: + Context::getContext().debug(label + "description <-- '" + text + '\''); + set("description", text); + break; - case modPrepend: - Context::getContext ().debug (label + "description <-- '" + text + "' + description"); - set ("description", text + ' ' + get ("description")); - break; + case modPrepend: + Context::getContext().debug(label + "description <-- '" + text + "' + description"); + set("description", text + ' ' + get("description")); + break; - case modAppend: - Context::getContext ().debug (label + "description <-- description + '" + text + '\''); - set ("description", get ("description") + ' ' + text); - break; + case modAppend: + Context::getContext().debug(label + "description <-- description + '" + text + '\''); + set("description", get("description") + ' ' + text); + break; - case modAnnotate: - Context::getContext ().debug (label + "new annotation <-- '" + text + '\''); - addAnnotation (text); - break; + case modAnnotate: + Context::getContext().debug(label + "new annotation <-- '" + text + '\''); + addAnnotation(text); + break; } - } - else if (! mods && text_required) - throw std::string ("Additional text must be provided."); + } else if (!mods && text_required) + throw std::string("Additional text must be provided."); // Modifying completed/deleted tasks generates a message, if the modification // does not change status. - if ((getStatus () == Task::completed || getStatus () == Task::deleted) && - getStatus () == originalStatus) - { - auto uuid = get ("uuid").substr (0, 8); - Context::getContext ().footnote (format ("Note: Modified task {1} is {2}. You may wish to make this task pending with: task {3} modify status:pending", uuid, get ("status"), uuid)); + if ((getStatus() == Task::completed || getStatus() == Task::deleted) && + getStatus() == originalStatus) { + auto uuid = get("uuid").substr(0, 8); + Context::getContext().footnote( + format("Note: Modified task {1} is {2}. You may wish to make this task pending with: task " + "{3} modify status:pending", + uuid, get("status"), uuid)); } } #endif @@ -2418,323 +2095,227 @@ void Task::modify (modType type, bool text_required /* = false */) //////////////////////////////////////////////////////////////////////////////// // Compare this task to another and summarize the differences for display, in // the future tense ("Foo will be set to .."). -std::string Task::diff (const Task& after) const -{ +std::string Task::diff(const Task& after) const { // Attributes are all there is, so figure the different attribute names // between this (before) and after. - std::vector beforeAtts; - for (auto& att : data) - beforeAtts.push_back (att.first); + std::vector beforeAtts; + for (auto& att : data) beforeAtts.push_back(att.first); - std::vector afterAtts; - for (auto& att : after.data) - afterAtts.push_back (att.first); + std::vector afterAtts; + for (auto& att : after.data) afterAtts.push_back(att.first); - std::vector beforeOnly; - std::vector afterOnly; - listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); + std::vector beforeOnly; + std::vector afterOnly; + listDiff(beforeAtts, afterAtts, beforeOnly, afterOnly); // Now start generating a description of the differences. std::stringstream out; - for (auto& name : beforeOnly) - { - if (isAnnotationAttr (name)) - { - out << " - " - << format ("Annotation {1} will be removed.", name) - << "\n"; - } - else if (isTagAttr (name)) - { - out << " - " - << format ("Tag {1} will be removed.", attr2Tag (name)) - << "\n"; - } - else if (isDepAttr (name)) - { - out << " - " - << format ("Depenency on {1} will be removed.", attr2Dep (name)) - << "\n"; - } - else if (name == "depends" || name == "tags") - { + for (auto& name : beforeOnly) { + if (isAnnotationAttr(name)) { + out << " - " << format("Annotation {1} will be removed.", name) << "\n"; + } else if (isTagAttr(name)) { + out << " - " << format("Tag {1} will be removed.", attr2Tag(name)) << "\n"; + } else if (isDepAttr(name)) { + out << " - " << format("Depenency on {1} will be removed.", attr2Dep(name)) << "\n"; + } else if (name == "depends" || name == "tags") { // do nothing for legacy attributes - } - else - { - out << " - " - << format ("{1} will be deleted.", Lexer::ucFirst (name)) - << "\n"; + } else { + out << " - " << format("{1} will be deleted.", Lexer::ucFirst(name)) << "\n"; } } - for (auto& name : afterOnly) - { - if (isAnnotationAttr (name)) - { - out << format ("Annotation of {1} will be added.\n", after.get (name)); - } - else if (isTagAttr (name)) - { - out << format ("Tag {1} will be added.\n", attr2Tag (name)); - } - else if (isDepAttr (name)) - { - out << format ("Dependency on {1} will be added.\n", attr2Dep (name)); - } - else if (name == "depends" || name == "tags") - { + for (auto& name : afterOnly) { + if (isAnnotationAttr(name)) { + out << format("Annotation of {1} will be added.\n", after.get(name)); + } else if (isTagAttr(name)) { + out << format("Tag {1} will be added.\n", attr2Tag(name)); + } else if (isDepAttr(name)) { + out << format("Dependency on {1} will be added.\n", attr2Dep(name)); + } else if (name == "depends" || name == "tags") { // do nothing for legacy attributes - } - else + } else out << " - " - << format ("{1} will be set to '{2}'.", - Lexer::ucFirst (name), - renderAttribute (name, after.get (name))) + << format("{1} will be set to '{2}'.", Lexer::ucFirst(name), + renderAttribute(name, after.get(name))) << "\n"; } - for (auto& name : beforeAtts) - { + for (auto& name : beforeAtts) { // Ignore UUID differences, and find values that changed, but are not also // in the beforeOnly and afterOnly lists, which have been handled above.. - if (name != "uuid" && - get (name) != after.get (name) && - std::find (beforeOnly.begin (), beforeOnly.end (), name) == beforeOnly.end () && - std::find (afterOnly.begin (), afterOnly.end (), name) == afterOnly.end ()) - { - if (name == "depends" || name == "tags") - { + if (name != "uuid" && get(name) != after.get(name) && + std::find(beforeOnly.begin(), beforeOnly.end(), name) == beforeOnly.end() && + std::find(afterOnly.begin(), afterOnly.end(), name) == afterOnly.end()) { + if (name == "depends" || name == "tags") { // do nothing for legacy attributes - } - else if (isTagAttr (name) || isDepAttr (name)) - { + } else if (isTagAttr(name) || isDepAttr(name)) { // ignore new attributes - } - else if (isAnnotationAttr (name)) - { - out << format ("Annotation will be changed to {1}.\n", after.get (name)); - } - else + } else if (isAnnotationAttr(name)) { + out << format("Annotation will be changed to {1}.\n", after.get(name)); + } else out << " - " - << format ("{1} will be changed from '{2}' to '{3}'.", - Lexer::ucFirst (name), - renderAttribute (name, get (name)), - renderAttribute (name, after.get (name))) + << format("{1} will be changed from '{2}' to '{3}'.", Lexer::ucFirst(name), + renderAttribute(name, get(name)), renderAttribute(name, after.get(name))) << "\n"; } } // Shouldn't just say nothing. - if (out.str ().length () == 0) - out << " - No changes will be made.\n"; + if (out.str().length() == 0) out << " - No changes will be made.\n"; - return out.str (); + return out.str(); } //////////////////////////////////////////////////////////////////////////////// // Similar to diff, but formatted for inclusion in the output of the info command -std::string Task::diffForInfo ( - const Task& after, - const std::string& dateformat, - long& last_timestamp, - const long current_timestamp) const -{ +std::string Task::diffForInfo(const Task& after, const std::string& dateformat, + long& last_timestamp, const long current_timestamp) const { // Attributes are all there is, so figure the different attribute names // between before and after. - std::vector beforeAtts; - for (auto& att : data) - beforeAtts.push_back (att.first); + std::vector beforeAtts; + for (auto& att : data) beforeAtts.push_back(att.first); - std::vector afterAtts; - for (auto& att : after.data) - afterAtts.push_back (att.first); + std::vector afterAtts; + for (auto& att : after.data) afterAtts.push_back(att.first); - std::vector beforeOnly; - std::vector afterOnly; - listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); + std::vector beforeOnly; + std::vector afterOnly; + listDiff(beforeAtts, afterAtts, beforeOnly, afterOnly); // Now start generating a description of the differences. std::stringstream out; - for (auto& name : beforeOnly) - { - if (isAnnotationAttr (name)) - { - out << format ("Annotation '{1}' deleted.\n", get (name)); - } - else if (isTagAttr (name)) - { - out << format ("Tag '{1}' deleted.\n", attr2Tag(name)); - } - else if (isDepAttr (name)) - { - out << format ("Dependency on '{1}' deleted.\n", attr2Dep(name)); - } - else if (name == "depends" || name == "tags") - { + for (auto& name : beforeOnly) { + if (isAnnotationAttr(name)) { + out << format("Annotation '{1}' deleted.\n", get(name)); + } else if (isTagAttr(name)) { + out << format("Tag '{1}' deleted.\n", attr2Tag(name)); + } else if (isDepAttr(name)) { + out << format("Dependency on '{1}' deleted.\n", attr2Dep(name)); + } else if (name == "depends" || name == "tags") { // do nothing for legacy attributes - } - else if (name == "start") - { - Datetime started (get ("start")); + } else if (name == "start") { + Datetime started(get("start")); Datetime stopped; - if (after.has ("end")) + if (after.has("end")) // Task was marked as finished, use end time - stopped = Datetime (after.get ("end")); + stopped = Datetime(after.get("end")); else // Start attribute was removed, use modification time - stopped = Datetime (current_timestamp); + stopped = Datetime(current_timestamp); - out << format ("{1} deleted (duration: {2}).", - Lexer::ucFirst (name), - Duration (stopped - started).format ()) + out << format("{1} deleted (duration: {2}).", Lexer::ucFirst(name), + Duration(stopped - started).format()) << "\n"; - } - else - { - out << format ("{1} deleted.\n", Lexer::ucFirst (name)); + } else { + out << format("{1} deleted.\n", Lexer::ucFirst(name)); } } - for (auto& name : afterOnly) - { - if (isAnnotationAttr (name)) - { - out << format ("Annotation of '{1}' added.\n", after.get (name)); - } - else if (isTagAttr (name)) - { - out << format ("Tag '{1}' added.\n", attr2Tag (name)); - } - else if (isDepAttr (name)) - { - out << format ("Dependency on '{1}' added.\n", attr2Dep (name)); - } - else if (name == "depends" || name == "tags") - { + for (auto& name : afterOnly) { + if (isAnnotationAttr(name)) { + out << format("Annotation of '{1}' added.\n", after.get(name)); + } else if (isTagAttr(name)) { + out << format("Tag '{1}' added.\n", attr2Tag(name)); + } else if (isDepAttr(name)) { + out << format("Dependency on '{1}' added.\n", attr2Dep(name)); + } else if (name == "depends" || name == "tags") { // do nothing for legacy attributes - } - else - { - if (name == "start") - last_timestamp = current_timestamp; + } else { + if (name == "start") last_timestamp = current_timestamp; - out << format ("{1} set to '{2}'.", - Lexer::ucFirst (name), - renderAttribute (name, after.get (name), dateformat)) + out << format("{1} set to '{2}'.", Lexer::ucFirst(name), + renderAttribute(name, after.get(name), dateformat)) << "\n"; } } for (auto& name : beforeAtts) - if (name != "uuid" && - name != "modified" && - get (name) != after.get (name) && - get (name) != "" && - after.get (name) != "") - { - if (name == "depends" || name == "tags") - { + if (name != "uuid" && name != "modified" && get(name) != after.get(name) && get(name) != "" && + after.get(name) != "") { + if (name == "depends" || name == "tags") { // do nothing for legacy attributes - } - else if (isTagAttr (name) || isDepAttr (name)) - { + } else if (isTagAttr(name) || isDepAttr(name)) { // ignore new attributes - } - else if (isAnnotationAttr (name)) - { - out << format ("Annotation changed to '{1}'.\n", after.get (name)); - } - else - out << format ("{1} changed from '{2}' to '{3}'.", - Lexer::ucFirst (name), - renderAttribute (name, get (name), dateformat), - renderAttribute (name, after.get (name), dateformat)) + } else if (isAnnotationAttr(name)) { + out << format("Annotation changed to '{1}'.\n", after.get(name)); + } else + out << format("{1} changed from '{2}' to '{3}'.", Lexer::ucFirst(name), + renderAttribute(name, get(name), dateformat), + renderAttribute(name, after.get(name), dateformat)) << "\n"; } // Shouldn't just say nothing. - if (out.str ().length () == 0) - out << "No changes made.\n"; + if (out.str().length() == 0) out << "No changes made.\n"; - return out.str (); + return out.str(); } //////////////////////////////////////////////////////////////////////////////// // Similar to diff, but formatted as a side-by-side table for an Undo preview -Table Task::diffForUndoSide ( - const Task& after) const -{ +Table Task::diffForUndoSide(const Task& after) const { // Set the colors. - Color color_red (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.before") : ""); - Color color_green (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.after") : ""); + Color color_red( + Context::getContext().color() ? Context::getContext().config.get("color.undo.before") : ""); + Color color_green( + Context::getContext().color() ? Context::getContext().config.get("color.undo.after") : ""); // Attributes are all there is, so figure the different attribute names // between before and after. Table view; - view.width (Context::getContext ().getWidth ()); - view.intraPadding (2); - view.add (""); - view.add ("Prior Values"); - view.add ("Current Values"); - setHeaderUnderline (view); + view.width(Context::getContext().getWidth()); + view.intraPadding(2); + view.add(""); + view.add("Prior Values"); + view.add("Current Values"); + setHeaderUnderline(view); - if (!is_empty ()) - { - const Task &before = *this; + if (!is_empty()) { + const Task& before = *this; - std::vector beforeAtts; - for (auto& att : before.data) - beforeAtts.push_back (att.first); + std::vector beforeAtts; + for (auto& att : before.data) beforeAtts.push_back(att.first); - std::vector afterAtts; - for (auto& att : after.data) - afterAtts.push_back (att.first); + std::vector afterAtts; + for (auto& att : after.data) afterAtts.push_back(att.first); - std::vector beforeOnly; - std::vector afterOnly; - listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly); + std::vector beforeOnly; + std::vector afterOnly; + listDiff(beforeAtts, afterAtts, beforeOnly, afterOnly); int row; - for (auto& name : beforeOnly) - { - row = view.addRow (); - view.set (row, 0, name); - view.set (row, 1, renderAttribute (name, before.get (name)), color_red); + for (auto& name : beforeOnly) { + row = view.addRow(); + view.set(row, 0, name); + view.set(row, 1, renderAttribute(name, before.get(name)), color_red); } - for (auto& att : before.data) - { - std::string priorValue = before.get (att.first); - std::string currentValue = after.get (att.first); + for (auto& att : before.data) { + std::string priorValue = before.get(att.first); + std::string currentValue = after.get(att.first); - if (currentValue != "") - { - row = view.addRow (); - view.set (row, 0, att.first); - view.set (row, 1, renderAttribute (att.first, priorValue), - (priorValue != currentValue ? color_red : Color ())); - view.set (row, 2, renderAttribute (att.first, currentValue), - (priorValue != currentValue ? color_green : Color ())); + if (currentValue != "") { + row = view.addRow(); + view.set(row, 0, att.first); + view.set(row, 1, renderAttribute(att.first, priorValue), + (priorValue != currentValue ? color_red : Color())); + view.set(row, 2, renderAttribute(att.first, currentValue), + (priorValue != currentValue ? color_green : Color())); } } - for (auto& name : afterOnly) - { - row = view.addRow (); - view.set (row, 0, name); - view.set (row, 2, renderAttribute (name, after.get (name)), color_green); + for (auto& name : afterOnly) { + row = view.addRow(); + view.set(row, 0, name); + view.set(row, 2, renderAttribute(name, after.get(name)), color_green); } - } - else - { + } else { int row; - for (auto& att : after.data) - { - row = view.addRow (); - view.set (row, 0, att.first); - view.set (row, 2, renderAttribute (att.first, after.get (att.first)), color_green); + for (auto& att : after.data) { + row = view.addRow(); + view.set(row, 0, att.first); + view.set(row, 2, renderAttribute(att.first, after.get(att.first)), color_green); } } @@ -2743,10 +2324,7 @@ Table Task::diffForUndoSide ( //////////////////////////////////////////////////////////////////////////////// // Similar to diff, but formatted as a diff for an Undo preview -Table Task::diffForUndoPatch ( - const Task& after, - const Datetime& lastChange) const -{ +Table Task::diffForUndoPatch(const Task& after, const Datetime& lastChange) const { // This style looks like this: // --- before 2009-07-04 00:00:25.000000000 +0200 // +++ after 2009-07-04 00:00:45.000000000 +0200 @@ -2762,62 +2340,60 @@ Table Task::diffForUndoPatch ( // // Set the colors. - Color color_red (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.before") : ""); - Color color_green (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.after") : ""); + Color color_red( + Context::getContext().color() ? Context::getContext().config.get("color.undo.before") : ""); + Color color_green( + Context::getContext().color() ? Context::getContext().config.get("color.undo.after") : ""); - const Task &before = *this; + const Task& before = *this; // Generate table header. Table view; - view.width (Context::getContext ().getWidth ()); - view.intraPadding (2); - view.add (""); - view.add (""); + view.width(Context::getContext().getWidth()); + view.intraPadding(2); + view.add(""); + view.add(""); - int row = view.addRow (); - view.set (row, 0, "--- previous state", color_red); - view.set (row, 1, "Undo will restore this state", color_red); + int row = view.addRow(); + view.set(row, 0, "--- previous state", color_red); + view.set(row, 1, "Undo will restore this state", color_red); - row = view.addRow (); - view.set (row, 0, "+++ current state ", color_green); - view.set (row, 1, format ("Change made {1}", - lastChange.toString (Context::getContext ().config.get ("dateformat"))), - color_green); + row = view.addRow(); + view.set(row, 0, "+++ current state ", color_green); + view.set(row, 1, + format("Change made {1}", + lastChange.toString(Context::getContext().config.get("dateformat"))), + color_green); - view.addRow (); + view.addRow(); // Add rows to table showing diffs. - std::vector all = Context::getContext ().getColumns (); + std::vector all = Context::getContext().getColumns(); // Now factor in the annotation attributes. for (auto& it : before.data) - if (it.first.substr (0, 11) == "annotation_") - all.push_back (it.first); + if (it.first.substr(0, 11) == "annotation_") all.push_back(it.first); for (auto& it : after.data) - if (it.first.substr (0, 11) == "annotation_") - all.push_back (it.first); + if (it.first.substr(0, 11) == "annotation_") all.push_back(it.first); // Now render all the attributes. - std::sort (all.begin (), all.end ()); + std::sort(all.begin(), all.end()); std::string before_att; std::string after_att; std::string last_att; - for (auto& a : all) - { + for (auto& a : all) { if (a != last_att) // Skip duplicates. { last_att = a; - before_att = before.get (a); - after_att = after.get (a); + before_att = before.get(a); + after_att = after.get(a); // Don't report different uuid. // Show nothing if values are the unchanged. - if (a == "uuid" || - before_att == after_att) - { + if (a == "uuid" || before_att == after_att) { // Show nothing - no point displaying that which did not change. // row = view.addRow (); @@ -2826,37 +2402,34 @@ Table Task::diffForUndoPatch ( } // Attribute deleted. - else if (before_att != "" && after_att == "") - { - row = view.addRow (); - view.set (row, 0, '-' + a + ':', color_red); - view.set (row, 1, before_att, color_red); + else if (before_att != "" && after_att == "") { + row = view.addRow(); + view.set(row, 0, '-' + a + ':', color_red); + view.set(row, 1, before_att, color_red); - row = view.addRow (); - view.set (row, 0, '+' + a + ':', color_green); + row = view.addRow(); + view.set(row, 0, '+' + a + ':', color_green); } // Attribute added. - else if (before_att == "" && after_att != "") - { - row = view.addRow (); - view.set (row, 0, '-' + a + ':', color_red); + else if (before_att == "" && after_att != "") { + row = view.addRow(); + view.set(row, 0, '-' + a + ':', color_red); - row = view.addRow (); - view.set (row, 0, '+' + a + ':', color_green); - view.set (row, 1, after_att, color_green); + row = view.addRow(); + view.set(row, 0, '+' + a + ':', color_green); + view.set(row, 1, after_att, color_green); } // Attribute changed. - else - { - row = view.addRow (); - view.set (row, 0, '-' + a + ':', color_red); - view.set (row, 1, before_att, color_red); + else { + row = view.addRow(); + view.set(row, 0, '-' + a + ':', color_red); + view.set(row, 1, before_att, color_red); - row = view.addRow (); - view.set (row, 0, '+' + a + ':', color_green); - view.set (row, 1, after_att, color_green); + row = view.addRow(); + view.set(row, 0, '+' + a + ':', color_green); + view.set(row, 1, after_att, color_green); } } } diff --git a/src/Task.h b/src/Task.h index 3f4e16e16..28acb5c08 100644 --- a/src/Task.h +++ b/src/Task.h @@ -27,27 +27,27 @@ #ifndef INCLUDED_TASK #define INCLUDED_TASK -#include -#include -#include -#include -#include +#include #include #include -#include +#include #include +#include -class Task -{ -public: +#include +#include +#include + +class Task { + public: static std::string defaultProject; static std::string defaultDue; static std::string defaultScheduled; static bool searchCaseSensitive; static bool regex; - static std::map attributes; // name -> type - static std::map coefficients; - static std::map > customOrder; + static std::map attributes; // name -> type + static std::map coefficients; + static std::map> customOrder; static float urgencyProjectCoefficient; static float urgencyActiveCoefficient; static float urgencyScheduledCoefficient; @@ -60,159 +60,159 @@ public: static float urgencyAgeCoefficient; static float urgencyAgeMax; -public: - Task () = default; - bool operator== (const Task&); - bool operator!= (const Task&); - Task (const std::string&); - Task (const json::object*); - Task (tc::Task); + public: + Task() = default; + bool operator==(const Task&); + bool operator!=(const Task&); + Task(const std::string&); + Task(const json::object*); + Task(tc::Task); - void parse (const std::string&); - std::string composeJSON (bool decorate = false) const; + void parse(const std::string&); + std::string composeJSON(bool decorate = false) const; // Status values. - enum status {pending, completed, deleted, recurring, waiting}; + enum status { pending, completed, deleted, recurring, waiting }; // Date state values. - enum dateState {dateNotDue, dateAfterToday, dateLaterToday, dateEarlierToday, dateBeforeToday}; + enum dateState { dateNotDue, dateAfterToday, dateLaterToday, dateEarlierToday, dateBeforeToday }; // Public data. - int id {0}; - float urgency_value {0.0}; - bool recalc_urgency {true}; - bool is_blocked {false}; - bool is_blocking {false}; - int annotation_count {0}; + int id{0}; + float urgency_value{0.0}; + bool recalc_urgency{true}; + bool is_blocked{false}; + bool is_blocking{false}; + int annotation_count{0}; // Series of helper functions. - static status textToStatus (const std::string&); - static std::string statusToText (status); - static tc::Status status2tc (const Task::status); - static Task::status tc2status (const tc::Status); + static status textToStatus(const std::string&); + static std::string statusToText(status); + static tc::Status status2tc(const Task::status); + static Task::status tc2status(const tc::Status); - void setAsNow (const std::string&); - bool has (const std::string&) const; - std::vector all () const; - const std::string identifier (bool shortened = false) const; - const std::string get (const std::string&) const; - const std::string& get_ref (const std::string&) const; - int get_int (const std::string&) const; - unsigned long get_ulong (const std::string&) const; - float get_float (const std::string&) const; - time_t get_date (const std::string&) const; - void set (const std::string&, const std::string&); - void set (const std::string&, long long); - void remove (const std::string&); + void setAsNow(const std::string&); + bool has(const std::string&) const; + std::vector all() const; + const std::string identifier(bool shortened = false) const; + const std::string get(const std::string&) const; + const std::string& get_ref(const std::string&) const; + int get_int(const std::string&) const; + unsigned long get_ulong(const std::string&) const; + float get_float(const std::string&) const; + time_t get_date(const std::string&) const; + void set(const std::string&, const std::string&); + void set(const std::string&, long long); + void remove(const std::string&); - bool is_empty () const; + bool is_empty() const; #ifdef PRODUCT_TASKWARRIOR - bool is_ready () const; - bool is_due () const; - bool is_dueyesterday () const; - bool is_duetoday () const; - bool is_duetomorrow () const; - bool is_dueweek () const; - bool is_duemonth () const; - bool is_duequarter () const; - bool is_dueyear () const; - bool is_overdue () const; - bool is_udaPresent () const; - bool is_orphanPresent () const; + bool is_ready() const; + bool is_due() const; + bool is_dueyesterday() const; + bool is_duetoday() const; + bool is_duetomorrow() const; + bool is_dueweek() const; + bool is_duemonth() const; + bool is_duequarter() const; + bool is_dueyear() const; + bool is_overdue() const; + bool is_udaPresent() const; + bool is_orphanPresent() const; - static bool isTagAttr (const std::string&); - static bool isDepAttr (const std::string&); - static bool isAnnotationAttr (const std::string&); + static bool isTagAttr(const std::string&); + static bool isDepAttr(const std::string&); + static bool isAnnotationAttr(const std::string&); #endif - bool is_waiting () const; + bool is_waiting() const; - status getStatus () const; - void setStatus (status); + status getStatus() const; + void setStatus(status); #ifdef PRODUCT_TASKWARRIOR - dateState getDateState (const std::string&) const; + dateState getDateState(const std::string&) const; #endif - int getTagCount () const; - bool hasTag (const std::string&) const; - void addTag (const std::string&); - void setTags (const std::vector &); - std::vector getTags () const; - void removeTag (const std::string&); + int getTagCount() const; + bool hasTag(const std::string&) const; + void addTag(const std::string&); + void setTags(const std::vector&); + std::vector getTags() const; + void removeTag(const std::string&); - int getAnnotationCount () const; - bool hasAnnotations () const; - std::map getAnnotations () const; - void setAnnotations (const std::map &); - void addAnnotation (const std::string&); - void removeAnnotations (); + int getAnnotationCount() const; + bool hasAnnotations() const; + std::map getAnnotations() const; + void setAnnotations(const std::map&); + void addAnnotation(const std::string&); + void removeAnnotations(); #ifdef PRODUCT_TASKWARRIOR - void addDependency (int); + void addDependency(int); #endif - void addDependency (const std::string&); + void addDependency(const std::string&); #ifdef PRODUCT_TASKWARRIOR - void removeDependency (int); - void removeDependency (const std::string&); - bool hasDependency (const std::string&) const; - std::vector getDependencyIDs () const; - std::vector getDependencyUUIDs () const; - std::vector getBlockedTasks () const; - std::vector getDependencyTasks () const; + void removeDependency(int); + void removeDependency(const std::string&); + bool hasDependency(const std::string&) const; + std::vector getDependencyIDs() const; + std::vector getDependencyUUIDs() const; + std::vector getBlockedTasks() const; + std::vector getDependencyTasks() const; - std::vector getUDAOrphans () const; + std::vector getUDAOrphans() const; - void substitute (const std::string&, const std::string&, const std::string&); + void substitute(const std::string&, const std::string&, const std::string&); #endif - void validate (bool applyDefault = true); + void validate(bool applyDefault = true); - float urgency_c () const; - float urgency (); + float urgency_c() const; + float urgency(); #ifdef PRODUCT_TASKWARRIOR - enum modType {modReplace, modPrepend, modAppend, modAnnotate}; - void modify (modType, bool text_required = false); + enum modType { modReplace, modPrepend, modAppend, modAnnotate }; + void modify(modType, bool text_required = false); #endif - std::string diff (const Task& after) const; - std::string diffForInfo (const Task& after, const std::string& dateformat, long& last_timestamp, const long current_timestamp) const; - Table diffForUndoSide (const Task& after) const; - Table diffForUndoPatch (const Task& after, const Datetime& lastChange) const; + std::string diff(const Task& after) const; + std::string diffForInfo(const Task& after, const std::string& dateformat, long& last_timestamp, + const long current_timestamp) const; + Table diffForUndoSide(const Task& after) const; + Table diffForUndoPatch(const Task& after, const Datetime& lastChange) const; + private: + int determineVersion(const std::string&); + void parseJSON(const std::string&); + void parseJSON(const json::object*); + void parseTC(const tc::Task&); + void parseLegacy(const std::string&); + void validate_before(const std::string&, const std::string&); + const std::string encode(const std::string&) const; + const std::string decode(const std::string&) const; + const std::string tag2Attr(const std::string&) const; + const std::string attr2Tag(const std::string&) const; + const std::string dep2Attr(const std::string&) const; + const std::string attr2Dep(const std::string&) const; + void fixDependsAttribute(); + void fixTagsAttribute(); -private: - int determineVersion (const std::string&); - void parseJSON (const std::string&); - void parseJSON (const json::object*); - void parseTC (const tc::Task&); - void parseLegacy (const std::string&); - void validate_before (const std::string&, const std::string&); - const std::string encode (const std::string&) const; - const std::string decode (const std::string&) const; - const std::string tag2Attr (const std::string&) const; - const std::string attr2Tag (const std::string&) const; - const std::string dep2Attr (const std::string&) const; - const std::string attr2Dep (const std::string&) const; - void fixDependsAttribute (); - void fixTagsAttribute (); + protected: + std::map data{}; -protected: - std::map data {}; - -public: - float urgency_project () const; - float urgency_active () const; - float urgency_scheduled () const; - float urgency_waiting () const; - float urgency_blocked () const; - float urgency_inherit () const; - float urgency_annotations () const; - float urgency_tags () const; - float urgency_due () const; - float urgency_blocking () const; - float urgency_age () const; + public: + float urgency_project() const; + float urgency_active() const; + float urgency_scheduled() const; + float urgency_waiting() const; + float urgency_blocked() const; + float urgency_inherit() const; + float urgency_annotations() const; + float urgency_tags() const; + float urgency_due() const; + float urgency_blocking() const; + float urgency_age() const; }; #endif diff --git a/src/Variant.cpp b/src/Variant.cpp index 3d34f7327..a5b68f172 100644 --- a/src/Variant.cpp +++ b/src/Variant.cpp @@ -27,927 +27,944 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include -#include #include #include #include #include +#include +#include #include +#include +#include + +#include +#include // These are all error messages generated by the expression evaluator, and are // mostly concerned with how various operators interact with the different // data types. -#define STRING_VARIANT_TIME_T "Cannot instantiate this type with a time_t value." -#define STRING_VARIANT_EXP_BOOL "Cannot exponentiate Booleans" -#define STRING_VARIANT_EXP_NON_INT "Cannot exponentiate to a non-integer power" -#define STRING_VARIANT_EXP_STRING "Cannot exponentiate strings" -#define STRING_VARIANT_EXP_DATE "Cannot exponentiate dates" -#define STRING_VARIANT_EXP_DURATION "Cannot exponentiate durations" -#define STRING_VARIANT_SUB_BOOL "Cannot subtract from a Boolean value" -#define STRING_VARIANT_SUB_STRING "Cannot subtract strings" -#define STRING_VARIANT_SUB_DATE "Cannot subtract a date" -#define STRING_VARIANT_ADD_BOOL "Cannot add two Boolean values" -#define STRING_VARIANT_ADD_DATE "Cannot add two date values" -#define STRING_VARIANT_MUL_BOOL "Cannot multiply Boolean values" -#define STRING_VARIANT_MUL_DATE "Cannot multiply date values" -#define STRING_VARIANT_MUL_REAL_STR "Cannot multiply real numbers by strings" -#define STRING_VARIANT_MUL_STR_REAL "Cannot multiply strings by real numbers" -#define STRING_VARIANT_MUL_STR_STR "Cannot multiply strings by strings" -#define STRING_VARIANT_MUL_STR_DATE "Cannot multiply strings by dates" -#define STRING_VARIANT_MUL_STR_DUR "Cannot multiply strings by durations" -#define STRING_VARIANT_MUL_DUR_STR "Cannot multiply durations by strings" -#define STRING_VARIANT_MUL_DUR_DATE "Cannot multiply durations by dates" -#define STRING_VARIANT_MUL_DUR_DUR "Cannot multiply durations by durations" -#define STRING_VARIANT_DIV_BOOL "Cannot divide Boolean values" -#define STRING_VARIANT_DIV_INT_BOOL "Cannot divide integers by Boolean values" -#define STRING_VARIANT_DIV_ZERO "Cannot divide by zero" -#define STRING_VARIANT_DIV_INT_STR "Cannot divide integer by string" -#define STRING_VARIANT_DIV_INT_DATE "Cannot divide integer by date values" +#define STRING_VARIANT_TIME_T "Cannot instantiate this type with a time_t value." +#define STRING_VARIANT_EXP_BOOL "Cannot exponentiate Booleans" +#define STRING_VARIANT_EXP_NON_INT "Cannot exponentiate to a non-integer power" +#define STRING_VARIANT_EXP_STRING "Cannot exponentiate strings" +#define STRING_VARIANT_EXP_DATE "Cannot exponentiate dates" +#define STRING_VARIANT_EXP_DURATION "Cannot exponentiate durations" +#define STRING_VARIANT_SUB_BOOL "Cannot subtract from a Boolean value" +#define STRING_VARIANT_SUB_STRING "Cannot subtract strings" +#define STRING_VARIANT_SUB_DATE "Cannot subtract a date" +#define STRING_VARIANT_ADD_BOOL "Cannot add two Boolean values" +#define STRING_VARIANT_ADD_DATE "Cannot add two date values" +#define STRING_VARIANT_MUL_BOOL "Cannot multiply Boolean values" +#define STRING_VARIANT_MUL_DATE "Cannot multiply date values" +#define STRING_VARIANT_MUL_REAL_STR "Cannot multiply real numbers by strings" +#define STRING_VARIANT_MUL_STR_REAL "Cannot multiply strings by real numbers" +#define STRING_VARIANT_MUL_STR_STR "Cannot multiply strings by strings" +#define STRING_VARIANT_MUL_STR_DATE "Cannot multiply strings by dates" +#define STRING_VARIANT_MUL_STR_DUR "Cannot multiply strings by durations" +#define STRING_VARIANT_MUL_DUR_STR "Cannot multiply durations by strings" +#define STRING_VARIANT_MUL_DUR_DATE "Cannot multiply durations by dates" +#define STRING_VARIANT_MUL_DUR_DUR "Cannot multiply durations by durations" +#define STRING_VARIANT_DIV_BOOL "Cannot divide Boolean values" +#define STRING_VARIANT_DIV_INT_BOOL "Cannot divide integers by Boolean values" +#define STRING_VARIANT_DIV_ZERO "Cannot divide by zero" +#define STRING_VARIANT_DIV_INT_STR "Cannot divide integer by string" +#define STRING_VARIANT_DIV_INT_DATE "Cannot divide integer by date values" #define STRING_VARIANT_DIV_REAL_BOOL "Cannot divide real by Boolean" -#define STRING_VARIANT_DIV_REAL_STR "Cannot divide real numbers by strings" +#define STRING_VARIANT_DIV_REAL_STR "Cannot divide real numbers by strings" #define STRING_VARIANT_DIV_REAL_DATE "Cannot divide real numbers by dates" -#define STRING_VARIANT_DIV_DUR_BOOL "Cannot divide duration by Boolean" -#define STRING_VARIANT_DIV_DUR_STR "Cannot divide durations by strings" -#define STRING_VARIANT_DIV_DUR_DATE "Cannot divide durations by dates" -#define STRING_VARIANT_DIV_DUR_DUR "Cannot divide durations by durations" -#define STRING_VARIANT_MOD_BOOL "Cannot modulo Booleans" -#define STRING_VARIANT_MOD_DATE "Cannot modulo date values" -#define STRING_VARIANT_MOD_DUR "Cannot modulo duration values" -#define STRING_VARIANT_MOD_INT_BOOL "Cannot modulo integer by Boolean" -#define STRING_VARIANT_MOD_INT_DATE "Cannot modulo integer by date values" -#define STRING_VARIANT_MOD_INT_DUR "Cannot modulo integer by duration values" -#define STRING_VARIANT_MOD_INT_STR "Cannot modulo integer by string" +#define STRING_VARIANT_DIV_DUR_BOOL "Cannot divide duration by Boolean" +#define STRING_VARIANT_DIV_DUR_STR "Cannot divide durations by strings" +#define STRING_VARIANT_DIV_DUR_DATE "Cannot divide durations by dates" +#define STRING_VARIANT_DIV_DUR_DUR "Cannot divide durations by durations" +#define STRING_VARIANT_MOD_BOOL "Cannot modulo Booleans" +#define STRING_VARIANT_MOD_DATE "Cannot modulo date values" +#define STRING_VARIANT_MOD_DUR "Cannot modulo duration values" +#define STRING_VARIANT_MOD_INT_BOOL "Cannot modulo integer by Boolean" +#define STRING_VARIANT_MOD_INT_DATE "Cannot modulo integer by date values" +#define STRING_VARIANT_MOD_INT_DUR "Cannot modulo integer by duration values" +#define STRING_VARIANT_MOD_INT_STR "Cannot modulo integer by string" #define STRING_VARIANT_MOD_REAL_BOOL "Cannot modulo real by Boolean" -#define STRING_VARIANT_MOD_REAL_DUR "Cannot modulo real by duration values" +#define STRING_VARIANT_MOD_REAL_DUR "Cannot modulo real by duration values" #define STRING_VARIANT_MOD_REAL_DATE "Cannot modulo real numbers by dates" -#define STRING_VARIANT_MOD_REAL_STR "Cannot modulo real numbers by strings" -#define STRING_VARIANT_MOD_STR "Cannot modulo string values" -#define STRING_VARIANT_MOD_ZERO "Cannot modulo zero" -#define STRING_VARIANT_SQRT_NEG "Cannot take the square root of a negative number." +#define STRING_VARIANT_MOD_REAL_STR "Cannot modulo real numbers by strings" +#define STRING_VARIANT_MOD_STR "Cannot modulo string values" +#define STRING_VARIANT_MOD_ZERO "Cannot modulo zero" +#define STRING_VARIANT_SQRT_NEG "Cannot take the square root of a negative number." std::string Variant::dateFormat = ""; bool Variant::searchCaseSensitive = true; bool Variant::searchUsingRegex = true; //////////////////////////////////////////////////////////////////////////////// -Variant::Variant (const Variant& other) -{ - _type = other._type; - _bool = other._bool; - _integer = other._integer; - _real = other._real; - _string = other._string; - _date = other._date; +Variant::Variant(const Variant& other) { + _type = other._type; + _bool = other._bool; + _integer = other._integer; + _real = other._real; + _string = other._string; + _date = other._date; _duration = other._duration; - _source = other._source; + _source = other._source; } //////////////////////////////////////////////////////////////////////////////// -Variant::Variant (const bool value) -: _type (Variant::type_boolean) -, _bool (value) -{ -} +Variant::Variant(const bool value) : _type(Variant::type_boolean), _bool(value) {} //////////////////////////////////////////////////////////////////////////////// -Variant::Variant (const int value) -: _type (Variant::type_integer) -, _integer (value) -{ -} +Variant::Variant(const int value) : _type(Variant::type_integer), _integer(value) {} //////////////////////////////////////////////////////////////////////////////// -Variant::Variant (const double value) -: _type (Variant::type_real) -, _real (value) -{ -} +Variant::Variant(const double value) : _type(Variant::type_real), _real(value) {} //////////////////////////////////////////////////////////////////////////////// -Variant::Variant (const std::string& value) -: _type (Variant::type_string) -, _string (value) -{ -} +Variant::Variant(const std::string& value) : _type(Variant::type_string), _string(value) {} //////////////////////////////////////////////////////////////////////////////// -Variant::Variant (const char* value) -: _type (Variant::type_string) -, _string (value) -{ -} +Variant::Variant(const char* value) : _type(Variant::type_string), _string(value) {} //////////////////////////////////////////////////////////////////////////////// -Variant::Variant (const time_t value, const enum type new_type) -: _type (new_type) -{ - switch (new_type) - { - case type_date: _date = value; break; - case type_duration: _duration = value; break; - default: - throw std::string (STRING_VARIANT_TIME_T); +Variant::Variant(const time_t value, const enum type new_type) : _type(new_type) { + switch (new_type) { + case type_date: + _date = value; + break; + case type_duration: + _duration = value; + break; + default: + throw std::string(STRING_VARIANT_TIME_T); } } //////////////////////////////////////////////////////////////////////////////// -void Variant::source (const std::string& input) -{ - _source = input; -} +void Variant::source(const std::string& input) { _source = input; } //////////////////////////////////////////////////////////////////////////////// -const std::string& Variant::source () const -{ - return _source; -} +const std::string& Variant::source() const { return _source; } //////////////////////////////////////////////////////////////////////////////// -Variant& Variant::operator= (const Variant& other) = default; +Variant& Variant::operator=(const Variant& other) = default; //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator&& (const Variant& other) const -{ - Variant left (*this); - Variant right (other); +bool Variant::operator&&(const Variant& other) const { + Variant left(*this); + Variant right(other); - if (left._type == type_string) - Lexer::dequote (left._string); + if (left._type == type_string) Lexer::dequote(left._string); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - left.cast (type_boolean); - right.cast (type_boolean); + left.cast(type_boolean); + right.cast(type_boolean); return left._bool && right._bool; } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator|| (const Variant& other) const -{ - Variant left (*this); - Variant right (other); +bool Variant::operator||(const Variant& other) const { + Variant left(*this); + Variant right(other); - if (left._type == type_string) - Lexer::dequote (left._string); + if (left._type == type_string) Lexer::dequote(left._string); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - left.cast (type_boolean); - right.cast (type_boolean); + left.cast(type_boolean); + right.cast(type_boolean); return left._bool || right._bool; } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator_xor (const Variant& other) const -{ - Variant left (*this); - Variant right (other); +bool Variant::operator_xor(const Variant& other) const { + Variant left(*this); + Variant right(other); - if (left._type == type_string) - Lexer::dequote (left._string); + if (left._type == type_string) Lexer::dequote(left._string); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - left.cast (type_boolean); - right.cast (type_boolean); + left.cast(type_boolean); + right.cast(type_boolean); - return (left._bool && !right._bool) || - (!left._bool && right._bool); + return (left._bool && !right._bool) || (!left._bool && right._bool); } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator< (const Variant& other) const -{ - Variant left (*this); - Variant right (other); +bool Variant::operator<(const Variant& other) const { + Variant left(*this); + Variant right(other); - if (left._type == type_string) - Lexer::dequote (left._string); + if (left._type == type_string) Lexer::dequote(left._string); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - switch (left._type) - { - case type_boolean: - switch (right._type) - { - case type_boolean: return !left._bool && right._bool; - case type_integer: left.cast (type_integer); return left._integer < right._integer; - case type_real: left.cast (type_real); return left._real < right._real; - case type_string: left.cast (type_string); return left._string < right._string; - case type_date: left.cast (type_date); return left._date < right._date; - case type_duration: left.cast (type_duration); return left._duration < right._duration; - } - break; - - case type_integer: - switch (right._type) - { - case type_boolean: right.cast (type_integer); return left._integer < right._integer; - case type_integer: return left._integer < right._integer; - case type_real: left.cast (type_real); return left._real < right._real; - case type_string: left.cast (type_string); return left._string < right._string; - case type_date: left.cast (type_date); return left._date < right._date; - case type_duration: left.cast (type_duration); return left._duration < right._duration; - } - break; - - case type_real: - switch (right._type) - { - case type_boolean: right.cast (type_real); return left._real < right._real; - case type_integer: right.cast (type_real); return left._real < right._real; - case type_real: return left._real < right._real; - case type_string: left.cast (type_string); return left._string < right._string; - case type_date: left.cast (type_date); return left._date < right._date; - case type_duration: left.cast (type_duration); return left._duration < right._duration; - } - break; - - case type_string: - switch (right._type) - { + switch (left._type) { case type_boolean: - case type_integer: - case type_real: - right.cast (type_string); - return left._string < right._string; - - case type_string: - { - if (left._string == right._string) - return false; - - auto order = Task::customOrder.find (left.source ()); - if (order != Task::customOrder.end ()) - { - // Guaranteed to be found, because of ColUDA::validate (). - auto posLeft = std::find (order->second.begin (), order->second.end (), left._string); - auto posRight = std::find (order->second.begin (), order->second.end (), right._string); - return posLeft < posRight; - } - else - { - if (left.trivial () || right.trivial ()) - return false; - + switch (right._type) { + case type_boolean: + return !left._bool && right._bool; + case type_integer: + left.cast(type_integer); + return left._integer < right._integer; + case type_real: + left.cast(type_real); + return left._real < right._real; + case type_string: + left.cast(type_string); return left._string < right._string; - } + case type_date: + left.cast(type_date); + return left._date < right._date; + case type_duration: + left.cast(type_duration); + return left._duration < right._duration; } + break; - case type_date: - if (left.trivial () || right.trivial ()) - return false; - - left.cast (type_date); - return left._date < right._date; - - case type_duration: - if (left.trivial () || right.trivial ()) - return false; - - left.cast (type_duration); - return left._duration < right._duration; - } - break; - - case type_date: - switch (right._type) - { - case type_boolean: case type_integer: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + return left._integer < right._integer; + case type_integer: + return left._integer < right._integer; + case type_real: + left.cast(type_real); + return left._real < right._real; + case type_string: + left.cast(type_string); + return left._string < right._string; + case type_date: + left.cast(type_date); + return left._date < right._date; + case type_duration: + left.cast(type_duration); + return left._duration < right._duration; + } + break; + case type_real: + switch (right._type) { + case type_boolean: + right.cast(type_real); + return left._real < right._real; + case type_integer: + right.cast(type_real); + return left._real < right._real; + case type_real: + return left._real < right._real; + case type_string: + left.cast(type_string); + return left._string < right._string; + case type_date: + left.cast(type_date); + return left._date < right._date; + case type_duration: + left.cast(type_duration); + return left._duration < right._duration; + } + break; + case type_string: + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + right.cast(type_string); + return left._string < right._string; + + case type_string: { + if (left._string == right._string) return false; + + auto order = Task::customOrder.find(left.source()); + if (order != Task::customOrder.end()) { + // Guaranteed to be found, because of ColUDA::validate (). + auto posLeft = std::find(order->second.begin(), order->second.end(), left._string); + auto posRight = std::find(order->second.begin(), order->second.end(), right._string); + return posLeft < posRight; + } else { + if (left.trivial() || right.trivial()) return false; + + return left._string < right._string; + } + } + + case type_date: + if (left.trivial() || right.trivial()) return false; + + left.cast(type_date); + return left._date < right._date; + + case type_duration: + if (left.trivial() || right.trivial()) return false; + + left.cast(type_duration); + return left._duration < right._duration; + } + break; + case type_date: + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + if (left.trivial() || right.trivial()) return false; + + right.cast(type_date); + return left._date < right._date; + } + break; + case type_duration: - if (left.trivial () || right.trivial ()) - return false; + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + if (left.trivial() || right.trivial()) return false; - right.cast (type_date); - return left._date < right._date; - } - break; - - case type_duration: - switch (right._type) - { - case type_boolean: - case type_integer: - case type_real: - case type_string: - case type_date: - case type_duration: - if (left.trivial () || right.trivial ()) - return false; - - right.cast (type_duration); - return left._duration < right._duration; - } - break; + right.cast(type_duration); + return left._duration < right._duration; + } + break; } return false; } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator<= (const Variant& other) const -{ - Variant left (*this); - Variant right (other); +bool Variant::operator<=(const Variant& other) const { + Variant left(*this); + Variant right(other); - if (left._type == type_string) - Lexer::dequote (left._string); + if (left._type == type_string) Lexer::dequote(left._string); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - switch (left._type) - { - case type_boolean: - switch (right._type) - { - case type_boolean: return !left._bool || right._bool; - case type_integer: left.cast (type_integer); return left._integer <= right._integer; - case type_real: left.cast (type_real); return left._real <= right._real; - case type_string: left.cast (type_string); return left._string <= right._string; - case type_date: left.cast (type_date); return left._date <= right._date; - case type_duration: left.cast (type_duration); return left._duration <= right._duration; - } - break; - - case type_integer: - switch (right._type) - { - case type_boolean: right.cast (type_integer); return left._integer <= right._integer; - case type_integer: return left._integer <= right._integer; - case type_real: left.cast (type_real); return left._real <= right._real; - case type_string: left.cast (type_string); return left._string <= right._string; - case type_date: left.cast (type_date); return left._date <= right._date; - case type_duration: left.cast (type_duration); return left._duration <= right._duration; - } - break; - - case type_real: - switch (right._type) - { - case type_boolean: right.cast (type_real); return left._real <= right._real; - case type_integer: right.cast (type_real); return left._real <= right._real; - case type_real: return left._real <= right._real; - case type_string: left.cast (type_string); return left._string <= right._string; - case type_date: left.cast (type_date); return left._date <= right._date; - case type_duration: left.cast (type_duration); return left._duration <= right._duration; - } - break; - - case type_string: - switch (right._type) - { + switch (left._type) { case type_boolean: - case type_integer: - case type_real: - right.cast (type_string); - return left._string <= right._string; - - case type_string: - { - if (left._string == right._string) - return true; - - auto order = Task::customOrder.find (left.source ()); - if (order != Task::customOrder.end ()) - { - // Guaranteed to be found, because of ColUDA::validate (). - auto posLeft = std::find (order->second.begin (), order->second.end (), left._string); - auto posRight = std::find (order->second.begin (), order->second.end (), right._string); - return posLeft <= posRight; - } - else - { - if (left.trivial () || right.trivial ()) - return false; - + switch (right._type) { + case type_boolean: + return !left._bool || right._bool; + case type_integer: + left.cast(type_integer); + return left._integer <= right._integer; + case type_real: + left.cast(type_real); + return left._real <= right._real; + case type_string: + left.cast(type_string); return left._string <= right._string; - } + case type_date: + left.cast(type_date); + return left._date <= right._date; + case type_duration: + left.cast(type_duration); + return left._duration <= right._duration; } + break; - case type_date: - if (left.trivial () || right.trivial ()) - return false; - - left.cast (type_date); - return left._date <= right._date; - - case type_duration: - if (left.trivial () || right.trivial ()) - return false; - - left.cast (type_duration); - return left._duration <= right._duration; - } - break; - - case type_date: - switch (right._type) - { - case type_boolean: case type_integer: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + return left._integer <= right._integer; + case type_integer: + return left._integer <= right._integer; + case type_real: + left.cast(type_real); + return left._real <= right._real; + case type_string: + left.cast(type_string); + return left._string <= right._string; + case type_date: + left.cast(type_date); + return left._date <= right._date; + case type_duration: + left.cast(type_duration); + return left._duration <= right._duration; + } + break; + case type_real: + switch (right._type) { + case type_boolean: + right.cast(type_real); + return left._real <= right._real; + case type_integer: + right.cast(type_real); + return left._real <= right._real; + case type_real: + return left._real <= right._real; + case type_string: + left.cast(type_string); + return left._string <= right._string; + case type_date: + left.cast(type_date); + return left._date <= right._date; + case type_duration: + left.cast(type_duration); + return left._duration <= right._duration; + } + break; + case type_string: + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + right.cast(type_string); + return left._string <= right._string; + + case type_string: { + if (left._string == right._string) return true; + + auto order = Task::customOrder.find(left.source()); + if (order != Task::customOrder.end()) { + // Guaranteed to be found, because of ColUDA::validate (). + auto posLeft = std::find(order->second.begin(), order->second.end(), left._string); + auto posRight = std::find(order->second.begin(), order->second.end(), right._string); + return posLeft <= posRight; + } else { + if (left.trivial() || right.trivial()) return false; + + return left._string <= right._string; + } + } + + case type_date: + if (left.trivial() || right.trivial()) return false; + + left.cast(type_date); + return left._date <= right._date; + + case type_duration: + if (left.trivial() || right.trivial()) return false; + + left.cast(type_duration); + return left._duration <= right._duration; + } + break; + case type_date: + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + if (left.trivial() || right.trivial()) return false; + + right.cast(type_date); + return left._date <= right._date; + } + break; + case type_duration: - if (left.trivial () || right.trivial ()) - return false; + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + if (left.trivial() || right.trivial()) return false; - right.cast (type_date); - return left._date <= right._date; - } - break; - - case type_duration: - switch (right._type) - { - case type_boolean: - case type_integer: - case type_real: - case type_string: - case type_date: - case type_duration: - if (left.trivial () || right.trivial ()) - return false; - - right.cast (type_duration); - return left._duration <= right._duration; - } - break; + right.cast(type_duration); + return left._duration <= right._duration; + } + break; } return false; } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator> (const Variant& other) const -{ - Variant left (*this); - Variant right (other); +bool Variant::operator>(const Variant& other) const { + Variant left(*this); + Variant right(other); - if (left._type == type_string) - Lexer::dequote (left._string); + if (left._type == type_string) Lexer::dequote(left._string); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - switch (left._type) - { - case type_boolean: - switch (right._type) - { - case type_boolean: return !left._bool && right._bool; - case type_integer: left.cast (type_integer); return left._integer > right._integer; - case type_real: left.cast (type_real); return left._real > right._real; - case type_string: left.cast (type_string); return left._string > right._string; - case type_date: left.cast (type_date); return left._date > right._date; - case type_duration: left.cast (type_duration); return left._duration > right._duration; - } - break; - - case type_integer: - switch (right._type) - { - case type_boolean: right.cast (type_integer); return left._integer > right._integer; - case type_integer: return left._integer > right._integer; - case type_real: left.cast (type_real); return left._real > right._real; - case type_string: left.cast (type_string); return left._string > right._string; - case type_date: left.cast (type_date); return left._date > right._date; - case type_duration: left.cast (type_duration); return left._duration > right._duration; - } - break; - - case type_real: - switch (right._type) - { - case type_boolean: right.cast (type_real); return left._real > right._real; - case type_integer: right.cast (type_real); return left._real > right._real; - case type_real: return left._real > right._real; - case type_string: left.cast (type_string); return left._string > right._string; - case type_date: left.cast (type_date); return left._date > right._date; - case type_duration: left.cast (type_duration); return left._duration > right._duration; - } - break; - - case type_string: - switch (right._type) - { + switch (left._type) { case type_boolean: - case type_integer: - case type_real: - right.cast (type_string); - return left._string > right._string; - - case type_string: - { - if (left._string == right._string) - return false; - - auto order = Task::customOrder.find (left.source ()); - if (order != Task::customOrder.end ()) - { - // Guaranteed to be found, because of ColUDA::validate (). - auto posLeft = std::find (order->second.begin (), order->second.end (), left._string); - auto posRight = std::find (order->second.begin (), order->second.end (), right._string); - return posLeft > posRight; - } - else - { - if (left.trivial () || right.trivial ()) - return false; - + switch (right._type) { + case type_boolean: + return !left._bool && right._bool; + case type_integer: + left.cast(type_integer); + return left._integer > right._integer; + case type_real: + left.cast(type_real); + return left._real > right._real; + case type_string: + left.cast(type_string); return left._string > right._string; - } + case type_date: + left.cast(type_date); + return left._date > right._date; + case type_duration: + left.cast(type_duration); + return left._duration > right._duration; } + break; - case type_date: - if (left.trivial () || right.trivial ()) - return false; - - left.cast (type_date); - return left._date > right._date; - - case type_duration: - if (left.trivial () || right.trivial ()) - return false; - - left.cast (type_duration); - return left._duration > right._duration; - } - break; - - case type_date: - switch (right._type) - { - case type_boolean: case type_integer: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + return left._integer > right._integer; + case type_integer: + return left._integer > right._integer; + case type_real: + left.cast(type_real); + return left._real > right._real; + case type_string: + left.cast(type_string); + return left._string > right._string; + case type_date: + left.cast(type_date); + return left._date > right._date; + case type_duration: + left.cast(type_duration); + return left._duration > right._duration; + } + break; + case type_real: + switch (right._type) { + case type_boolean: + right.cast(type_real); + return left._real > right._real; + case type_integer: + right.cast(type_real); + return left._real > right._real; + case type_real: + return left._real > right._real; + case type_string: + left.cast(type_string); + return left._string > right._string; + case type_date: + left.cast(type_date); + return left._date > right._date; + case type_duration: + left.cast(type_duration); + return left._duration > right._duration; + } + break; + case type_string: + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + right.cast(type_string); + return left._string > right._string; + + case type_string: { + if (left._string == right._string) return false; + + auto order = Task::customOrder.find(left.source()); + if (order != Task::customOrder.end()) { + // Guaranteed to be found, because of ColUDA::validate (). + auto posLeft = std::find(order->second.begin(), order->second.end(), left._string); + auto posRight = std::find(order->second.begin(), order->second.end(), right._string); + return posLeft > posRight; + } else { + if (left.trivial() || right.trivial()) return false; + + return left._string > right._string; + } + } + + case type_date: + if (left.trivial() || right.trivial()) return false; + + left.cast(type_date); + return left._date > right._date; + + case type_duration: + if (left.trivial() || right.trivial()) return false; + + left.cast(type_duration); + return left._duration > right._duration; + } + break; + case type_date: + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + if (left.trivial() || right.trivial()) return false; + + right.cast(type_date); + return left._date > right._date; + } + break; + case type_duration: - if (left.trivial () || right.trivial ()) - return false; + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + if (left.trivial() || right.trivial()) return false; - right.cast (type_date); - return left._date > right._date; - } - break; - - case type_duration: - switch (right._type) - { - case type_boolean: - case type_integer: - case type_real: - case type_string: - case type_date: - case type_duration: - if (left.trivial () || right.trivial ()) - return false; - - right.cast (type_duration); - return left._duration > right._duration; - } - break; + right.cast(type_duration); + return left._duration > right._duration; + } + break; } return false; } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator>= (const Variant& other) const -{ - Variant left (*this); - Variant right (other); +bool Variant::operator>=(const Variant& other) const { + Variant left(*this); + Variant right(other); - if (left._type == type_string) - Lexer::dequote (left._string); + if (left._type == type_string) Lexer::dequote(left._string); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - switch (left._type) - { - case type_boolean: - switch (right._type) - { - case type_boolean: return left._bool || !right._bool; - case type_integer: left.cast (type_integer); return left._integer >= right._integer; - case type_real: left.cast (type_real); return left._real >= right._real; - case type_string: left.cast (type_string); return left._string >= right._string; - case type_date: left.cast (type_date); return left._date >= right._date; - case type_duration: left.cast (type_duration); return left._duration >= right._duration; - } - break; - - case type_integer: - switch (right._type) - { - case type_boolean: right.cast (type_integer); return left._integer >= right._integer; - case type_integer: return left._integer >= right._integer; - case type_real: left.cast (type_real); return left._real >= right._real; - case type_string: left.cast (type_string); return left._string >= right._string; - case type_date: left.cast (type_date); return left._date >= right._date; - case type_duration: left.cast (type_duration); return left._duration >= right._duration; - } - break; - - case type_real: - switch (right._type) - { - case type_boolean: right.cast (type_real); return left._real >= right._real; - case type_integer: right.cast (type_real); return left._real >= right._real; - case type_real: return left._real >= right._real; - case type_string: left.cast (type_string); return left._string >= right._string; - case type_date: left.cast (type_date); return left._date >= right._date; - case type_duration: left.cast (type_duration); return left._duration >= right._duration; - } - break; - - case type_string: - switch (right._type) - { + switch (left._type) { case type_boolean: - case type_integer: - case type_real: - right.cast (type_string); - return left._string >= right._string; - - case type_string: - { - if (left._string == right._string) - return true; - - auto order = Task::customOrder.find (left.source ()); - if (order != Task::customOrder.end ()) - { - // Guaranteed to be found, because of ColUDA::validate (). - auto posLeft = std::find (order->second.begin (), order->second.end (), left._string); - auto posRight = std::find (order->second.begin (), order->second.end (), right._string); - return posLeft >= posRight; - } - else - { - if (left.trivial () || right.trivial ()) - return false; - + switch (right._type) { + case type_boolean: + return left._bool || !right._bool; + case type_integer: + left.cast(type_integer); + return left._integer >= right._integer; + case type_real: + left.cast(type_real); + return left._real >= right._real; + case type_string: + left.cast(type_string); return left._string >= right._string; - } + case type_date: + left.cast(type_date); + return left._date >= right._date; + case type_duration: + left.cast(type_duration); + return left._duration >= right._duration; } + break; - case type_date: - if (left.trivial () || right.trivial ()) - return false; - - left.cast (type_date); - return left._date >= right._date; - - case type_duration: - if (left.trivial () || right.trivial ()) - return false; - - left.cast (type_duration); - return left._duration >= right._duration; - } - break; - - case type_date: - switch (right._type) - { - case type_boolean: case type_integer: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + return left._integer >= right._integer; + case type_integer: + return left._integer >= right._integer; + case type_real: + left.cast(type_real); + return left._real >= right._real; + case type_string: + left.cast(type_string); + return left._string >= right._string; + case type_date: + left.cast(type_date); + return left._date >= right._date; + case type_duration: + left.cast(type_duration); + return left._duration >= right._duration; + } + break; + case type_real: + switch (right._type) { + case type_boolean: + right.cast(type_real); + return left._real >= right._real; + case type_integer: + right.cast(type_real); + return left._real >= right._real; + case type_real: + return left._real >= right._real; + case type_string: + left.cast(type_string); + return left._string >= right._string; + case type_date: + left.cast(type_date); + return left._date >= right._date; + case type_duration: + left.cast(type_duration); + return left._duration >= right._duration; + } + break; + case type_string: + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + right.cast(type_string); + return left._string >= right._string; + + case type_string: { + if (left._string == right._string) return true; + + auto order = Task::customOrder.find(left.source()); + if (order != Task::customOrder.end()) { + // Guaranteed to be found, because of ColUDA::validate (). + auto posLeft = std::find(order->second.begin(), order->second.end(), left._string); + auto posRight = std::find(order->second.begin(), order->second.end(), right._string); + return posLeft >= posRight; + } else { + if (left.trivial() || right.trivial()) return false; + + return left._string >= right._string; + } + } + + case type_date: + if (left.trivial() || right.trivial()) return false; + + left.cast(type_date); + return left._date >= right._date; + + case type_duration: + if (left.trivial() || right.trivial()) return false; + + left.cast(type_duration); + return left._duration >= right._duration; + } + break; + case type_date: + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + if (left.trivial() || right.trivial()) return false; + + right.cast(type_date); + return left._date >= right._date; + } + break; + case type_duration: - if (left.trivial () || right.trivial ()) - return false; + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + if (left.trivial() || right.trivial()) return false; - right.cast (type_date); - return left._date >= right._date; - } - break; - - case type_duration: - switch (right._type) - { - case type_boolean: - case type_integer: - case type_real: - case type_string: - case type_date: - case type_duration: - if (left.trivial () || right.trivial ()) - return false; - - right.cast (type_duration); - return left._duration >= right._duration; - } - break; + right.cast(type_duration); + return left._duration >= right._duration; + } + break; } return false; } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator== (const Variant& other) const -{ - Variant left (*this); - Variant right (other); +bool Variant::operator==(const Variant& other) const { + Variant left(*this); + Variant right(other); - if (left._type == type_string) - Lexer::dequote (left._string); + if (left._type == type_string) Lexer::dequote(left._string); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - switch (left._type) - { - case type_boolean: - switch (right._type) - { - case type_boolean: return left._bool == right._bool; - case type_integer: left.cast (type_integer); return left._integer == right._integer; - case type_real: left.cast (type_real); return left._real == right._real; - case type_string: left.cast (type_string); return left._string == right._string; - case type_date: left.cast (type_date); return left._date == right._date; - case type_duration: left.cast (type_duration); return left._duration == right._duration; - } - break; - - case type_integer: - switch (right._type) - { - case type_boolean: right.cast (type_integer); return left._integer == right._integer; - case type_integer: return left._integer == right._integer; - case type_real: left.cast (type_real); return left._real == right._real; - case type_string: left.cast (type_string); return left._string == right._string; - case type_date: left.cast (type_date); return left._date == right._date; - case type_duration: left.cast (type_duration); return left._duration == right._duration; - } - break; - - case type_real: - switch (right._type) - { - case type_boolean: right.cast (type_real); return left._real == right._real; - case type_integer: right.cast (type_real); return left._real == right._real; - case type_real: return left._real == right._real; - case type_string: left.cast (type_string); return left._string == right._string; - case type_date: left.cast (type_date); return left._date == right._date; - case type_duration: left.cast (type_duration); return left._duration == right._duration; - } - break; - - case type_string: - switch (right._type) - { + switch (left._type) { case type_boolean: + switch (right._type) { + case type_boolean: + return left._bool == right._bool; + case type_integer: + left.cast(type_integer); + return left._integer == right._integer; + case type_real: + left.cast(type_real); + return left._real == right._real; + case type_string: + left.cast(type_string); + return left._string == right._string; + case type_date: + left.cast(type_date); + return left._date == right._date; + case type_duration: + left.cast(type_duration); + return left._duration == right._duration; + } + break; + case type_integer: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + return left._integer == right._integer; + case type_integer: + return left._integer == right._integer; + case type_real: + left.cast(type_real); + return left._real == right._real; + case type_string: + left.cast(type_string); + return left._string == right._string; + case type_date: + left.cast(type_date); + return left._date == right._date; + case type_duration: + left.cast(type_duration); + return left._duration == right._duration; + } + break; + case type_real: + switch (right._type) { + case type_boolean: + right.cast(type_real); + return left._real == right._real; + case type_integer: + right.cast(type_real); + return left._real == right._real; + case type_real: + return left._real == right._real; + case type_string: + left.cast(type_string); + return left._string == right._string; + case type_date: + left.cast(type_date); + return left._date == right._date; + case type_duration: + left.cast(type_duration); + return left._duration == right._duration; + } + break; + case type_string: - right.cast (type_string); + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + right.cast(type_string); - // Status is always compared caseless. - if (left.source () == "status") - return compare (left._string, right._string, false); + // Status is always compared caseless. + if (left.source() == "status") return compare(left._string, right._string, false); - return left._string == right._string; + return left._string == right._string; + + case type_date: + if (left.trivial() || right.trivial()) return false; + + left.cast(type_date); + return left._date == right._date; + + case type_duration: + left.cast(type_duration); + return left._duration == right._duration; + } + break; case type_date: - if (left.trivial () || right.trivial ()) - return false; + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + if (left.trivial() || right.trivial()) return false; - left.cast (type_date); - return left._date == right._date; + right.cast(type_date); + return left._date == right._date; + } + break; case type_duration: - left.cast (type_duration); - return left._duration == right._duration; - } - break; - - case type_date: - switch (right._type) - { - case type_boolean: - case type_integer: - case type_real: - case type_string: - case type_date: - case type_duration: - if (left.trivial () || right.trivial ()) - return false; - - right.cast (type_date); - return left._date == right._date; - } - break; - - case type_duration: - switch (right._type) - { - case type_boolean: - case type_integer: - case type_real: - case type_string: - case type_date: - case type_duration: - right.cast (type_duration); - return left._duration == right._duration; - } - break; + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + right.cast(type_duration); + return left._duration == right._duration; + } + break; } return false; } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator!= (const Variant& other) const -{ - return !(*this == other); -} +bool Variant::operator!=(const Variant& other) const { return !(*this == other); } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator_match (const Variant& other, const Task& task) const -{ +bool Variant::operator_match(const Variant& other, const Task& task) const { // Simple matching case first. - Variant left (*this); - Variant right (other); + Variant left(*this); + Variant right(other); - if (left._type == type_string) - Lexer::dequote (left._string); + if (left._type == type_string) Lexer::dequote(left._string); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - left.cast (type_string); - right.cast (type_string); + left.cast(type_string); + right.cast(type_string); std::string pattern = right._string; - Lexer::dequote (pattern); + Lexer::dequote(pattern); - if (searchUsingRegex) - { - RX r (pattern, searchCaseSensitive); - if (r.match (left._string)) - return true; + if (searchUsingRegex) { + RX r(pattern, searchCaseSensitive); + if (r.match(left._string)) return true; // If the above did not match, and the left source is "description", then // in the annotations. - if (left.source () == "description") - { - for (auto& a : task.getAnnotations ()) - if (r.match (a.second)) - return true; + if (left.source() == "description") { + for (auto& a : task.getAnnotations()) + if (r.match(a.second)) return true; } - } - else - { + } else { // If pattern starts with '^', look for a leftmost compare only. - if (pattern[0] == '^' && - find (left._string, - pattern.substr (1), - searchCaseSensitive) == 0) - { + if (pattern[0] == '^' && find(left._string, pattern.substr(1), searchCaseSensitive) == 0) { return true; } // If pattern ends with '$', look for a rightmost compare only. - else if (pattern[pattern.length () - 1] == '$' && - find (left._string, - pattern.substr (0, pattern.length () - 1), - searchCaseSensitive) == (left._string.length () - pattern.length () + 1)) - { + else if (pattern[pattern.length() - 1] == '$' && + find(left._string, pattern.substr(0, pattern.length() - 1), searchCaseSensitive) == + (left._string.length() - pattern.length() + 1)) { return true; } - else if (find (left._string, pattern, searchCaseSensitive) != std::string::npos) + else if (find(left._string, pattern, searchCaseSensitive) != std::string::npos) return true; // If the above did not match, and the left source is "description", then // in the annotations. - if (left.source () == "description") - { - for (auto& a : task.getAnnotations ()) - if (find (a.second, pattern, searchCaseSensitive) != std::string::npos) - return true; + if (left.source() == "description") { + for (auto& a : task.getAnnotations()) + if (find(a.second, pattern, searchCaseSensitive) != std::string::npos) return true; } } @@ -955,9 +972,8 @@ bool Variant::operator_match (const Variant& other, const Task& task) const } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator_nomatch (const Variant& other, const Task& task) const -{ - return ! operator_match (other, task); +bool Variant::operator_nomatch(const Variant& other, const Task& task) const { + return !operator_match(other, task); } //////////////////////////////////////////////////////////////////////////////// @@ -966,178 +982,173 @@ bool Variant::operator_nomatch (const Variant& other, const Task& task) const // date date --> same day check // string string --> leftmost // -bool Variant::operator_partial (const Variant& other) const -{ - Variant left (*this); - Variant right (other); +bool Variant::operator_partial(const Variant& other) const { + Variant left(*this); + Variant right(other); - if (left._type == type_string) - Lexer::dequote (left._string); + if (left._type == type_string) Lexer::dequote(left._string); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - switch (left._type) - { - case type_boolean: - switch (right._type) - { - case type_boolean: return left._bool == right._bool; - case type_integer: left.cast (type_integer); return left._integer == right._integer; - case type_real: left.cast (type_real); return left._real == right._real; - case type_string: left.cast (type_string); return left._string == right._string; - - // Same-day comparison. - case type_date: - { - left.cast (type_date); - Datetime left_date (left._date); - Datetime right_date (right._date); - return left_date.sameDay (right_date); - } - - case type_duration: left.cast (type_duration); return left._duration == right._duration; - } - break; - - case type_integer: - switch (right._type) - { - case type_boolean: right.cast (type_integer); return left._integer == right._integer; - case type_integer: return left._integer == right._integer; - case type_real: left.cast (type_real); return left._real == right._real; - case type_string: left.cast (type_string); return left._string == right._string; - - // Same-day comparison. - case type_date: - { - left.cast (type_date); - Datetime left_date (left._date); - Datetime right_date (right._date); - return left_date.sameDay (right_date); - } - - case type_duration: left.cast (type_duration); return left._duration == right._duration; - } - break; - - case type_real: - switch (right._type) - { + switch (left._type) { case type_boolean: + switch (right._type) { + case type_boolean: + return left._bool == right._bool; + case type_integer: + left.cast(type_integer); + return left._integer == right._integer; + case type_real: + left.cast(type_real); + return left._real == right._real; + case type_string: + left.cast(type_string); + return left._string == right._string; + + // Same-day comparison. + case type_date: { + left.cast(type_date); + Datetime left_date(left._date); + Datetime right_date(right._date); + return left_date.sameDay(right_date); + } + + case type_duration: + left.cast(type_duration); + return left._duration == right._duration; + } + break; + case type_integer: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + return left._integer == right._integer; + case type_integer: + return left._integer == right._integer; + case type_real: + left.cast(type_real); + return left._real == right._real; + case type_string: + left.cast(type_string); + return left._string == right._string; + + // Same-day comparison. + case type_date: { + left.cast(type_date); + Datetime left_date(left._date); + Datetime right_date(right._date); + return left_date.sameDay(right_date); + } + + case type_duration: + left.cast(type_duration); + return left._duration == right._duration; + } + break; + case type_real: - right.cast (type_real); - return left._real == right._real; + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + right.cast(type_real); + return left._real == right._real; + + case type_string: + left.cast(type_string); + return left._string == right._string; + + // Same-day comparison. + case type_date: { + left.cast(type_date); + Datetime left_date(left._date); + Datetime right_date(right._date); + return left_date.sameDay(right_date); + } + + case type_duration: + left.cast(type_duration); + return left._duration == right._duration; + } + break; case type_string: - left.cast (type_string); - return left._string == right._string; + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + case type_string: { + right.cast(type_string); - // Same-day comparison. - case type_date: - { - left.cast (type_date); - Datetime left_date (left._date); - Datetime right_date (right._date); - return left_date.sameDay (right_date); + // Status is always compared caseless. + if (left.source() == "status") return compare(left._string, right._string, false); + + int left_len = left._string.length(); + int right_len = right._string.length(); + + if ((left_len == 0 && right_len != 0) || (left_len != 0 && right_len == 0)) return false; + + // Dodgy. + if (left._string.length() < right._string.length()) return false; + + return left._string.substr(0, right._string.length()) == right._string; + } + + // Same-day comparison. + case type_date: { + if (left.trivial() || right.trivial()) return false; + + left.cast(type_date); + Datetime left_date(left._date); + Datetime right_date(right._date); + return left_date.sameDay(right_date); + } + + case type_duration: + left.cast(type_duration); + return left._duration == right._duration; } + break; + + case type_date: + switch (right._type) { + // Same-day comparison. + case type_string: { + if (left.trivial() || right.trivial()) return false; + + right.cast(type_date); + Datetime left_date(left._date); + Datetime right_date(right._date); + return left_date.sameDay(right_date); + } + + case type_boolean: + case type_integer: + case type_real: + case type_date: + case type_duration: { + right.cast(type_date); + Datetime left_date(left._date); + Datetime right_date(right._date); + return left_date.sameDay(right_date); + } + } + break; case type_duration: - left.cast (type_duration); - return left._duration == right._duration; - } - break; - - case type_string: - switch (right._type) - { - case type_boolean: - case type_integer: - case type_real: - case type_string: - { - right.cast (type_string); - - // Status is always compared caseless. - if (left.source () == "status") - return compare (left._string, right._string, false); - - int left_len = left._string.length (); - int right_len = right._string.length (); - - if ((left_len == 0 && right_len != 0) || - (left_len != 0 && right_len == 0)) - return false; - - // Dodgy. - if (left._string.length () < right._string.length ()) - return false; - - return left._string.substr (0, right._string.length ()) == right._string; + switch (right._type) { + // Same-day comparison. + case type_boolean: + case type_integer: + case type_real: + case type_string: + case type_date: + case type_duration: + right.cast(type_duration); + return left._duration == right._duration; } - - // Same-day comparison. - case type_date: - { - if (left.trivial () || right.trivial ()) - return false; - - left.cast (type_date); - Datetime left_date (left._date); - Datetime right_date (right._date); - return left_date.sameDay (right_date); - } - - case type_duration: - left.cast (type_duration); - return left._duration == right._duration; - } - break; - - case type_date: - switch (right._type) - { - // Same-day comparison. - case type_string: - { - if (left.trivial () || right.trivial ()) - return false; - - right.cast (type_date); - Datetime left_date (left._date); - Datetime right_date (right._date); - return left_date.sameDay (right_date); - } - - case type_boolean: - case type_integer: - case type_real: - case type_date: - case type_duration: - { - right.cast (type_date); - Datetime left_date (left._date); - Datetime right_date (right._date); - return left_date.sameDay (right_date); - } - } - break; - - case type_duration: - switch (right._type) - { - // Same-day comparison. - case type_boolean: - case type_integer: - case type_real: - case type_string: - case type_date: - case type_duration: - right.cast (type_duration); - return left._duration == right._duration; - } - break; + break; } return false; @@ -1145,860 +1156,947 @@ bool Variant::operator_partial (const Variant& other) const //////////////////////////////////////////////////////////////////////////////// // Inverse of operator_partial. -bool Variant::operator_nopartial (const Variant& other) const -{ - return ! operator_partial (other); +bool Variant::operator_nopartial(const Variant& other) const { return !operator_partial(other); } + +//////////////////////////////////////////////////////////////////////////////// +bool Variant::operator_hastag(const Variant& other, const Task& task) const { + Variant right(other); + right.cast(type_string); + Lexer::dequote(right._string); + return task.hasTag(right._string); } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator_hastag (const Variant& other, const Task& task) const -{ - Variant right (other); - right.cast (type_string); - Lexer::dequote (right._string); - return task.hasTag (right._string); +bool Variant::operator_notag(const Variant& other, const Task& task) const { + return !operator_hastag(other, task); } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator_notag (const Variant& other, const Task& task) const -{ - return ! operator_hastag (other, task); +bool Variant::operator!() const { + Variant left(*this); + + if (left._type == type_string) Lexer::dequote(left._string); + + left.cast(type_boolean); + return !left._bool; } //////////////////////////////////////////////////////////////////////////////// -bool Variant::operator! () const -{ - Variant left (*this); +Variant& Variant::operator^=(const Variant& other) { + switch (_type) { + case type_boolean: + throw std::string(STRING_VARIANT_EXP_BOOL); + break; - if (left._type == type_string) - Lexer::dequote (left._string); + case type_integer: + switch (other._type) { + case type_boolean: + throw std::string(STRING_VARIANT_EXP_BOOL); + case type_integer: + _integer = (int)pow(static_cast(_integer), static_cast(other._integer)); + break; + case type_real: + throw std::string(STRING_VARIANT_EXP_NON_INT); + case type_string: + throw std::string(STRING_VARIANT_EXP_STRING); + case type_date: + throw std::string(STRING_VARIANT_EXP_DATE); + case type_duration: + throw std::string(STRING_VARIANT_EXP_DURATION); + } + break; - left.cast (type_boolean); - return ! left._bool; -} + case type_real: + switch (other._type) { + case type_boolean: + throw std::string(STRING_VARIANT_EXP_BOOL); + case type_integer: + _real = pow(_real, static_cast(other._integer)); + break; + case type_real: + throw std::string(STRING_VARIANT_EXP_NON_INT); + case type_string: + throw std::string(STRING_VARIANT_EXP_STRING); + case type_date: + throw std::string(STRING_VARIANT_EXP_DATE); + case type_duration: + throw std::string(STRING_VARIANT_EXP_DURATION); + } + break; -//////////////////////////////////////////////////////////////////////////////// -Variant& Variant::operator^= (const Variant& other) -{ - switch (_type) - { - case type_boolean: - throw std::string (STRING_VARIANT_EXP_BOOL); - break; + case type_string: + throw std::string(STRING_VARIANT_EXP_STRING); + break; - case type_integer: - switch (other._type) - { - case type_boolean: throw std::string (STRING_VARIANT_EXP_BOOL); - case type_integer: _integer = (int) pow (static_cast(_integer), static_cast(other._integer)); break; - case type_real: throw std::string (STRING_VARIANT_EXP_NON_INT); - case type_string: throw std::string (STRING_VARIANT_EXP_STRING); - case type_date: throw std::string (STRING_VARIANT_EXP_DATE); - case type_duration: throw std::string (STRING_VARIANT_EXP_DURATION); - } - break; + case type_date: + throw std::string(STRING_VARIANT_EXP_DATE); + break; - case type_real: - switch (other._type) - { - case type_boolean: throw std::string (STRING_VARIANT_EXP_BOOL); - case type_integer: _real = pow (_real, static_cast(other._integer)); break; - case type_real: throw std::string (STRING_VARIANT_EXP_NON_INT); - case type_string: throw std::string (STRING_VARIANT_EXP_STRING); - case type_date: throw std::string (STRING_VARIANT_EXP_DATE); - case type_duration: throw std::string (STRING_VARIANT_EXP_DURATION); - } - break; - - case type_string: - throw std::string (STRING_VARIANT_EXP_STRING); - break; - - case type_date: - throw std::string (STRING_VARIANT_EXP_DATE); - break; - - case type_duration: - throw std::string (STRING_VARIANT_EXP_DURATION); - break; + case type_duration: + throw std::string(STRING_VARIANT_EXP_DURATION); + break; } return *this; } //////////////////////////////////////////////////////////////////////////////// -Variant Variant::operator^ (const Variant& other) const -{ - Variant left (*this); +Variant Variant::operator^(const Variant& other) const { + Variant left(*this); left ^= other; return left; } //////////////////////////////////////////////////////////////////////////////// -Variant& Variant::operator-= (const Variant& other) -{ - Variant right (other); - - switch (_type) - { - case type_boolean: - throw std::string (STRING_VARIANT_SUB_BOOL); - break; - - case type_integer: - switch (right._type) - { - case type_boolean: right.cast (type_integer); _integer -= right._integer; break; - case type_integer: _integer -= right._integer; break; - case type_real: cast (type_real); _real -= right._real; break; - case type_string: throw std::string (STRING_VARIANT_SUB_STRING); - case type_date: cast (type_date); _date -= right._date; break; - case type_duration: cast (type_duration); _duration -= right._duration; break; - } - break; - - case type_real: - switch (right._type) - { - case type_string: - throw std::string (STRING_VARIANT_SUB_STRING); +Variant& Variant::operator-=(const Variant& other) { + Variant right(other); + switch (_type) { case type_boolean: - case type_integer: - case type_real: - case type_date: - case type_duration: - right.cast (type_real); - _real -= right._real; + throw std::string(STRING_VARIANT_SUB_BOOL); + break; + + case type_integer: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + _integer -= right._integer; + break; + case type_integer: + _integer -= right._integer; + break; + case type_real: + cast(type_real); + _real -= right._real; + break; + case type_string: + throw std::string(STRING_VARIANT_SUB_STRING); + case type_date: + cast(type_date); + _date -= right._date; + break; + case type_duration: + cast(type_duration); + _duration -= right._duration; + break; + } + break; + + case type_real: + switch (right._type) { + case type_string: + throw std::string(STRING_VARIANT_SUB_STRING); + + case type_boolean: + case type_integer: + case type_real: + case type_date: + case type_duration: + right.cast(type_real); + _real -= right._real; + break; + } break; - } - break; - case type_string: - switch (right._type) - { case type_string: - cast (type_string); _string += '-' + right._string; break; - case type_boolean: - case type_integer: - case type_real: - case type_date: - case type_duration: - throw std::string (STRING_VARIANT_SUB_STRING); + switch (right._type) { + case type_string: + cast(type_string); + _string += '-' + right._string; + break; + case type_boolean: + case type_integer: + case type_real: + case type_date: + case type_duration: + throw std::string(STRING_VARIANT_SUB_STRING); + break; + } break; - } - break; - case type_date: - switch (right._type) - { - case type_boolean: right.cast (type_integer); _date -= right._integer; break; - case type_integer: _date -= right._integer; break; - case type_real: _date -= (int) right._real; break; - case type_string: throw std::string (STRING_VARIANT_SUB_STRING); - case type_date: _type = Variant::type_duration; _duration = _date - right._date; break; - case type_duration: _date -= right._duration; break; - } - break; + case type_date: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + _date -= right._integer; + break; + case type_integer: + _date -= right._integer; + break; + case type_real: + _date -= (int)right._real; + break; + case type_string: + throw std::string(STRING_VARIANT_SUB_STRING); + case type_date: + _type = Variant::type_duration; + _duration = _date - right._date; + break; + case type_duration: + _date -= right._duration; + break; + } + break; - case type_duration: - switch (right._type) - { - case type_boolean: right.cast (type_integer); _duration -= right._integer; break; - case type_integer: _duration -= right._integer; break; - case type_real: _duration -= (int) right._real; break; - case type_string: throw std::string (STRING_VARIANT_SUB_STRING); - case type_date: throw std::string (STRING_VARIANT_SUB_DATE); - case type_duration: _duration -= right._duration; break; - } - break; + case type_duration: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + _duration -= right._integer; + break; + case type_integer: + _duration -= right._integer; + break; + case type_real: + _duration -= (int)right._real; + break; + case type_string: + throw std::string(STRING_VARIANT_SUB_STRING); + case type_date: + throw std::string(STRING_VARIANT_SUB_DATE); + case type_duration: + _duration -= right._duration; + break; + } + break; } return *this; } //////////////////////////////////////////////////////////////////////////////// -Variant Variant::operator- (const Variant& other) const -{ - Variant left (*this); +Variant Variant::operator-(const Variant& other) const { + Variant left(*this); left -= other; return left; } //////////////////////////////////////////////////////////////////////////////// -Variant& Variant::operator+= (const Variant& other) -{ - Variant right (other); +Variant& Variant::operator+=(const Variant& other) { + Variant right(other); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - switch (_type) - { - case type_boolean: - switch (right._type) - { - case type_boolean: throw std::string (STRING_VARIANT_ADD_BOOL); - case type_integer: cast (type_integer); _integer += right._integer; break; - case type_real: cast (type_real); _real += right._real; break; - case type_string: cast (type_string); _string += right._string; break; - case type_date: cast (type_date); _date += right._date; break; - case type_duration: cast (type_duration); _duration += right._duration; break; - } - break; - - case type_integer: - switch (right._type) - { - case type_boolean: right.cast (type_integer); _integer += right._integer; break; - case type_integer: _integer += right._integer; break; - case type_real: cast (type_real); _real += right._real; break; - case type_string: cast (type_string); _string += right._string; break; - case type_date: cast (type_date); _date += right._date; break; - case type_duration: cast (type_duration); _duration += right._duration; break; - } - break; - - case type_real: - switch (right._type) - { + switch (_type) { case type_boolean: + switch (right._type) { + case type_boolean: + throw std::string(STRING_VARIANT_ADD_BOOL); + case type_integer: + cast(type_integer); + _integer += right._integer; + break; + case type_real: + cast(type_real); + _real += right._real; + break; + case type_string: + cast(type_string); + _string += right._string; + break; + case type_date: + cast(type_date); + _date += right._date; + break; + case type_duration: + cast(type_duration); + _duration += right._duration; + break; + } + break; + case type_integer: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + _integer += right._integer; + break; + case type_integer: + _integer += right._integer; + break; + case type_real: + cast(type_real); + _real += right._real; + break; + case type_string: + cast(type_string); + _string += right._string; + break; + case type_date: + cast(type_date); + _date += right._date; + break; + case type_duration: + cast(type_duration); + _duration += right._duration; + break; + } + break; + case type_real: - right.cast (type_real); - _real += right._real; + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + right.cast(type_real); + _real += right._real; + break; + + case type_string: + cast(type_string); + _string += right._string; + break; + + case type_date: + _type = type_date; + _date = (unsigned)(int)_real + right._date; + break; + + case type_duration: + _type = type_duration; + _duration = (unsigned)(int)_real + right._duration; + break; + } break; case type_string: - cast (type_string); - _string += right._string; + _string += (std::string)right; break; case type_date: - _type = type_date; - _date = (unsigned) (int) _real + right._date; + switch (right._type) { + case type_boolean: + right.cast(type_date); + _date += right._date; + break; + case type_integer: + _date += right._integer; + break; + case type_real: + _date += (int)right._real; + break; + case type_string: + cast(type_string); + _string += right._string; + break; + case type_date: + throw std::string(STRING_VARIANT_ADD_DATE); + case type_duration: + _date += right._duration; + break; + } break; case type_duration: - _type = type_duration; - _duration = (unsigned) (int) _real + right._duration; + switch (right._type) { + case type_boolean: + right.cast(type_duration); + _duration += right._duration; + break; + case type_integer: + _duration += right._integer; + break; + case type_real: + _duration += (int)right._real; + break; + case type_string: + cast(type_string); + _string += right._string; + break; + case type_date: + _type = Variant::type_date; + _date += right._date + _duration; + break; + case type_duration: + _duration += right._duration; + break; + } break; - } - break; - - case type_string: - _string += (std::string) right; - break; - - case type_date: - switch (right._type) - { - case type_boolean: right.cast (type_date); _date += right._date; break; - case type_integer: _date += right._integer; break; - case type_real: _date += (int) right._real; break; - case type_string: cast (type_string); _string += right._string; break; - case type_date: throw std::string (STRING_VARIANT_ADD_DATE); - case type_duration: _date += right._duration; break; - } - break; - - case type_duration: - switch (right._type) - { - case type_boolean: right.cast (type_duration); _duration += right._duration; break; - case type_integer: _duration += right._integer; break; - case type_real: _duration += (int) right._real; break; - case type_string: cast (type_string); _string += right._string; break; - case type_date: _type = Variant::type_date; _date += right._date + _duration; break; - case type_duration: _duration += right._duration; break; - } - break; } return *this; } //////////////////////////////////////////////////////////////////////////////// -Variant Variant::operator+ (const Variant& other) const -{ - Variant left (*this); +Variant Variant::operator+(const Variant& other) const { + Variant left(*this); left += other; return left; } //////////////////////////////////////////////////////////////////////////////// -Variant& Variant::operator*= (const Variant& other) -{ - Variant right (other); +Variant& Variant::operator*=(const Variant& other) { + Variant right(other); - if (right._type == type_string) - Lexer::dequote (right._string); + if (right._type == type_string) Lexer::dequote(right._string); - switch (_type) - { - case type_boolean: - switch (right._type) - { - case type_boolean: throw std::string (STRING_VARIANT_MUL_BOOL); - case type_integer: cast (type_integer); _integer *= right._integer; break; - case type_real: cast (type_real); _real *= right._real; break; - case type_string: _string = (_bool ? right._string : ""); _type = type_string; break; - case type_date: throw std::string (STRING_VARIANT_MUL_DATE); - case type_duration: cast (type_duration); _duration *= right._duration; break; - } - break; - - case type_integer: - switch (right._type) - { - case type_boolean: right.cast (type_integer); _integer *= right._integer; break; - case type_integer: _integer *= right._integer; break; - case type_real: cast (type_real); _real *= right._real; break; - case type_string: - { - int limit = _integer; - // assert (limit < 128); - _type = type_string; - _string = ""; - while (limit--) - _string += right._string; + switch (_type) { + case type_boolean: + switch (right._type) { + case type_boolean: + throw std::string(STRING_VARIANT_MUL_BOOL); + case type_integer: + cast(type_integer); + _integer *= right._integer; + break; + case type_real: + cast(type_real); + _real *= right._real; + break; + case type_string: + _string = (_bool ? right._string : ""); + _type = type_string; + break; + case type_date: + throw std::string(STRING_VARIANT_MUL_DATE); + case type_duration: + cast(type_duration); + _duration *= right._duration; + break; } break; - case type_date: throw std::string (STRING_VARIANT_MUL_DATE); - case type_duration: cast (type_duration); _duration *= right._duration; break; - } - break; - case type_real: - switch (right._type) - { - case type_boolean: case type_integer: + switch (right._type) { + case type_boolean: + right.cast(type_integer); + _integer *= right._integer; + break; + case type_integer: + _integer *= right._integer; + break; + case type_real: + cast(type_real); + _real *= right._real; + break; + case type_string: { + int limit = _integer; + // assert (limit < 128); + _type = type_string; + _string = ""; + while (limit--) _string += right._string; + } break; + case type_date: + throw std::string(STRING_VARIANT_MUL_DATE); + case type_duration: + cast(type_duration); + _duration *= right._duration; + break; + } + break; + case type_real: - right.cast (type_real); - _real *= right._real; + switch (right._type) { + case type_boolean: + case type_integer: + case type_real: + right.cast(type_real); + _real *= right._real; + break; + + case type_string: + throw std::string(STRING_VARIANT_MUL_REAL_STR); + + case type_date: + throw std::string(STRING_VARIANT_MUL_DATE); + + case type_duration: + _type = type_duration; + _duration = (time_t)(unsigned)(int)(_real * static_cast(right._duration)); + } break; case type_string: - throw std::string (STRING_VARIANT_MUL_REAL_STR); + switch (right._type) { + case type_boolean: + if (!right._bool) _string = ""; + break; + case type_integer: { + int limit = right._integer - 1; + // assert (limit < 128); + std::string fragment = _string; + while (limit--) _string += fragment; + } break; + case type_real: + throw std::string(STRING_VARIANT_MUL_STR_REAL); + case type_string: + throw std::string(STRING_VARIANT_MUL_STR_STR); + case type_date: + throw std::string(STRING_VARIANT_MUL_STR_DATE); + case type_duration: + throw std::string(STRING_VARIANT_MUL_STR_DUR); + } + break; case type_date: - throw std::string (STRING_VARIANT_MUL_DATE); + throw std::string(STRING_VARIANT_MUL_DATE); case type_duration: - _type = type_duration; - _duration = (time_t) (unsigned) (int) (_real * static_cast(right._duration)); - } - break; - - case type_string: - switch (right._type) - { - case type_boolean: if (! right._bool) _string = ""; break; - case type_integer: - { - int limit = right._integer - 1; - // assert (limit < 128); - std::string fragment = _string; - while (limit--) - _string += fragment; + switch (right._type) { + case type_boolean: + right.cast(type_duration); + _duration *= right._duration; + break; + case type_integer: + _duration *= right._integer; + break; + case type_real: + _duration = (time_t)(unsigned)(int)(static_cast(_duration) * right._real); + break; + case type_string: + throw std::string(STRING_VARIANT_MUL_DUR_STR); + case type_date: + throw std::string(STRING_VARIANT_MUL_DUR_DATE); + case type_duration: + throw std::string(STRING_VARIANT_MUL_DUR_DUR); } break; - case type_real: throw std::string (STRING_VARIANT_MUL_STR_REAL); - case type_string: throw std::string (STRING_VARIANT_MUL_STR_STR); - case type_date: throw std::string (STRING_VARIANT_MUL_STR_DATE); - case type_duration: throw std::string (STRING_VARIANT_MUL_STR_DUR); - } - break; - - case type_date: - throw std::string (STRING_VARIANT_MUL_DATE); - - case type_duration: - switch (right._type) - { - case type_boolean: right.cast (type_duration); _duration *= right._duration; break; - case type_integer: _duration *= right._integer; break; - case type_real: - _duration = (time_t) (unsigned) (int) (static_cast(_duration) * right._real); - break; - case type_string: throw std::string (STRING_VARIANT_MUL_DUR_STR); - case type_date: throw std::string (STRING_VARIANT_MUL_DUR_DATE); - case type_duration: throw std::string (STRING_VARIANT_MUL_DUR_DUR); - } - break; } return *this; } //////////////////////////////////////////////////////////////////////////////// -Variant Variant::operator* (const Variant& other) const -{ - Variant left (*this); +Variant Variant::operator*(const Variant& other) const { + Variant left(*this); left *= other; return left; } //////////////////////////////////////////////////////////////////////////////// -Variant& Variant::operator/= (const Variant& other) -{ - Variant right (other); +Variant& Variant::operator/=(const Variant& other) { + Variant right(other); - switch (_type) - { - case type_boolean: - throw std::string (STRING_VARIANT_DIV_BOOL); - break; - - case type_integer: - switch (right._type) - { + switch (_type) { case type_boolean: - throw std::string (STRING_VARIANT_DIV_INT_BOOL); + throw std::string(STRING_VARIANT_DIV_BOOL); + break; case type_integer: - if (right._integer == 0) - throw std::string (STRING_VARIANT_DIV_ZERO); - _integer /= right._integer; + switch (right._type) { + case type_boolean: + throw std::string(STRING_VARIANT_DIV_INT_BOOL); + + case type_integer: + if (right._integer == 0) throw std::string(STRING_VARIANT_DIV_ZERO); + _integer /= right._integer; + break; + + case type_real: + if (right._real == 0.0) throw std::string(STRING_VARIANT_DIV_ZERO); + cast(type_real); + _real /= right._real; + break; + + case type_string: + throw std::string(STRING_VARIANT_DIV_INT_STR); + + case type_date: + throw std::string(STRING_VARIANT_DIV_INT_DATE); + + case type_duration: + if (right._duration == 0) throw std::string(STRING_VARIANT_DIV_ZERO); + _type = type_duration; + _duration = (time_t)(unsigned)(int)(_integer / right._duration); + break; + } break; case type_real: - if (right._real == 0.0) - throw std::string (STRING_VARIANT_DIV_ZERO); - cast (type_real); - _real /= right._real; + switch (right._type) { + case type_boolean: + throw std::string(STRING_VARIANT_DIV_REAL_BOOL); + + case type_integer: + if (right._integer == 0) throw std::string(STRING_VARIANT_DIV_ZERO); + _real /= static_cast(right._integer); + break; + + case type_real: + if (right._real == 0) throw std::string(STRING_VARIANT_DIV_ZERO); + _real /= right._real; + break; + + case type_string: + throw std::string(STRING_VARIANT_DIV_REAL_STR); + + case type_date: + throw std::string(STRING_VARIANT_DIV_REAL_DATE); + + case type_duration: + if (right._duration == 0) throw std::string(STRING_VARIANT_DIV_ZERO); + _type = type_duration; + _duration = (time_t)(unsigned)(int)(_real / right._duration); + break; + } break; case type_string: - throw std::string (STRING_VARIANT_DIV_INT_STR); + throw std::string(STRING_VARIANT_DIV_REAL_STR); + break; case type_date: - throw std::string (STRING_VARIANT_DIV_INT_DATE); + throw std::string(STRING_VARIANT_DIV_REAL_DATE); case type_duration: - if (right._duration == 0) - throw std::string (STRING_VARIANT_DIV_ZERO); - _type = type_duration; - _duration = (time_t) (unsigned) (int) (_integer / right._duration); + switch (right._type) { + case type_boolean: + throw std::string(STRING_VARIANT_DIV_DUR_BOOL); + + case type_integer: + if (right._integer == 0) throw std::string(STRING_VARIANT_DIV_ZERO); + _duration /= right._integer; + break; + + case type_real: + if (right._real == 0) throw std::string(STRING_VARIANT_DIV_ZERO); + _duration = (time_t)(unsigned)(int)(static_cast(_duration) / right._real); + break; + + case type_string: + throw std::string(STRING_VARIANT_DIV_DUR_STR); + + case type_date: + throw std::string(STRING_VARIANT_DIV_DUR_DATE); + + case type_duration: + throw std::string(STRING_VARIANT_DIV_DUR_DUR); + } break; - } - break; - - case type_real: - switch (right._type) - { - case type_boolean: - throw std::string (STRING_VARIANT_DIV_REAL_BOOL); - - case type_integer: - if (right._integer == 0) - throw std::string (STRING_VARIANT_DIV_ZERO); - _real /= static_cast(right._integer); - break; - - case type_real: - if (right._real == 0) - throw std::string (STRING_VARIANT_DIV_ZERO); - _real /= right._real; - break; - - case type_string: - throw std::string (STRING_VARIANT_DIV_REAL_STR); - - case type_date: - throw std::string (STRING_VARIANT_DIV_REAL_DATE); - - case type_duration: - if (right._duration == 0) - throw std::string (STRING_VARIANT_DIV_ZERO); - _type = type_duration; - _duration = (time_t) (unsigned) (int) (_real / right._duration); - break; - } - break; - - case type_string: - throw std::string (STRING_VARIANT_DIV_REAL_STR); - break; - - case type_date: - throw std::string (STRING_VARIANT_DIV_REAL_DATE); - - case type_duration: - switch (right._type) - { - case type_boolean: - throw std::string (STRING_VARIANT_DIV_DUR_BOOL); - - case type_integer: - if (right._integer == 0) - throw std::string (STRING_VARIANT_DIV_ZERO); - _duration /= right._integer; - break; - - case type_real: - if (right._real == 0) - throw std::string (STRING_VARIANT_DIV_ZERO); - _duration = (time_t) (unsigned) (int) (static_cast(_duration) / right._real); - break; - - case type_string: - throw std::string (STRING_VARIANT_DIV_DUR_STR); - - case type_date: - throw std::string (STRING_VARIANT_DIV_DUR_DATE); - - case type_duration: - throw std::string (STRING_VARIANT_DIV_DUR_DUR); - } - break; } return *this; } //////////////////////////////////////////////////////////////////////////////// -Variant Variant::operator/ (const Variant& other) const -{ - Variant left (*this); +Variant Variant::operator/(const Variant& other) const { + Variant left(*this); left /= other; return left; } //////////////////////////////////////////////////////////////////////////////// -Variant& Variant::operator%= (const Variant& other) -{ - Variant right (other); +Variant& Variant::operator%=(const Variant& other) { + Variant right(other); - switch (_type) - { - case type_boolean: - throw std::string (STRING_VARIANT_MOD_BOOL); - break; - - case type_integer: - switch (right._type) - { + switch (_type) { case type_boolean: - throw std::string (STRING_VARIANT_MOD_INT_BOOL); + throw std::string(STRING_VARIANT_MOD_BOOL); + break; case type_integer: - if (right._integer == 0) - throw std::string (STRING_VARIANT_MOD_ZERO); - _integer %= right._integer; + switch (right._type) { + case type_boolean: + throw std::string(STRING_VARIANT_MOD_INT_BOOL); + + case type_integer: + if (right._integer == 0) throw std::string(STRING_VARIANT_MOD_ZERO); + _integer %= right._integer; + break; + + case type_real: + if (right._real == 0.0) throw std::string(STRING_VARIANT_MOD_ZERO); + cast(type_real); + _real = fmod(_real, right._real); + break; + + case type_string: + throw std::string(STRING_VARIANT_MOD_INT_STR); + + case type_date: + throw std::string(STRING_VARIANT_MOD_INT_DATE); + + case type_duration: + throw std::string(STRING_VARIANT_MOD_INT_DUR); + } break; case type_real: - if (right._real == 0.0) - throw std::string (STRING_VARIANT_MOD_ZERO); - cast (type_real); - _real = fmod (_real, right._real); + switch (right._type) { + case type_boolean: + throw std::string(STRING_VARIANT_MOD_REAL_BOOL); + + case type_integer: + if (right._integer == 0) throw std::string(STRING_VARIANT_MOD_ZERO); + _real = fmod(_real, static_cast(right._integer)); + break; + + case type_real: + if (right._real == 0) throw std::string(STRING_VARIANT_MOD_ZERO); + _real = fmod(_real, right._real); + break; + + case type_string: + throw std::string(STRING_VARIANT_MOD_REAL_STR); + + case type_date: + throw std::string(STRING_VARIANT_MOD_REAL_DATE); + + case type_duration: + throw std::string(STRING_VARIANT_MOD_REAL_DUR); + } break; case type_string: - throw std::string (STRING_VARIANT_MOD_INT_STR); + throw std::string(STRING_VARIANT_MOD_STR); case type_date: - throw std::string (STRING_VARIANT_MOD_INT_DATE); + throw std::string(STRING_VARIANT_MOD_DATE); case type_duration: - throw std::string (STRING_VARIANT_MOD_INT_DUR); - } - break; - - case type_real: - switch (right._type) - { - case type_boolean: - throw std::string (STRING_VARIANT_MOD_REAL_BOOL); - - case type_integer: - if (right._integer == 0) - throw std::string (STRING_VARIANT_MOD_ZERO); - _real = fmod (_real, static_cast(right._integer)); - break; - - case type_real: - if (right._real == 0) - throw std::string (STRING_VARIANT_MOD_ZERO); - _real = fmod (_real, right._real); - break; - - case type_string: - throw std::string (STRING_VARIANT_MOD_REAL_STR); - - case type_date: - throw std::string (STRING_VARIANT_MOD_REAL_DATE); - - case type_duration: - throw std::string (STRING_VARIANT_MOD_REAL_DUR); - } - break; - - case type_string: - throw std::string (STRING_VARIANT_MOD_STR); - - case type_date: - throw std::string (STRING_VARIANT_MOD_DATE); - - case type_duration: - throw std::string (STRING_VARIANT_MOD_DUR); + throw std::string(STRING_VARIANT_MOD_DUR); } return *this; } //////////////////////////////////////////////////////////////////////////////// -Variant Variant::operator% (const Variant& other) const -{ - Variant left (*this); +Variant Variant::operator%(const Variant& other) const { + Variant left(*this); left %= other; return left; } //////////////////////////////////////////////////////////////////////////////// -Variant::operator std::string () const -{ - switch (_type) - { - case type_boolean: - return std::string (_bool ? "true" : "false"); +Variant::operator std::string() const { + switch (_type) { + case type_boolean: + return std::string(_bool ? "true" : "false"); - case type_integer: - { + case type_integer: { std::stringstream s; s << _integer; - return s.str (); + return s.str(); } - case type_real: - { + case type_real: { std::stringstream s; s << _real; - return s.str (); + return s.str(); } - case type_string: - return _string; + case type_string: + return _string; - case type_date: - return Datetime (_date).toISOLocalExtended (); + case type_date: + return Datetime(_date).toISOLocalExtended(); - case type_duration: - return Duration (_duration).formatISO (); + case type_duration: + return Duration(_duration).formatISO(); } return ""; } //////////////////////////////////////////////////////////////////////////////// -void Variant::sqrt () -{ - if (_type == type_string) - Lexer::dequote (_string); +void Variant::sqrt() { + if (_type == type_string) Lexer::dequote(_string); - cast (type_real); - if (_real < 0.0) - throw std::string (STRING_VARIANT_SQRT_NEG); - _real = ::sqrt (_real); + cast(type_real); + if (_real < 0.0) throw std::string(STRING_VARIANT_SQRT_NEG); + _real = ::sqrt(_real); } //////////////////////////////////////////////////////////////////////////////// -void Variant::cast (const enum type new_type) -{ +void Variant::cast(const enum type new_type) { // Short circuit. - if (_type == new_type) - return; + if (_type == new_type) return; // From type_boolean - switch (_type) - { - case type_boolean: - switch (new_type) - { - case type_boolean: break; - case type_integer: _integer = _bool ? 1 : 0; break; - case type_real: _real = _bool ? 1.0 : 0.0; break; - case type_string: _string = _bool ? "true" : "false"; break; - case type_date: _date = _bool ? 1 : 0; break; - case type_duration: _duration = _bool ? 1 : 0; break; - } - break; - - case type_integer: - switch (new_type) - { - case type_boolean: _bool = _integer == 0 ? false : true; break; - case type_integer: break; - case type_real: _real = static_cast(_integer); break; - case type_string: - { - char temp[24]; - snprintf (temp, 24, "%lld", _integer); - _string = temp; - } - break; - case type_date: _date = (time_t) _integer; break; - case type_duration: _duration = (time_t) _integer; break; - } - break; - - case type_real: - switch (new_type) - { - case type_boolean: _bool = _real == 0.0 ? false : true; break; - case type_integer: _integer = (long long) _real; break; - case type_real: break; - case type_string: - { - char temp[24]; - snprintf (temp, 24, "%g", _real); - _string = temp; - } - break; - case type_date: _date = (time_t) (int) _real; break; - case type_duration: _duration = (time_t) (int) _real; break; - } - break; - - case type_string: - Lexer::dequote (_string); - switch (new_type) - { + switch (_type) { case type_boolean: - _bool = (_string.length () == 0 || - _string == "0" || - _string == "0.0") ? false : true; + switch (new_type) { + case type_boolean: + break; + case type_integer: + _integer = _bool ? 1 : 0; + break; + case type_real: + _real = _bool ? 1.0 : 0.0; + break; + case type_string: + _string = _bool ? "true" : "false"; + break; + case type_date: + _date = _bool ? 1 : 0; + break; + case type_duration: + _duration = _bool ? 1 : 0; + break; + } break; + case type_integer: - _integer = (long long) strtol (_string.c_str (), nullptr, (_string.substr (0, 2) == "0x" ? 16 : 10)); + switch (new_type) { + case type_boolean: + _bool = _integer == 0 ? false : true; + break; + case type_integer: + break; + case type_real: + _real = static_cast(_integer); + break; + case type_string: { + char temp[24]; + snprintf(temp, 24, "%lld", _integer); + _string = temp; + } break; + case type_date: + _date = (time_t)_integer; + break; + case type_duration: + _duration = (time_t)_integer; + break; + } break; - case type_real: _real = strtod (_string.c_str (), nullptr); break; - case type_string: break; + + case type_real: + switch (new_type) { + case type_boolean: + _bool = _real == 0.0 ? false : true; + break; + case type_integer: + _integer = (long long)_real; + break; + case type_real: + break; + case type_string: { + char temp[24]; + snprintf(temp, 24, "%g", _real); + _string = temp; + } break; + case type_date: + _date = (time_t)(int)_real; + break; + case type_duration: + _duration = (time_t)(int)_real; + break; + } + break; + + case type_string: + Lexer::dequote(_string); + switch (new_type) { + case type_boolean: + _bool = (_string.length() == 0 || _string == "0" || _string == "0.0") ? false : true; + break; + case type_integer: + _integer = + (long long)strtol(_string.c_str(), nullptr, (_string.substr(0, 2) == "0x" ? 16 : 10)); + break; + case type_real: + _real = strtod(_string.c_str(), nullptr); + break; + case type_string: + break; + case type_date: { + _date = 0; + Datetime iso; + std::string::size_type pos = 0; + if (iso.parse(_string, pos, dateFormat) && pos == _string.length()) { + _date = iso.toEpoch(); + break; + } + + pos = 0; + Duration isop; + if (isop.parse(_string, pos) && pos == _string.length()) { + _date = Datetime().toEpoch() + isop.toTime_t(); + break; + } + + if (dateFormat != "") { + _date = Datetime(_string, dateFormat).toEpoch(); + break; + } + } break; + case type_duration: { + _duration = 0; + std::string::size_type pos = 0; + Duration iso; + if (iso.parse(_string, pos) && pos == _string.length()) { + _duration = iso.toTime_t(); + } + } break; + } + break; + case type_date: - { - _date = 0; - Datetime iso; - std::string::size_type pos = 0; - if (iso.parse (_string, pos, dateFormat) && - pos == _string.length ()) - { - _date = iso.toEpoch (); + switch (new_type) { + case type_boolean: + _bool = _date != 0 ? true : false; break; - } - - pos = 0; - Duration isop; - if (isop.parse (_string, pos) && - pos == _string.length ()) - { - _date = Datetime ().toEpoch () + isop.toTime_t (); + case type_integer: + _integer = (long long)_date; break; - } - - if (dateFormat != "") - { - _date = Datetime (_string, dateFormat).toEpoch (); + case type_real: + _real = static_cast(_date); + break; + case type_string: + _string = (std::string) * this; + break; + case type_date: + break; + // TODO: Not exactly correct (should duration convert into date?), but + // currently needed for symmetry, which is assumed by operators. + case type_duration: + _duration = _date - time(nullptr); break; - } } break; + case type_duration: - { - _duration = 0; - std::string::size_type pos = 0; - Duration iso; - if (iso.parse (_string, pos) && - pos == _string.length ()) - { - _duration = iso.toTime_t (); - } + switch (new_type) { + case type_boolean: + _bool = _duration != 0 ? true : false; + break; + case type_integer: + _integer = (long long)_duration; + break; + case type_real: + _real = static_cast(_duration); + break; + case type_string: + _string = (std::string) * this; + break; + case type_date: + _date = time(nullptr) + _duration; + break; + case type_duration: + break; } break; - } - break; - - case type_date: - switch (new_type) - { - case type_boolean: _bool = _date != 0 ? true : false; break; - case type_integer: _integer = (long long) _date; break; - case type_real: _real = static_cast(_date); break; - case type_string: _string = (std::string) *this; break; - case type_date: break; - // TODO: Not exactly correct (should duration convert into date?), but - // currently needed for symmetry, which is assumed by operators. - case type_duration: _duration = _date - time (nullptr); break; - } - break; - - case type_duration: - switch (new_type) - { - case type_boolean: _bool = _duration != 0 ? true : false; break; - case type_integer: _integer = (long long) _duration; break; - case type_real: _real = static_cast(_duration); break; - case type_string: _string = (std::string) *this; break; - case type_date: _date = time (nullptr) + _duration; break; - case type_duration: break; - } - break; } _type = new_type; } //////////////////////////////////////////////////////////////////////////////// -int Variant::type () -{ - return _type; -} +int Variant::type() { return _type; } //////////////////////////////////////////////////////////////////////////////// -bool Variant::trivial () const -{ - return (_type == type_integer && _integer == 0) || - (_type == type_real && _real == 0.0) || - (_type == type_string && _string == "") || - (_type == type_date && _date == 0) || +bool Variant::trivial() const { + return (_type == type_integer && _integer == 0) || (_type == type_real && _real == 0.0) || + (_type == type_string && _string == "") || (_type == type_date && _date == 0) || (_type == type_duration && _duration == 0); } //////////////////////////////////////////////////////////////////////////////// -bool Variant::get_bool () const -{ - return _bool; -} +bool Variant::get_bool() const { return _bool; } //////////////////////////////////////////////////////////////////////////////// -long long Variant::get_integer () const -{ - return _integer; -} +long long Variant::get_integer() const { return _integer; } //////////////////////////////////////////////////////////////////////////////// -double Variant::get_real () const -{ - return _real; -} +double Variant::get_real() const { return _real; } //////////////////////////////////////////////////////////////////////////////// -const std::string& Variant::get_string () const -{ - return _string; -} +const std::string& Variant::get_string() const { return _string; } //////////////////////////////////////////////////////////////////////////////// -time_t Variant::get_date () const -{ - return _date; -} +time_t Variant::get_date() const { return _date; } //////////////////////////////////////////////////////////////////////////////// -time_t Variant::get_duration () const -{ - return _duration; -} +time_t Variant::get_duration() const { return _duration; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Variant.h b/src/Variant.h index 2970a9e61..3cbc5c6af 100644 --- a/src/Variant.h +++ b/src/Variant.h @@ -27,94 +27,94 @@ #ifndef INCLUDED_VARIANT #define INCLUDED_VARIANT +#include +#include + #include #include -#include -#include -class Variant -{ -public: +class Variant { + public: static std::string dateFormat; static bool searchCaseSensitive; static bool searchUsingRegex; static bool isoEnabled; - enum type {type_boolean, type_integer, type_real, type_string, type_date, type_duration}; + enum type { type_boolean, type_integer, type_real, type_string, type_date, type_duration }; - Variant () = default; - Variant (const Variant&); - Variant (const bool); - Variant (const int); - Variant (const double); - Variant (const std::string&); - Variant (const char*); - Variant (const time_t, const enum type); + Variant() = default; + Variant(const Variant&); + Variant(const bool); + Variant(const int); + Variant(const double); + Variant(const std::string&); + Variant(const char*); + Variant(const time_t, const enum type); - void source (const std::string&); - const std::string& source () const; + void source(const std::string&); + const std::string& source() const; - Variant& operator= (const Variant&); + Variant& operator=(const Variant&); - bool operator&& (const Variant&) const; - bool operator|| (const Variant&) const; - bool operator_xor (const Variant&) const; - bool operator< (const Variant&) const; - bool operator<= (const Variant&) const; - bool operator> (const Variant&) const; - bool operator>= (const Variant&) const; - bool operator== (const Variant&) const; - bool operator!= (const Variant&) const; - bool operator_match (const Variant&, const Task&) const; - bool operator_nomatch (const Variant&, const Task&) const; - bool operator_partial (const Variant&) const; - bool operator_nopartial (const Variant&) const; - bool operator_hastag (const Variant&, const Task&) const; - bool operator_notag (const Variant&, const Task&) const; - bool operator! () const; + bool operator&&(const Variant&) const; + bool operator||(const Variant&) const; + bool operator_xor(const Variant&) const; + bool operator<(const Variant&) const; + bool operator<=(const Variant&) const; + bool operator>(const Variant&) const; + bool operator>=(const Variant&) const; + bool operator==(const Variant&) const; + bool operator!=(const Variant&) const; + bool operator_match(const Variant&, const Task&) const; + bool operator_nomatch(const Variant&, const Task&) const; + bool operator_partial(const Variant&) const; + bool operator_nopartial(const Variant&) const; + bool operator_hastag(const Variant&, const Task&) const; + bool operator_notag(const Variant&, const Task&) const; + bool operator!() const; - Variant& operator^= (const Variant&); - Variant operator^ (const Variant&) const; + Variant& operator^=(const Variant&); + Variant operator^(const Variant&) const; - Variant& operator-= (const Variant&); - Variant operator- (const Variant&) const; + Variant& operator-=(const Variant&); + Variant operator-(const Variant&) const; - Variant& operator+= (const Variant&); - Variant operator+ (const Variant&) const; + Variant& operator+=(const Variant&); + Variant operator+(const Variant&) const; - Variant& operator*= (const Variant&); - Variant operator* (const Variant&) const; + Variant& operator*=(const Variant&); + Variant operator*(const Variant&) const; - Variant& operator/= (const Variant&); - Variant operator/ (const Variant&) const; + Variant& operator/=(const Variant&); + Variant operator/(const Variant&) const; - Variant& operator%= (const Variant&); - Variant operator% (const Variant&) const; + Variant& operator%=(const Variant&); + Variant operator%(const Variant&) const; - operator std::string () const; - void sqrt (); + operator std::string() const; + void sqrt(); - void cast (const enum type); - int type (); - bool trivial () const; + void cast(const enum type); + int type(); + bool trivial() const; - bool get_bool () const; - long long get_integer () const; - double get_real () const; - const std::string& get_string () const; - time_t get_date () const; - time_t get_duration () const; + bool get_bool() const; + long long get_integer() const; + double get_real() const; + const std::string& get_string() const; + time_t get_date() const; + time_t get_duration() const; -private: - enum type _type {type_boolean}; - bool _bool {false}; - long long _integer {0}; - double _real {0.0}; - std::string _string {""}; - time_t _date {0}; - time_t _duration {0}; + private: + enum type _type { type_boolean }; + bool _bool{false}; + long long _integer{0}; + double _real{0.0}; + std::string _string{""}; + time_t _date{0}; + time_t _duration{0}; - std::string _source {""}; + std::string _source{""}; }; #endif diff --git a/src/Version.cpp b/src/Version.cpp index 8d79c6816..e1633f86c 100644 --- a/src/Version.cpp +++ b/src/Version.cpp @@ -67,38 +67,32 @@ bool Version::is_valid() const { return major >= 0; } //////////////////////////////////////////////////////////////////////////////// bool Version::operator<(const Version &other) const { - return std::tie(major, minor, patch) < - std::tie(other.major, other.minor, other.patch); + return std::tie(major, minor, patch) < std::tie(other.major, other.minor, other.patch); } //////////////////////////////////////////////////////////////////////////////// bool Version::operator<=(const Version &other) const { - return std::tie(major, minor, patch) <= - std::tie(other.major, other.minor, other.patch); + return std::tie(major, minor, patch) <= std::tie(other.major, other.minor, other.patch); } //////////////////////////////////////////////////////////////////////////////// bool Version::operator>(const Version &other) const { - return std::tie(major, minor, patch) > - std::tie(other.major, other.minor, other.patch); + return std::tie(major, minor, patch) > std::tie(other.major, other.minor, other.patch); } //////////////////////////////////////////////////////////////////////////////// bool Version::operator>=(const Version &other) const { - return std::tie(major, minor, patch) >= - std::tie(other.major, other.minor, other.patch); + return std::tie(major, minor, patch) >= std::tie(other.major, other.minor, other.patch); } //////////////////////////////////////////////////////////////////////////////// bool Version::operator==(const Version &other) const { - return std::tie(major, minor, patch) == - std::tie(other.major, other.minor, other.patch); + return std::tie(major, minor, patch) == std::tie(other.major, other.minor, other.patch); } //////////////////////////////////////////////////////////////////////////////// bool Version::operator!=(const Version &other) const { - return std::tie(major, minor, patch) != - std::tie(other.major, other.minor, other.patch); + return std::tie(major, minor, patch) != std::tie(other.major, other.minor, other.patch); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Version.h b/src/Version.h index 2c96d61e7..03b81d2ad 100644 --- a/src/Version.h +++ b/src/Version.h @@ -31,7 +31,7 @@ // A utility class for handling Taskwarrior versions. class Version { -public: + public: // Parse a version from a string. This must be of the format // digits.digits.digits. explicit Version(std::string version); @@ -60,9 +60,9 @@ public: // Convert back to a string. operator std::string() const; - friend std::ostream& operator<<(std::ostream& os, const Version& version); + friend std::ostream &operator<<(std::ostream &os, const Version &version); -private: + private: int major = -1; int minor = -1; int patch = -1; diff --git a/src/ViewTask.cpp b/src/ViewTask.cpp index 791316002..ce4cbaa2a 100644 --- a/src/ViewTask.cpp +++ b/src/ViewTask.cpp @@ -27,42 +27,39 @@ #include // cmake.h include header must come first -#include -#include #include +#include #include -#include -#include #include +#include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -ViewTask::ViewTask () -: _width (0) -, _left_margin (0) -, _header (0) -, _sort_header (0) -, _odd (0) -, _even (0) -, _intra_padding (1) -, _intra_odd (0) -, _intra_even (0) -, _extra_padding (0) -, _extra_odd (0) -, _extra_even (0) -, _truncate_lines (0) -, _truncate_rows (0) -, _lines (0) -, _rows (0) -{ -} +ViewTask::ViewTask() + : _width(0), + _left_margin(0), + _header(0), + _sort_header(0), + _odd(0), + _even(0), + _intra_padding(1), + _intra_odd(0), + _intra_even(0), + _extra_padding(0), + _extra_odd(0), + _extra_even(0), + _truncate_lines(0), + _truncate_rows(0), + _lines(0), + _rows(0) {} //////////////////////////////////////////////////////////////////////////////// -ViewTask::~ViewTask () -{ - for (auto& col : _columns) - delete col; +ViewTask::~ViewTask() { + for (auto& col : _columns) delete col; - _columns.clear (); + _columns.clear(); } //////////////////////////////////////////////////////////////////////////////// @@ -108,64 +105,55 @@ ViewTask::~ViewTask () // the larger fields. If the widest field is W0, and the second widest // field is W1, then a solution may be achievable by reducing W0 --> W1. // -std::string ViewTask::render (std::vector & data, std::vector & sequence) -{ +std::string ViewTask::render(std::vector& data, std::vector& sequence) { Timer timer; - bool const obfuscate = Context::getContext ().config.getBoolean ("obfuscate"); - bool const print_empty_columns = Context::getContext ().config.getBoolean ("print.empty.columns"); - std::vector nonempty_columns; - std::vector nonempty_sort; + bool const obfuscate = Context::getContext().config.getBoolean("obfuscate"); + bool const print_empty_columns = Context::getContext().config.getBoolean("print.empty.columns"); + std::vector nonempty_columns; + std::vector nonempty_sort; // Determine minimal, ideal column widths. - std::vector minimal; - std::vector ideal; + std::vector minimal; + std::vector ideal; - for (unsigned int i = 0; i < _columns.size (); ++i) - { + for (unsigned int i = 0; i < _columns.size(); ++i) { // Headers factor in to width calculations. unsigned int global_min = 0; unsigned int global_ideal = global_min; - for (unsigned int s = 0; s < sequence.size (); ++s) - { - if ((int)s >= _truncate_lines && _truncate_lines != 0) - break; + for (unsigned int s = 0; s < sequence.size(); ++s) { + if ((int)s >= _truncate_lines && _truncate_lines != 0) break; - if ((int)s >= _truncate_rows && _truncate_rows != 0) - break; + if ((int)s >= _truncate_rows && _truncate_rows != 0) break; // Determine minimum and ideal width for this column. unsigned int min = 0; unsigned int ideal = 0; - _columns[i]->measure (data[sequence[s]], min, ideal); + _columns[i]->measure(data[sequence[s]], min, ideal); - if (min > global_min) global_min = min; + if (min > global_min) global_min = min; if (ideal > global_ideal) global_ideal = ideal; // If a fixed-width column was just measured, there is no point repeating // the measurement for all tasks. - if (_columns[i]->is_fixed_width ()) - break; + if (_columns[i]->is_fixed_width()) break; } - if (print_empty_columns || global_min != 0) - { - unsigned int label_length = utf8_width (_columns[i]->label ()); - if (label_length > global_min) global_min = label_length; + if (print_empty_columns || global_min != 0) { + unsigned int label_length = utf8_width(_columns[i]->label()); + if (label_length > global_min) global_min = label_length; if (label_length > global_ideal) global_ideal = label_length; - minimal.push_back (global_min); - ideal.push_back (global_ideal); + minimal.push_back(global_min); + ideal.push_back(global_ideal); } - if (! print_empty_columns) - { - if (global_min != 0) // Column is nonempty + if (!print_empty_columns) { + if (global_min != 0) // Column is nonempty { - nonempty_columns.push_back (_columns[i]); - nonempty_sort.push_back (_sort[i]); - } - else // Column is empty, drop it + nonempty_columns.push_back(_columns[i]); + nonempty_sort.push_back(_sort[i]); + } else // Column is empty, drop it { // Note: This is safe to do because we set _columns = nonempty_columns // after iteration over _columns is finished. @@ -174,51 +162,40 @@ std::string ViewTask::render (std::vector & data, std::vector & seque } } - if (! print_empty_columns) - { + if (!print_empty_columns) { _columns = nonempty_columns; _sort = nonempty_sort; } - int all_extra = _left_margin - + (2 * _extra_padding) - + ((_columns.size () - 1) * _intra_padding); + int all_extra = _left_margin + (2 * _extra_padding) + ((_columns.size() - 1) * _intra_padding); // Sum the widths. - int sum_minimal = std::accumulate (minimal.begin (), minimal.end (), 0); - int sum_ideal = std::accumulate (ideal.begin (), ideal.end (), 0); + int sum_minimal = std::accumulate(minimal.begin(), minimal.end(), 0); + int sum_ideal = std::accumulate(ideal.begin(), ideal.end(), 0); // Calculate final column widths. int overage = _width - sum_minimal - all_extra; - Context::getContext ().debug (format ("ViewTask::render min={1} ideal={2} overage={3} width={4}", - sum_minimal + all_extra, - sum_ideal + all_extra, - overage, - _width)); + Context::getContext().debug(format("ViewTask::render min={1} ideal={2} overage={3} width={4}", + sum_minimal + all_extra, sum_ideal + all_extra, overage, + _width)); - std::vector widths; + std::vector widths; // Ideal case. Everything fits. - if (_width == 0 || sum_ideal + all_extra <= _width) - { + if (_width == 0 || sum_ideal + all_extra <= _width) { widths = ideal; } // Not enough for minimum. Decrease certain columns. - else if (overage < 0) - { + else if (overage < 0) { // Determine which columns are the longest. unsigned int longest = 0; unsigned int second_longest = 0; - for (unsigned int j = 0; j < minimal.size(); j++) - { - if (minimal[j] > minimal[longest]) - { + for (unsigned int j = 0; j < minimal.size(); j++) { + if (minimal[j] > minimal[longest]) { second_longest = longest; longest = j; - } - else if (minimal[j] > minimal[second_longest]) - { + } else if (minimal[j] > minimal[second_longest]) { second_longest = j; } } @@ -226,53 +203,46 @@ std::string ViewTask::render (std::vector & data, std::vector & seque // Case 1: Shortening longest column still keeps it longest. Let it bear // all the shortening. widths = minimal; - if (minimal[longest] + overage >= minimal[second_longest]) - widths[longest] += overage; + if (minimal[longest] + overage >= minimal[second_longest]) widths[longest] += overage; // Case 2: Shorten the longest column to second longest length. Try to // split shortening them evenly. - else - { + else { int decrease = minimal[second_longest] - minimal[longest]; widths[longest] += decrease; overage = overage - decrease; // Attempt to decrease the two longest columns (at most to two characters) - if (-overage <= widths[longest] + widths[second_longest] - 4) - { + if (-overage <= widths[longest] + widths[second_longest] - 4) { // Compute half of the overage, rounding up int half_overage = overage / 2 + overage % 2; // Decrease both larges columns by this amount widths[longest] += half_overage; widths[second_longest] += half_overage; - } - else + } else // If reducing two of the longest solumns to 2 characters is not sufficient, then give up. - Context::getContext ().error (format ("The report has a minimum width of {1} and does not fit in the available width of {2}.", sum_minimal + all_extra, _width)); + Context::getContext().error(format( + "The report has a minimum width of {1} and does not fit in the available width of {2}.", + sum_minimal + all_extra, _width)); } } // Perfect minimal width. - else if (overage == 0) - { + else if (overage == 0) { widths = minimal; } // Extra space to share. - else if (overage > 0) - { + else if (overage > 0) { widths = minimal; // Spread 'overage' among columns where width[i] < ideal[i] bool needed = true; - while (overage && needed) - { + while (overage && needed) { needed = false; - for (unsigned int i = 0; i < _columns.size () && overage; ++i) - { - if (widths[i] < ideal[i]) - { + for (unsigned int i = 0; i < _columns.size() && overage; ++i) { + if (widths[i] < ideal[i]) { ++widths[i]; --overage; needed = true; @@ -283,39 +253,34 @@ std::string ViewTask::render (std::vector & data, std::vector & seque // Compose column headers. unsigned int max_lines = 0; - std::vector > headers; - for (unsigned int c = 0; c < _columns.size (); ++c) - { - headers.emplace_back (); - _columns[c]->renderHeader (headers[c], widths[c], _sort[c] ? _sort_header : _header); + std::vector> headers; + for (unsigned int c = 0; c < _columns.size(); ++c) { + headers.emplace_back(); + _columns[c]->renderHeader(headers[c], widths[c], _sort[c] ? _sort_header : _header); - if (headers[c].size () > max_lines) - max_lines = headers[c].size (); + if (headers[c].size() > max_lines) max_lines = headers[c].size(); } // Render column headers. - std::string left_margin = std::string (_left_margin, ' '); - std::string extra = std::string (_extra_padding, ' '); - std::string intra = std::string (_intra_padding, ' '); + std::string left_margin = std::string(_left_margin, ' '); + std::string extra = std::string(_extra_padding, ' '); + std::string intra = std::string(_intra_padding, ' '); - std::string extra_odd = Context::getContext ().color () ? _extra_odd.colorize (extra) : extra; - std::string extra_even = Context::getContext ().color () ? _extra_even.colorize (extra) : extra; - std::string intra_odd = Context::getContext ().color () ? _intra_odd.colorize (intra) : intra; - std::string intra_even = Context::getContext ().color () ? _intra_even.colorize (intra) : intra; + std::string extra_odd = Context::getContext().color() ? _extra_odd.colorize(extra) : extra; + std::string extra_even = Context::getContext().color() ? _extra_even.colorize(extra) : extra; + std::string intra_odd = Context::getContext().color() ? _intra_odd.colorize(intra) : intra; + std::string intra_even = Context::getContext().color() ? _intra_even.colorize(intra) : intra; std::string out; _lines = 0; - for (unsigned int i = 0; i < max_lines; ++i) - { + for (unsigned int i = 0; i < max_lines; ++i) { out += left_margin + extra; - for (unsigned int c = 0; c < _columns.size (); ++c) - { - if (c) - out += intra; + for (unsigned int c = 0; c < _columns.size(); ++c) { + if (c) out += intra; - if (headers[c].size () < max_lines - i) - out += _header.colorize (std::string (widths[c], ' ')); + if (headers[c].size() < max_lines - i) + out += _header.colorize(std::string(widths[c], ' ')); else out += headers[c][i]; } @@ -323,60 +288,50 @@ std::string ViewTask::render (std::vector & data, std::vector & seque out += extra; // Trim right. - out.erase (out.find_last_not_of (' ') + 1); + out.erase(out.find_last_not_of(' ') + 1); out += "\n"; // Stop if the line limit is exceeded. - if (++_lines >= _truncate_lines && _truncate_lines != 0) - { - Context::getContext ().time_render_us += timer.total_us (); + if (++_lines >= _truncate_lines && _truncate_lines != 0) { + Context::getContext().time_render_us += timer.total_us(); return out; } } // Compose, render columns, in sequence. _rows = 0; - std::vector > cells; - for (unsigned int s = 0; s < sequence.size (); ++s) - { + std::vector> cells; + for (unsigned int s = 0; s < sequence.size(); ++s) { max_lines = 0; // Apply color rules to task. Color rule_color; - autoColorize (data[sequence[s]], rule_color); + autoColorize(data[sequence[s]], rule_color); // Alternate rows based on |s % 2| bool odd = (s % 2) ? true : false; Color row_color; - if (Context::getContext ().color ()) - { + if (Context::getContext().color()) { row_color = odd ? _odd : _even; - row_color.blend (rule_color); + row_color.blend(rule_color); } - for (unsigned int c = 0; c < _columns.size (); ++c) - { - cells.emplace_back (); - _columns[c]->render (cells[c], data[sequence[s]], widths[c], row_color); + for (unsigned int c = 0; c < _columns.size(); ++c) { + cells.emplace_back(); + _columns[c]->render(cells[c], data[sequence[s]], widths[c], row_color); - if (cells[c].size () > max_lines) - max_lines = cells[c].size (); + if (cells[c].size() > max_lines) max_lines = cells[c].size(); if (obfuscate) - if (_columns[c]->type () == "string") - for (auto& line : cells[c]) - line = obfuscateText (line); + if (_columns[c]->type() == "string") + for (auto& line : cells[c]) line = obfuscateText(line); } // Listing breaks are simply blank lines inserted when a column value // changes. - if (s > 0 && - _breaks.size () > 0) - { - for (const auto& b : _breaks) - { - if (data[sequence[s - 1]].get (b) != data[sequence[s]].get (b)) - { + if (s > 0 && _breaks.size() > 0) { + for (const auto& b : _breaks) { + if (data[sequence[s - 1]].get(b) != data[sequence[s]].get(b)) { out += "\n"; ++_lines; @@ -386,51 +341,46 @@ std::string ViewTask::render (std::vector & data, std::vector & seque } } - for (unsigned int i = 0; i < max_lines; ++i) - { + for (unsigned int i = 0; i < max_lines; ++i) { out += left_margin + (odd ? extra_odd : extra_even); - for (unsigned int c = 0; c < _columns.size (); ++c) - { - if (c) - { - if (row_color.nontrivial ()) - row_color._colorize (out, intra); + for (unsigned int c = 0; c < _columns.size(); ++c) { + if (c) { + if (row_color.nontrivial()) + row_color._colorize(out, intra); else out += (odd ? intra_odd : intra_even); } - if (i < cells[c].size ()) + if (i < cells[c].size()) out += cells[c][i]; else - row_color._colorize (out, std::string (widths[c], ' ')); + row_color._colorize(out, std::string(widths[c], ' ')); } out += (odd ? extra_odd : extra_even); // Trim right. - out.erase (out.find_last_not_of (' ') + 1); + out.erase(out.find_last_not_of(' ') + 1); out += "\n"; // Stop if the line limit is exceeded. - if (++_lines >= _truncate_lines && _truncate_lines != 0) - { - Context::getContext ().time_render_us += timer.total_us (); + if (++_lines >= _truncate_lines && _truncate_lines != 0) { + Context::getContext().time_render_us += timer.total_us(); return out; } } - cells.clear (); + cells.clear(); // Stop if the row limit is exceeded. - if (++_rows >= _truncate_rows && _truncate_rows != 0) - { - Context::getContext ().time_render_us += timer.total_us (); + if (++_rows >= _truncate_rows && _truncate_rows != 0) { + Context::getContext().time_render_us += timer.total_us(); return out; } } - Context::getContext ().time_render_us += timer.total_us (); + Context::getContext().time_render_us += timer.total_us(); return out; } diff --git a/src/ViewTask.h b/src/ViewTask.h index e71bc95bf..b606ec992 100644 --- a/src/ViewTask.h +++ b/src/ViewTask.h @@ -27,63 +27,68 @@ #ifndef INCLUDED_VIEWTASK #define INCLUDED_VIEWTASK -#include -#include -#include #include #include +#include -class ViewTask -{ -public: - ViewTask (); - ~ViewTask (); +#include +#include + +class ViewTask { + public: + ViewTask(); + ~ViewTask(); // View specifications. - void add (Column* column, bool sort = false) { _columns.push_back (column); _sort.push_back (sort); } - void width (int width) { _width = width; } - void leftMargin (int margin) { _left_margin = margin; } - void colorHeader (Color& c) { _header = c; if (!_sort_header) _sort_header = c; } - void colorSortHeader (Color& c) { _sort_header = c; } - void colorOdd (Color& c) { _odd = c; } - void colorEven (Color& c) { _even = c; } - void intraPadding (int padding) { _intra_padding = padding; } - void intraColorOdd (Color& c) { _intra_odd = c; } - void intraColorEven (Color& c) { _intra_even = c; } - void extraPadding (int padding) { _extra_padding = padding; } - void extraColorOdd (Color& c) { _extra_odd = c; } - void extraColorEven (Color& c) { _extra_even = c; } - void truncateLines (int n) { _truncate_lines = n; } - void truncateRows (int n) { _truncate_rows = n; } - void addBreak (const std::string& attr) { _breaks.push_back (attr); } - int lines () { return _lines; } - int rows () { return _rows; } + void add(Column* column, bool sort = false) { + _columns.push_back(column); + _sort.push_back(sort); + } + void width(int width) { _width = width; } + void leftMargin(int margin) { _left_margin = margin; } + void colorHeader(Color& c) { + _header = c; + if (!_sort_header) _sort_header = c; + } + void colorSortHeader(Color& c) { _sort_header = c; } + void colorOdd(Color& c) { _odd = c; } + void colorEven(Color& c) { _even = c; } + void intraPadding(int padding) { _intra_padding = padding; } + void intraColorOdd(Color& c) { _intra_odd = c; } + void intraColorEven(Color& c) { _intra_even = c; } + void extraPadding(int padding) { _extra_padding = padding; } + void extraColorOdd(Color& c) { _extra_odd = c; } + void extraColorEven(Color& c) { _extra_even = c; } + void truncateLines(int n) { _truncate_lines = n; } + void truncateRows(int n) { _truncate_rows = n; } + void addBreak(const std::string& attr) { _breaks.push_back(attr); } + int lines() { return _lines; } + int rows() { return _rows; } // View rendering. - std::string render (std::vector &, std::vector &); + std::string render(std::vector&, std::vector&); -private: - std::vector _columns; - std::vector _sort; - std::vector _breaks; - int _width; - int _left_margin; - Color _header; - Color _sort_header; - Color _odd; - Color _even; - int _intra_padding; - Color _intra_odd; - Color _intra_even; - int _extra_padding; - Color _extra_odd; - Color _extra_even; - int _truncate_lines; - int _truncate_rows; - int _lines; - int _rows; + private: + std::vector _columns; + std::vector _sort; + std::vector _breaks; + int _width; + int _left_margin; + Color _header; + Color _sort_header; + Color _odd; + Color _even; + int _intra_padding; + Color _intra_odd; + Color _intra_even; + int _extra_padding; + Color _extra_odd; + Color _extra_even; + int _truncate_lines; + int _truncate_rows; + int _lines; + int _rows; }; #endif //////////////////////////////////////////////////////////////////////////////// - diff --git a/src/calc.cpp b/src/calc.cpp index af852d64b..4ff490abd 100644 --- a/src/calc.cpp +++ b/src/calc.cpp @@ -27,57 +27,53 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include #include -#include #include #include -#include +#include +#include #include +#include +#include +#include + +#include +#include //////////////////////////////////////////////////////////////////////////////// // Constants. -bool get (const std::string&, Variant&) -{ -/* - // An example, although a bad one because this is supported by default. - if (name == "pi") {value = Variant (3.14159165); return true;} -*/ +bool get(const std::string&, Variant&) { + /* + // An example, although a bad one because this is supported by default. + if (name == "pi") {value = Variant (3.14159165); return true;} + */ return false; } //////////////////////////////////////////////////////////////////////////////// -int main (int argc, char** argv) -{ +int main(int argc, char** argv) { int status = 0; - try - { + try { Context globalContext; - Context::setContext (&globalContext); + Context::setContext(&globalContext); // Same operating parameters as Context::staticInitialization. - Datetime::standaloneDateEnabled = false; - Datetime::standaloneTimeEnabled = false; + Datetime::standaloneDateEnabled = false; + Datetime::standaloneTimeEnabled = false; Duration::standaloneSecondsEnabled = false; - bool infix {true}; + bool infix{true}; // Add a source for constants. Eval e; - e.addSource (get); + e.addSource(get); // Combine all the arguments into one expression string. std::string expression; - for (int i = 1; i < argc; i++) - { - if (!strcmp (argv[i], "-h") || ! strcmp (argv[i], "--help")) - { + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { std::cout << '\n' << "Usage: " << argv[0] << " [options] ''\n" << '\n' @@ -87,56 +83,47 @@ int main (int argc, char** argv) << " -i|--infix Infix expression (default)\n" << " -p|--postfix Postfix expression\n" << '\n'; - exit (1); - } - else if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "--version")) - { + exit(1); + } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) { std::cout << '\n' - << format ("calc {1} built for ", VERSION) - << osName () + << format("calc {1} built for ", VERSION) << osName() << '\n' + << "Copyright (C) 2006 - 2021 T. Babej, P. Beckingham, F. Hernandez." << '\n' << '\n' - << "Copyright (C) 2006 - 2021 T. Babej, P. Beckingham, F. Hernandez." - << '\n' - << '\n' - << "Taskwarrior may be copied only under the terms of the MIT license, which may be found in the Taskwarrior source kit." + << "Taskwarrior may be copied only under the terms of the MIT license, which may " + "be found in the Taskwarrior source kit." << '\n' << '\n'; - exit (1); - } - else if (!strcmp (argv[i], "-d") || !strcmp (argv[i], "--debug")) - e.debug (true); - else if (!strcmp (argv[i], "-i") || !strcmp (argv[i], "--infix")) + exit(1); + } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) + e.debug(true); + else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--infix")) infix = true; - else if (!strcmp (argv[i], "-p") || !strcmp (argv[i], "--postfix")) + else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--postfix")) infix = false; else - expression += std::string (argv[i]) + ' '; + expression += std::string(argv[i]) + ' '; } Variant result; if (infix) - e.evaluateInfixExpression (expression, result); + e.evaluateInfixExpression(expression, result); else - e.evaluatePostfixExpression (expression, result); + e.evaluatePostfixExpression(expression, result); // Show any debug output. - for (const auto& i : Context::getContext ().debugMessages) - std::cout << i << '\n'; + for (const auto& i : Context::getContext().debugMessages) std::cout << i << '\n'; // Show the result in string form. - std::cout << (std::string) result - << '\n'; + std::cout << (std::string)result << '\n'; } - catch (const std::string& error) - { + catch (const std::string& error) { std::cerr << error << '\n'; status = -1; } - catch (...) - { + catch (...) { std::cerr << "Unknown error occured. Oops.\n"; status = -2; } diff --git a/src/columns/ColDepends.cpp b/src/columns/ColDepends.cpp index 1bd01a72b..d0c77d0b3 100644 --- a/src/columns/ColDepends.cpp +++ b/src/columns/ColDepends.cpp @@ -28,30 +28,26 @@ // cmake.h include header must come first #include -#include #include -#include #include -#include #include -#include +#include #include +#include +#include + +#include #include #define STRING_COLUMN_LABEL_DEP "Depends" //////////////////////////////////////////////////////////////////////////////// -ColumnDepends::ColumnDepends () -{ - _name = "depends"; - _style = "list"; - _label = STRING_COLUMN_LABEL_DEP; - _styles = {"list", - "count", - "indicator"}; - _examples = {"1 2 10", - "[3]", - Context::getContext ().config.get ("dependency.indicator")}; +ColumnDepends::ColumnDepends() { + _name = "depends"; + _style = "list"; + _label = STRING_COLUMN_LABEL_DEP; + _styles = {"list", "count", "indicator"}; + _examples = {"1 2 10", "[3]", Context::getContext().config.get("dependency.indicator")}; _hyphenate = false; } @@ -59,156 +55,131 @@ ColumnDepends::ColumnDepends () //////////////////////////////////////////////////////////////////////////////// // Overriden so that style <----> label are linked. // Note that you can not determine which gets called first. -void ColumnDepends::setStyle (const std::string& value) -{ - Column::setStyle (value); +void ColumnDepends::setStyle(const std::string& value) { + Column::setStyle(value); - if (_style == "indicator" && _label == STRING_COLUMN_LABEL_DEP) _label = _label.substr (0, Context::getContext ().config.get ("dependency.indicator").length ()); - else if (_style == "count" && _label == STRING_COLUMN_LABEL_DEP) _label = "Dep"; + if (_style == "indicator" && _label == STRING_COLUMN_LABEL_DEP) + _label = _label.substr(0, Context::getContext().config.get("dependency.indicator").length()); + else if (_style == "count" && _label == STRING_COLUMN_LABEL_DEP) + _label = "Dep"; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnDepends::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnDepends::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - auto deptasks = task.getDependencyTasks (); + auto deptasks = task.getDependencyTasks(); - if (deptasks.size () > 0) - { - if (_style == "indicator") - { - minimum = maximum = utf8_width (Context::getContext ().config.get ("dependency.indicator")); + if (deptasks.size() > 0) { + if (_style == "indicator") { + minimum = maximum = utf8_width(Context::getContext().config.get("dependency.indicator")); } - else if (_style == "count") - { - minimum = maximum = 2 + format ((int) deptasks.size ()).length (); + else if (_style == "count") { + minimum = maximum = 2 + format((int)deptasks.size()).length(); } - else if (_style == "default" || - _style == "list") - { + else if (_style == "default" || _style == "list") { minimum = maximum = 0; - std::vector blocking_ids; + std::vector blocking_ids; blocking_ids.reserve(deptasks.size()); - for (auto& i : deptasks) - blocking_ids.push_back (i.id); + for (auto& i : deptasks) blocking_ids.push_back(i.id); - auto all = join (" ", blocking_ids); - maximum = all.length (); + auto all = join(" ", blocking_ids); + maximum = all.length(); unsigned int length; - for (auto& i : deptasks) - { - length = format (i.id).length (); - if (length > minimum) - minimum = length; + for (auto& i : deptasks) { + length = format(i.id).length(); + if (length > minimum) minimum = length; } } } } //////////////////////////////////////////////////////////////////////////////// -void ColumnDepends::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - auto deptasks = task.getDependencyTasks (); +void ColumnDepends::render(std::vector& lines, Task& task, int width, Color& color) { + auto deptasks = task.getDependencyTasks(); - if (deptasks.size () > 0) - { - if (_style == "indicator") - { - renderStringRight (lines, width, color, Context::getContext ().config.get ("dependency.indicator")); + if (deptasks.size() > 0) { + if (_style == "indicator") { + renderStringRight(lines, width, color, + Context::getContext().config.get("dependency.indicator")); } - else if (_style == "count") - { - renderStringRight (lines, width, color, '[' + format (static_cast (deptasks.size ())) + ']'); + else if (_style == "count") { + renderStringRight(lines, width, color, '[' + format(static_cast(deptasks.size())) + ']'); } - else if (_style == "default" || - _style == "list") - { - std::vector blocking_ids; + else if (_style == "default" || _style == "list") { + std::vector blocking_ids; blocking_ids.reserve(deptasks.size()); - for (const auto& t : deptasks) - blocking_ids.push_back (t.id); + for (const auto& t : deptasks) blocking_ids.push_back(t.id); - auto combined = join (" ", blocking_ids); + auto combined = join(" ", blocking_ids); - std::vector all; - wrapText (all, combined, width, _hyphenate); + std::vector all; + wrapText(all, combined, width, _hyphenate); - for (const auto& i : all) - renderStringLeft (lines, width, color, i); + for (const auto& i : all) renderStringLeft(lines, width, color, i); } } } //////////////////////////////////////////////////////////////////////////////// -void ColumnDepends::modify (Task& task, const std::string& value) -{ +void ColumnDepends::modify(Task& task, const std::string& value) { // Apply or remove dendencies in turn. - for (auto& dep : split (value, ',')) - { + for (auto& dep : split(value, ',')) { bool removal = false; - if (dep[0] == '-') - { + if (dep[0] == '-') { removal = true; dep = dep.substr(1); } - auto hyphen = dep.find ('-'); - long lower, upper; // For ID ranges - std::regex valid_uuid ("[a-f0-9]{8}([a-f0-9-]{4,28})?"); // TODO: Make more precise + auto hyphen = dep.find('-'); + long lower, upper; // For ID ranges + std::regex valid_uuid("[a-f0-9]{8}([a-f0-9-]{4,28})?"); // TODO: Make more precise // UUID - if (dep.length () >= 8 && std::regex_match (dep, valid_uuid)) - { - // Full UUID, can be added directly - if (dep.length () == 36) - if (removal) - task.removeDependency (dep); - else - task.addDependency (dep); + if (dep.length() >= 8 && std::regex_match(dep, valid_uuid)) { + // Full UUID, can be added directly + if (dep.length() == 36) + if (removal) + task.removeDependency(dep); + else + task.addDependency(dep); - // Short UUID, need to look up full form - else - { - Task loaded_task; - if (Context::getContext ().tdb2.get (dep, loaded_task)) - if (removal) - task.removeDependency (loaded_task.get ("uuid")); - else - task.addDependency (loaded_task.get ("uuid")); - else - throw format ("Dependency could not be set - task with UUID '{1}' does not exist.", dep); - } + // Short UUID, need to look up full form + else { + Task loaded_task; + if (Context::getContext().tdb2.get(dep, loaded_task)) + if (removal) + task.removeDependency(loaded_task.get("uuid")); + else + task.addDependency(loaded_task.get("uuid")); + else + throw format("Dependency could not be set - task with UUID '{1}' does not exist.", dep); + } } // ID range - else if (dep.find ('-') != std::string::npos && - extractLongInteger (dep.substr (0, hyphen), lower) && - extractLongInteger (dep.substr (hyphen + 1), upper)) - { + else if (dep.find('-') != std::string::npos && + extractLongInteger(dep.substr(0, hyphen), lower) && + extractLongInteger(dep.substr(hyphen + 1), upper)) { for (long i = lower; i <= upper; i++) - if (removal) - task.removeDependency (i); - else - task.addDependency (i); + if (removal) + task.removeDependency(i); + else + task.addDependency(i); } // Simple ID - else if (extractLongInteger (dep, lower)) + else if (extractLongInteger(dep, lower)) if (removal) - task.removeDependency (lower); + task.removeDependency(lower); else - task.addDependency (lower); + task.addDependency(lower); else - throw format ("Invalid dependency value: '{1}'", dep); + throw format("Invalid dependency value: '{1}'", dep); } } diff --git a/src/columns/ColDepends.h b/src/columns/ColDepends.h index bf36c5b03..ef79b8f06 100644 --- a/src/columns/ColDepends.h +++ b/src/columns/ColDepends.h @@ -29,17 +29,16 @@ #include -class ColumnDepends : public ColumnTypeString -{ -public: - ColumnDepends (); +class ColumnDepends : public ColumnTypeString { + public: + ColumnDepends(); - void setStyle (const std::string&); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); - void modify (Task&, const std::string&); + void setStyle(const std::string&); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); + void modify(Task&, const std::string&); -private: + private: bool _hyphenate; }; diff --git a/src/columns/ColDescription.cpp b/src/columns/ColDescription.cpp index b36661ff7..460465cff 100644 --- a/src/columns/ColDescription.cpp +++ b/src/columns/ColDescription.cpp @@ -28,233 +28,188 @@ // cmake.h include header must come first #include -#include #include #include -#include #include +#include +#include #include #include //////////////////////////////////////////////////////////////////////////////// -ColumnDescription::ColumnDescription () -{ - _name = "description"; - _style = "combined"; - _label = "Description"; +ColumnDescription::ColumnDescription() { + _name = "description"; + _style = "combined"; + _label = "Description"; _modifiable = true; - _styles = {"combined", - "desc", - "oneline", - "truncated", - "count", - "truncated_count"}; + _styles = {"combined", "desc", "oneline", "truncated", "count", "truncated_count"}; - _dateformat = Context::getContext ().config.get ("dateformat.annotation"); - if (_dateformat == "") - _dateformat = Context::getContext ().config.get ("dateformat"); + _dateformat = Context::getContext().config.get("dateformat.annotation"); + if (_dateformat == "") _dateformat = Context::getContext().config.get("dateformat"); - std::string t = Datetime ().toString (_dateformat); - std::string d = "Move your clothes down on to the lower peg"; + std::string t = Datetime().toString(_dateformat); + std::string d = "Move your clothes down on to the lower peg"; std::string a1 = "Immediately before your lunch"; std::string a2 = "If you are playing in the match this afternoon"; std::string a3 = "Before you write your letter home"; std::string a4 = "If you're not getting your hair cut"; - _examples = {d + "\n " + t + ' ' + a1 - + "\n " + t + ' ' + a2 - + "\n " + t + ' ' + a3 - + "\n " + t + ' ' + a4, - d, - d + ' ' + t + ' ' + a1 - + ' ' + t + ' ' + a2 - + ' ' + t + ' ' + a3 - + ' ' + t + ' ' + a4, - d.substr (0, 20) + "...", - d + " [4]", - d.substr (0, 20) + "... [4]"}; + _examples = { + d + "\n " + t + ' ' + a1 + "\n " + t + ' ' + a2 + "\n " + t + ' ' + a3 + "\n " + t + ' ' + + a4, + d, + d + ' ' + t + ' ' + a1 + ' ' + t + ' ' + a2 + ' ' + t + ' ' + a3 + ' ' + t + ' ' + a4, + d.substr(0, 20) + "...", + d + " [4]", + d.substr(0, 20) + "... [4]"}; - _hyphenate = Context::getContext ().config.getBoolean ("hyphenate"); + _hyphenate = Context::getContext().config.getBoolean("hyphenate"); - _indent = Context::getContext ().config.getInteger ("indent.annotation"); + _indent = Context::getContext().config.getInteger("indent.annotation"); } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ - std::string description = task.get (_name); +void ColumnDescription::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { + std::string description = task.get(_name); // The text // // ... - if (_style == "default" || - _style == "combined") - { - minimum = longestWord (description); - maximum = utf8_width (description); + if (_style == "default" || _style == "combined") { + minimum = longestWord(description); + maximum = utf8_width(description); - if (task.annotation_count) - { - unsigned int min_anno = _indent + Datetime::length (_dateformat); - if (min_anno > minimum) - minimum = min_anno; + if (task.annotation_count) { + unsigned int min_anno = _indent + Datetime::length(_dateformat); + if (min_anno > minimum) minimum = min_anno; - for (auto& i : task.getAnnotations ()) - { - unsigned int len = min_anno + 1 + utf8_width (i.second); - if (len > maximum) - maximum = len; + for (auto& i : task.getAnnotations()) { + unsigned int len = min_anno + 1 + utf8_width(i.second); + if (len > maximum) maximum = len; } } } // Just the text - else if (_style == "desc") - { - maximum = utf8_width (description); - minimum = longestWord (description); + else if (_style == "desc") { + maximum = utf8_width(description); + minimum = longestWord(description); } // The text ... - else if (_style == "oneline") - { - minimum = longestWord (description); - maximum = utf8_width (description); + else if (_style == "oneline") { + minimum = longestWord(description); + maximum = utf8_width(description); - if (task.annotation_count) - { - auto min_anno = Datetime::length (_dateformat); - for (auto& i : task.getAnnotations ()) - maximum += min_anno + 1 + utf8_width (i.second); + if (task.annotation_count) { + auto min_anno = Datetime::length(_dateformat); + for (auto& i : task.getAnnotations()) maximum += min_anno + 1 + utf8_width(i.second); } } // The te... - else if (_style == "truncated") - { + else if (_style == "truncated") { minimum = 4; - maximum = utf8_width (description); + maximum = utf8_width(description); } // The text [2] - else if (_style == "count") - { + else if (_style == "count") { // + ' ' + '[' + + ']' - maximum = utf8_width (description) + 1 + 1 + format (task.annotation_count).length () + 1; - minimum = longestWord (description); + maximum = utf8_width(description) + 1 + 1 + format(task.annotation_count).length() + 1; + minimum = longestWord(description); } // The te... [2] - else if (_style == "truncated_count") - { + else if (_style == "truncated_count") { minimum = 4; - maximum = utf8_width (description) + 1 + 1 + format (task.annotation_count).length () + 1; + maximum = utf8_width(description) + 1 + 1 + format(task.annotation_count).length() + 1; } } //////////////////////////////////////////////////////////////////////////////// -void ColumnDescription::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - std::string description = task.get (_name); +void ColumnDescription::render(std::vector& lines, Task& task, int width, + Color& color) { + std::string description = task.get(_name); // This is a description // // ... - if (_style == "default" || - _style == "combined") - { - if (task.annotation_count) - { - for (const auto& i : task.getAnnotations ()) - { - Datetime dt (strtoll (i.first.substr (11).c_str (), nullptr, 10)); - description += '\n' + std::string (_indent, ' ') + dt.toString (_dateformat) + ' ' + i.second; + if (_style == "default" || _style == "combined") { + if (task.annotation_count) { + for (const auto& i : task.getAnnotations()) { + Datetime dt(strtoll(i.first.substr(11).c_str(), nullptr, 10)); + description += '\n' + std::string(_indent, ' ') + dt.toString(_dateformat) + ' ' + i.second; } } - std::vector raw; - wrapText (raw, description, width, _hyphenate); + std::vector raw; + wrapText(raw, description, width, _hyphenate); - for (const auto& i : raw) - renderStringLeft (lines, width, color, i); + for (const auto& i : raw) renderStringLeft(lines, width, color, i); } // This is a description - else if (_style == "desc") - { - std::vector raw; - wrapText (raw, description, width, _hyphenate); + else if (_style == "desc") { + std::vector raw; + wrapText(raw, description, width, _hyphenate); - for (const auto& i : raw) - renderStringLeft (lines, width, color, i); + for (const auto& i : raw) renderStringLeft(lines, width, color, i); } // This is a description ... - else if (_style == "oneline") - { - if (task.annotation_count) - { - for (const auto& i : task.getAnnotations ()) - { - Datetime dt (strtoll (i.first.substr (11).c_str (), nullptr, 10)); - description += ' ' + dt.toString (_dateformat) + ' ' + i.second; + else if (_style == "oneline") { + if (task.annotation_count) { + for (const auto& i : task.getAnnotations()) { + Datetime dt(strtoll(i.first.substr(11).c_str(), nullptr, 10)); + description += ' ' + dt.toString(_dateformat) + ' ' + i.second; } } - std::vector raw; - wrapText (raw, description, width, _hyphenate); + std::vector raw; + wrapText(raw, description, width, _hyphenate); - for (const auto& i : raw) - renderStringLeft (lines, width, color, i); + for (const auto& i : raw) renderStringLeft(lines, width, color, i); } // This is a des... - else if (_style == "truncated") - { - int len = utf8_width (description); + else if (_style == "truncated") { + int len = utf8_width(description); if (len > width) - renderStringLeft (lines, width, color, description.substr (0, width - 3) + "..."); + renderStringLeft(lines, width, color, description.substr(0, width - 3) + "..."); else - renderStringLeft (lines, width, color, description); + renderStringLeft(lines, width, color, description); } // This is a description [2] - else if (_style == "count") - { - if (task.annotation_count) - description += " [" + format (task.annotation_count) + ']'; + else if (_style == "count") { + if (task.annotation_count) description += " [" + format(task.annotation_count) + ']'; - std::vector raw; - wrapText (raw, description, width, _hyphenate); + std::vector raw; + wrapText(raw, description, width, _hyphenate); - for (const auto& i : raw) - renderStringLeft (lines, width, color, i); + for (const auto& i : raw) renderStringLeft(lines, width, color, i); } // This is a des... [2] - else if (_style == "truncated_count") - { - int len = utf8_width (description); + else if (_style == "truncated_count") { + int len = utf8_width(description); std::string annos_count; int len_annos = 0; - if (task.annotation_count) - { - annos_count = " [" + format (task.annotation_count) + ']'; - len_annos = utf8_width (annos_count); + if (task.annotation_count) { + annos_count = " [" + format(task.annotation_count) + ']'; + len_annos = utf8_width(annos_count); len += len_annos; } if (len > width) - renderStringLeft (lines, width, color, description.substr (0, width - len_annos - 3) + "..." + annos_count); + renderStringLeft(lines, width, color, + description.substr(0, width - len_annos - 3) + "..." + annos_count); else - renderStringLeft (lines, width, color, description + annos_count); + renderStringLeft(lines, width, color, description + annos_count); } } diff --git a/src/columns/ColDescription.h b/src/columns/ColDescription.h index 0e1edb177..0178316a5 100644 --- a/src/columns/ColDescription.h +++ b/src/columns/ColDescription.h @@ -29,14 +29,13 @@ #include -class ColumnDescription : public ColumnTypeString -{ -public: - ColumnDescription (); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnDescription : public ColumnTypeString { + public: + ColumnDescription(); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -private: + private: bool _hyphenate; std::string _dateformat; int _indent; diff --git a/src/columns/ColDue.cpp b/src/columns/ColDue.cpp index c1aee8ed3..40a23fab2 100644 --- a/src/columns/ColDue.cpp +++ b/src/columns/ColDue.cpp @@ -30,22 +30,19 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnDue::ColumnDue () -{ - _name = "due"; +ColumnDue::ColumnDue() { + _name = "due"; _modifiable = true; - _label = "Due"; + _label = "Due"; } //////////////////////////////////////////////////////////////////////////////// // Overriden so that style <----> label are linked. // Note that you can not determine which gets called first. -void ColumnDue::setStyle (const std::string& value) -{ - Column::setStyle (value); +void ColumnDue::setStyle(const std::string& value) { + Column::setStyle(value); - if (_style == "countdown" && _label == "Due") - _label = "Count"; + if (_style == "countdown" && _label == "Due") _label = "Count"; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColDue.h b/src/columns/ColDue.h index 754ee06c7..4a35f378f 100644 --- a/src/columns/ColDue.h +++ b/src/columns/ColDue.h @@ -29,11 +29,10 @@ #include -class ColumnDue : public ColumnTypeDate -{ -public: - ColumnDue (); - void setStyle (const std::string&); +class ColumnDue : public ColumnTypeDate { + public: + ColumnDue(); + void setStyle(const std::string&); }; #endif diff --git a/src/columns/ColEnd.cpp b/src/columns/ColEnd.cpp index baddb8670..7b838a149 100644 --- a/src/columns/ColEnd.cpp +++ b/src/columns/ColEnd.cpp @@ -30,9 +30,8 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnEnd::ColumnEnd () -{ - _name = "end"; +ColumnEnd::ColumnEnd() { + _name = "end"; _label = "Completed"; } diff --git a/src/columns/ColEnd.h b/src/columns/ColEnd.h index 387c16a43..4a19bbad6 100644 --- a/src/columns/ColEnd.h +++ b/src/columns/ColEnd.h @@ -29,10 +29,9 @@ #include -class ColumnEnd : public ColumnTypeDate -{ -public: - ColumnEnd (); +class ColumnEnd : public ColumnTypeDate { + public: + ColumnEnd(); }; #endif diff --git a/src/columns/ColEntry.cpp b/src/columns/ColEntry.cpp index 8068d710a..86670a1fd 100644 --- a/src/columns/ColEntry.cpp +++ b/src/columns/ColEntry.cpp @@ -30,23 +30,19 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnEntry::ColumnEntry () -{ - _name = "entry"; +ColumnEntry::ColumnEntry() { + _name = "entry"; _modifiable = true; - _label = "Added"; + _label = "Added"; } //////////////////////////////////////////////////////////////////////////////// // Overriden so that style <----> label are linked. // Note that you can not determine which gets called first. -void ColumnEntry::setStyle (const std::string& value) -{ - Column::setStyle (value); +void ColumnEntry::setStyle(const std::string& value) { + Column::setStyle(value); - if (_style == "age" && - _label == "Added") - _label = "Age"; + if (_style == "age" && _label == "Added") _label = "Age"; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColEntry.h b/src/columns/ColEntry.h index ce734d1ab..72ae2ff7e 100644 --- a/src/columns/ColEntry.h +++ b/src/columns/ColEntry.h @@ -29,11 +29,10 @@ #include -class ColumnEntry : public ColumnTypeDate -{ -public: - ColumnEntry (); - void setStyle (const std::string&); +class ColumnEntry : public ColumnTypeDate { + public: + ColumnEntry(); + void setStyle(const std::string&); }; #endif diff --git a/src/columns/ColID.cpp b/src/columns/ColID.cpp index 9f66406e8..69f572434 100644 --- a/src/columns/ColID.cpp +++ b/src/columns/ColID.cpp @@ -28,48 +28,47 @@ // cmake.h include header must come first #include -#include #include +#include //////////////////////////////////////////////////////////////////////////////// -ColumnID::ColumnID () -{ - _name = "id"; - _style = "number"; - _label = "ID"; +ColumnID::ColumnID() { + _name = "id"; + _style = "number"; + _label = "ID"; _modifiable = false; - _styles = {"number"}; - _examples = {"123"}; + _styles = {"number"}; + _examples = {"123"}; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnID::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnID::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { int length; - if (task.id < 10) length = 1; // Fast - else if (task.id < 100) length = 2; // Fast - else if (task.id < 1000) length = 3; // Fast - else if (task.id < 10000) length = 4; // Fast - else if (task.id < 100000) length = 5; // Fast - else length = 1 + (int) log10 ((double) task.id); // Slow + if (task.id < 10) + length = 1; // Fast + else if (task.id < 100) + length = 2; // Fast + else if (task.id < 1000) + length = 3; // Fast + else if (task.id < 10000) + length = 4; // Fast + else if (task.id < 100000) + length = 5; // Fast + else + length = 1 + (int)log10((double)task.id); // Slow minimum = maximum = length; } //////////////////////////////////////////////////////////////////////////////// -void ColumnID::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - // Completed and deleted tasks have no ID. +void ColumnID::render(std::vector& lines, Task& task, int width, Color& color) { + // Completed and deleted tasks have no ID. if (task.id) - renderInteger (lines, width, color, task.id); + renderInteger(lines, width, color, task.id); else - renderStringRight (lines, width, color, "-"); + renderStringRight(lines, width, color, "-"); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColID.h b/src/columns/ColID.h index fdec75db8..8279995a3 100644 --- a/src/columns/ColID.h +++ b/src/columns/ColID.h @@ -29,14 +29,13 @@ #include -class ColumnID : public ColumnTypeNumeric -{ -public: - ColumnID (); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnID : public ColumnTypeNumeric { + public: + ColumnID(); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -private: + private: }; #endif diff --git a/src/columns/ColIMask.cpp b/src/columns/ColIMask.cpp index d4c79e249..067e8b85c 100644 --- a/src/columns/ColIMask.cpp +++ b/src/columns/ColIMask.cpp @@ -31,34 +31,25 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnIMask::ColumnIMask () -{ - _name = "imask"; - _style = "number"; - _label = "Mask Index"; +ColumnIMask::ColumnIMask() { + _name = "imask"; + _style = "number"; + _label = "Mask Index"; _modifiable = false; - _styles = {"number"}; - _examples = {"12"}; + _styles = {"number"}; + _examples = {"12"}; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnIMask::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnIMask::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - minimum = maximum = task.get (_name).length (); + if (task.has(_name)) minimum = maximum = task.get(_name).length(); } //////////////////////////////////////////////////////////////////////////////// -void ColumnIMask::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - renderStringRight (lines, width, color, task.get (_name)); +void ColumnIMask::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) renderStringRight(lines, width, color, task.get(_name)); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColIMask.h b/src/columns/ColIMask.h index ccf5ec56b..7fdc4b07f 100644 --- a/src/columns/ColIMask.h +++ b/src/columns/ColIMask.h @@ -29,14 +29,13 @@ #include -class ColumnIMask : public ColumnTypeNumeric -{ -public: - ColumnIMask (); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnIMask : public ColumnTypeNumeric { + public: + ColumnIMask(); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -private: + private: }; #endif diff --git a/src/columns/ColLast.cpp b/src/columns/ColLast.cpp index abeaff93d..341f4dcee 100644 --- a/src/columns/ColLast.cpp +++ b/src/columns/ColLast.cpp @@ -31,34 +31,25 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnLast::ColumnLast () -{ - _name = "last"; - _style = "number"; - _label = "Last instance"; +ColumnLast::ColumnLast() { + _name = "last"; + _style = "number"; + _label = "Last instance"; _modifiable = false; - _styles = {"number"}; - _examples = {"12"}; + _styles = {"number"}; + _examples = {"12"}; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnLast::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnLast::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - minimum = maximum = task.get (_name).length (); + if (task.has(_name)) minimum = maximum = task.get(_name).length(); } //////////////////////////////////////////////////////////////////////////////// -void ColumnLast::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - renderStringRight (lines, width, color, task.get (_name)); +void ColumnLast::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) renderStringRight(lines, width, color, task.get(_name)); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColLast.h b/src/columns/ColLast.h index 54d17c782..8f24c291f 100644 --- a/src/columns/ColLast.h +++ b/src/columns/ColLast.h @@ -29,14 +29,13 @@ #include -class ColumnLast : public ColumnTypeNumeric -{ -public: - ColumnLast (); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnLast : public ColumnTypeNumeric { + public: + ColumnLast(); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -private: + private: }; #endif diff --git a/src/columns/ColMask.cpp b/src/columns/ColMask.cpp index 3be3418ca..ca5a52f92 100644 --- a/src/columns/ColMask.cpp +++ b/src/columns/ColMask.cpp @@ -31,34 +31,25 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnMask::ColumnMask () -{ - _name = "mask"; - _style = "default"; - _label = "Mask"; +ColumnMask::ColumnMask() { + _name = "mask"; + _style = "default"; + _label = "Mask"; _modifiable = false; - _styles = {"default"}; - _examples = {"++++---"}; + _styles = {"default"}; + _examples = {"++++---"}; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnMask::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnMask::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - minimum = maximum = task.get (_name).length (); + if (task.has(_name)) minimum = maximum = task.get(_name).length(); } //////////////////////////////////////////////////////////////////////////////// -void ColumnMask::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - renderStringLeft (lines, width, color, task.get (_name)); +void ColumnMask::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) renderStringLeft(lines, width, color, task.get(_name)); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColMask.h b/src/columns/ColMask.h index 9346e4dac..b7f28a9f4 100644 --- a/src/columns/ColMask.h +++ b/src/columns/ColMask.h @@ -29,14 +29,13 @@ #include -class ColumnMask : public ColumnTypeString -{ -public: - ColumnMask (); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnMask : public ColumnTypeString { + public: + ColumnMask(); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -private: + private: }; #endif diff --git a/src/columns/ColModified.cpp b/src/columns/ColModified.cpp index 658e4b80b..42091e88b 100644 --- a/src/columns/ColModified.cpp +++ b/src/columns/ColModified.cpp @@ -30,9 +30,8 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnModified::ColumnModified () -{ - _name = "modified"; +ColumnModified::ColumnModified() { + _name = "modified"; _label = "Modified"; } diff --git a/src/columns/ColModified.h b/src/columns/ColModified.h index 523d2f869..06d79b213 100644 --- a/src/columns/ColModified.h +++ b/src/columns/ColModified.h @@ -29,10 +29,9 @@ #include -class ColumnModified : public ColumnTypeDate -{ -public: - ColumnModified (); +class ColumnModified : public ColumnTypeDate { + public: + ColumnModified(); }; #endif diff --git a/src/columns/ColParent.cpp b/src/columns/ColParent.cpp index dfd63d4d6..e49315904 100644 --- a/src/columns/ColParent.cpp +++ b/src/columns/ColParent.cpp @@ -31,45 +31,37 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnParent::ColumnParent () -{ - _name = "parent"; - _style = "long"; - _label = "Parent task"; +ColumnParent::ColumnParent() { + _name = "parent"; + _style = "long"; + _label = "Parent task"; _modifiable = false; - _styles = {"long", "short"}; - _examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"}; + _styles = {"long", "short"}; + _examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"}; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnParent::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnParent::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { - if (_style == "default" || _style == "long") minimum = maximum = 36; - else if (_style == "short") minimum = maximum = 8; + if (task.has(_name)) { + if (_style == "default" || _style == "long") + minimum = maximum = 36; + else if (_style == "short") + minimum = maximum = 8; } } //////////////////////////////////////////////////////////////////////////////// -void ColumnParent::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { +void ColumnParent::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) { // f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default // f30cb9c3 short - if (_style == "default" || - _style == "long") - renderStringLeft (lines, width, color, task.get(_name)); + if (_style == "default" || _style == "long") + renderStringLeft(lines, width, color, task.get(_name)); else if (_style == "short") - renderStringLeft (lines, width, color, task.get (_name).substr (0, 8)); + renderStringLeft(lines, width, color, task.get(_name).substr(0, 8)); } } diff --git a/src/columns/ColParent.h b/src/columns/ColParent.h index 2fff8f45d..9332a729d 100644 --- a/src/columns/ColParent.h +++ b/src/columns/ColParent.h @@ -29,14 +29,13 @@ #include -class ColumnParent : public ColumnTypeString -{ -public: - ColumnParent (); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnParent : public ColumnTypeString { + public: + ColumnParent(); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -private: + private: }; #endif diff --git a/src/columns/ColProject.cpp b/src/columns/ColProject.cpp index e9444b00c..0454bb5bb 100644 --- a/src/columns/ColProject.cpp +++ b/src/columns/ColProject.cpp @@ -30,120 +30,91 @@ #include #include #include -#include -#include #include -#include +#include +#include #include +#include #include #include //////////////////////////////////////////////////////////////////////////////// -ColumnProject::ColumnProject () -{ - _name = "project"; - _style = "full"; - _label = "Project"; - _styles = {"full", "parent", "indented"}; - _examples = {"home.garden", - "home", - " home.garden"}; - _hyphenate = Context::getContext ().config.getBoolean ("hyphenate"); +ColumnProject::ColumnProject() { + _name = "project"; + _style = "full"; + _label = "Project"; + _styles = {"full", "parent", "indented"}; + _examples = {"home.garden", "home", " home.garden"}; + _hyphenate = Context::getContext().config.getBoolean("hyphenate"); } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnProject::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnProject::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { - std::string project = task.get (_name); + if (task.has(_name)) { + std::string project = task.get(_name); - if (_style == "parent") - { - auto period = project.find ('.'); - if (period != std::string::npos) - project = project.substr (0, period); - } - else if (_style == "indented") - { - project = indentProject (project, " ", '.'); + if (_style == "parent") { + auto period = project.find('.'); + if (period != std::string::npos) project = project.substr(0, period); + } else if (_style == "indented") { + project = indentProject(project, " ", '.'); } - minimum = longestWord (project); - maximum = utf8_width (project); + minimum = longestWord(project); + maximum = utf8_width(project); } } //////////////////////////////////////////////////////////////////////////////// -void ColumnProject::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { - std::string project = task.get (_name); - if (_style == "parent") - { - auto period = project.find ('.'); - if (period != std::string::npos) - project = project.substr (0, period); - } - else if (_style == "indented") - { - project = indentProject (project, " ", '.'); +void ColumnProject::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) { + std::string project = task.get(_name); + if (_style == "parent") { + auto period = project.find('.'); + if (period != std::string::npos) project = project.substr(0, period); + } else if (_style == "indented") { + project = indentProject(project, " ", '.'); } - std::vector raw; - wrapText (raw, project, width, _hyphenate); + std::vector raw; + wrapText(raw, project, width, _hyphenate); - for (const auto& i : raw) - renderStringLeft (lines, width, color, i); + for (const auto& i : raw) renderStringLeft(lines, width, color, i); } } //////////////////////////////////////////////////////////////////////////////// -void ColumnProject::modify (Task& task, const std::string& value) -{ +void ColumnProject::modify(Task& task, const std::string& value) { std::string label = " MODIFICATION "; // Only if it's a DOM ref, eval it first. - Lexer lexer (value); + Lexer lexer(value); std::string domRef; Lexer::Type type; - if (lexer.token (domRef, type) && - type == Lexer::Type::dom) - { - try - { + if (lexer.token(domRef, type) && type == Lexer::Type::dom) { + try { Eval e; - e.addSource (domSource); + e.addSource(domSource); Variant v; - e.evaluateInfixExpression (value, v); - task.set (_name, (std::string) v); - Context::getContext ().debug (label + _name + " <-- '" + (std::string) v + "' <-- '" + value + '\''); - } - catch (const std::string& e) - { + e.evaluateInfixExpression(value, v); + task.set(_name, (std::string)v); + Context::getContext().debug(label + _name + " <-- '" + (std::string)v + "' <-- '" + value + + '\''); + } catch (const std::string& e) { // If the expression failed because it didn't look like an expression, // simply store it as-is. - if (e == "The value is not an expression.") - { - task.set (_name, value); - Context::getContext ().debug (label + _name + " <-- '" + value + '\''); - } - else + if (e == "The value is not an expression.") { + task.set(_name, value); + Context::getContext().debug(label + _name + " <-- '" + value + '\''); + } else throw; } - } - else - { - task.set (_name, value); - Context::getContext ().debug (label + _name + " <-- '" + value + '\''); + } else { + task.set(_name, value); + Context::getContext().debug(label + _name + " <-- '" + value + '\''); } } diff --git a/src/columns/ColProject.h b/src/columns/ColProject.h index ab61584c5..ae470f3b9 100644 --- a/src/columns/ColProject.h +++ b/src/columns/ColProject.h @@ -29,15 +29,14 @@ #include -class ColumnProject : public ColumnTypeString -{ -public: - ColumnProject (); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); - void modify (Task&, const std::string&); +class ColumnProject : public ColumnTypeString { + public: + ColumnProject(); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); + void modify(Task&, const std::string&); -private: + private: bool _hyphenate; }; diff --git a/src/columns/ColRType.cpp b/src/columns/ColRType.cpp index bbe62b2c8..600e2201c 100644 --- a/src/columns/ColRType.cpp +++ b/src/columns/ColRType.cpp @@ -29,72 +29,60 @@ #include #include -#include #include +#include + #include //////////////////////////////////////////////////////////////////////////////// -ColumnRType::ColumnRType () -{ - _name = "rtype"; - _style = "default"; - _label = "Recurrence type"; +ColumnRType::ColumnRType() { + _name = "rtype"; + _style = "default"; + _label = "Recurrence type"; _modifiable = false; - _styles = {"default", "indicator"}; - _examples = {"periodic", "chained"}; + _styles = {"default", "indicator"}; + _examples = {"periodic", "chained"}; } //////////////////////////////////////////////////////////////////////////////// // Overriden so that style <----> label are linked. // Note that you can not determine which gets called first. -void ColumnRType::setStyle (const std::string& value) -{ - Column::setStyle (value); +void ColumnRType::setStyle(const std::string& value) { + Column::setStyle(value); if (_style == "indicator" && _label == "Recurrence type") - _label = _label.substr (0, Context::getContext ().config.get ("rtype.indicator").length ()); + _label = _label.substr(0, Context::getContext().config.get("rtype.indicator").length()); } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnRType::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnRType::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { + if (task.has(_name)) { if (_style == "default") - minimum = maximum = task.get (_name).length (); + minimum = maximum = task.get(_name).length(); else if (_style == "indicator") minimum = maximum = 1; } } //////////////////////////////////////////////////////////////////////////////// -void ColumnRType::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { +void ColumnRType::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) { if (_style == "default") - renderStringRight (lines, width, color, task.get (_name)); + renderStringRight(lines, width, color, task.get(_name)); - else if (_style == "indicator") - { - std::string value {" "}; - value[0] = toupper (task.get (_name)[0]); - renderStringRight (lines, width, color, value); + else if (_style == "indicator") { + std::string value{" "}; + value[0] = toupper(task.get(_name)[0]); + renderStringRight(lines, width, color, value); } } } //////////////////////////////////////////////////////////////////////////////// -bool ColumnRType::validate (const std::string& input) const -{ - return input == "periodic" || - input == "chained"; +bool ColumnRType::validate(const std::string& input) const { + return input == "periodic" || input == "chained"; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColRType.h b/src/columns/ColRType.h index d40eae126..dc7b86ffe 100644 --- a/src/columns/ColRType.h +++ b/src/columns/ColRType.h @@ -29,16 +29,15 @@ #include -class ColumnRType : public ColumnTypeString -{ -public: - ColumnRType (); - void setStyle (const std::string&); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); - bool validate (const std::string&) const; +class ColumnRType : public ColumnTypeString { + public: + ColumnRType(); + void setStyle(const std::string&); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); + bool validate(const std::string&) const; -private: + private: }; #endif diff --git a/src/columns/ColRecur.cpp b/src/columns/ColRecur.cpp index 319705a18..dc6f0fefc 100644 --- a/src/columns/ColRecur.cpp +++ b/src/columns/ColRecur.cpp @@ -31,100 +31,81 @@ #include #include #include -#include -#include #include -#include +#include +#include #include +#include #include //////////////////////////////////////////////////////////////////////////////// -ColumnRecur::ColumnRecur () -{ - _name = "recur"; - _style = "duration"; - _label = "Recur"; +ColumnRecur::ColumnRecur() { + _name = "recur"; + _style = "duration"; + _label = "Recur"; _modifiable = true; - _styles = {"duration", "indicator"}; - _examples = {"weekly", Context::getContext ().config.get ("recurrence.indicator")}; + _styles = {"duration", "indicator"}; + _examples = {"weekly", Context::getContext().config.get("recurrence.indicator")}; } //////////////////////////////////////////////////////////////////////////////// // Overriden so that style <----> label are linked. // Note that you can not determine which gets called first. -void ColumnRecur::setStyle (const std::string& value) -{ - Column::setStyle (value); +void ColumnRecur::setStyle(const std::string& value) { + Column::setStyle(value); if (_style == "indicator" && _label == "Recur") - _label = _label.substr (0, Context::getContext ().config.get ("recurrence.indicator").length ()); + _label = _label.substr(0, Context::getContext().config.get("recurrence.indicator").length()); } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnRecur::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnRecur::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { - if (_style == "default" || - _style == "duration") - { - minimum = maximum = Duration (task.get (_name)).formatISO ().length (); - } - else if (_style == "indicator") - { - minimum = maximum = utf8_width (Context::getContext ().config.get ("recurrence.indicator")); + if (task.has(_name)) { + if (_style == "default" || _style == "duration") { + minimum = maximum = Duration(task.get(_name)).formatISO().length(); + } else if (_style == "indicator") { + minimum = maximum = utf8_width(Context::getContext().config.get("recurrence.indicator")); } } } //////////////////////////////////////////////////////////////////////////////// -void ColumnRecur::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { - if (_style == "default" || - _style == "duration") - renderStringRight (lines, width, color, Duration (task.get (_name)).formatISO ()); +void ColumnRecur::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) { + if (_style == "default" || _style == "duration") + renderStringRight(lines, width, color, Duration(task.get(_name)).formatISO()); else if (_style == "indicator") - renderStringRight (lines, width, color, Context::getContext ().config.get ("recurrence.indicator")); + renderStringRight(lines, width, color, + Context::getContext().config.get("recurrence.indicator")); } } //////////////////////////////////////////////////////////////////////////////// // The duration is stored in raw form, but it must still be valid, // and therefore is parsed first. -void ColumnRecur::modify (Task& task, const std::string& value) -{ +void ColumnRecur::modify(Task& task, const std::string& value) { // Try to evaluate 'value'. It might work. Variant evaluatedValue; - try - { + try { Eval e; - e.addSource (domSource); - e.evaluateInfixExpression (value, evaluatedValue); + e.addSource(domSource); + e.evaluateInfixExpression(value, evaluatedValue); } - catch (...) - { - evaluatedValue = Variant (value); + catch (...) { + evaluatedValue = Variant(value); } - if (evaluatedValue.type () == Variant::type_duration) - { + if (evaluatedValue.type() == Variant::type_duration) { // Store the raw value, for 'recur'. std::string label = " MODIFICATION "; - Context::getContext ().debug (label + _name + " <-- '" + value + '\''); - task.set (_name, value); - } - else - throw format ("The duration value '{1}' is not supported.", value); + Context::getContext().debug(label + _name + " <-- '" + value + '\''); + task.set(_name, value); + } else + throw format("The duration value '{1}' is not supported.", value); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColRecur.h b/src/columns/ColRecur.h index e9560dd91..3de674aa9 100644 --- a/src/columns/ColRecur.h +++ b/src/columns/ColRecur.h @@ -31,16 +31,15 @@ // This is 'string', and not 'duration' to force the value to be stored as a // raw duration, so that it can be reevaluated every time. -class ColumnRecur : public ColumnTypeString -{ -public: - ColumnRecur (); - void setStyle (const std::string&); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); - void modify (Task&, const std::string&); +class ColumnRecur : public ColumnTypeString { + public: + ColumnRecur(); + void setStyle(const std::string&); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); + void modify(Task&, const std::string&); -private: + private: }; #endif diff --git a/src/columns/ColScheduled.cpp b/src/columns/ColScheduled.cpp index e20ae46ba..2a5ca9849 100644 --- a/src/columns/ColScheduled.cpp +++ b/src/columns/ColScheduled.cpp @@ -30,21 +30,18 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnScheduled::ColumnScheduled () -{ - _name = "scheduled"; +ColumnScheduled::ColumnScheduled() { + _name = "scheduled"; _label = "Scheduled"; } //////////////////////////////////////////////////////////////////////////////// // Overriden so that style <----> label are linked. // Note that you can not determine which gets called first. -void ColumnScheduled::setStyle (const std::string& value) -{ - Column::setStyle (value); +void ColumnScheduled::setStyle(const std::string& value) { + Column::setStyle(value); - if (_style == "countdown" && _label == "Scheduled") - _label = "Count"; + if (_style == "countdown" && _label == "Scheduled") _label = "Count"; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColScheduled.h b/src/columns/ColScheduled.h index 31beec4ca..c92440212 100644 --- a/src/columns/ColScheduled.h +++ b/src/columns/ColScheduled.h @@ -29,11 +29,10 @@ #include -class ColumnScheduled : public ColumnTypeDate -{ -public: - ColumnScheduled (); - void setStyle (const std::string&); +class ColumnScheduled : public ColumnTypeDate { + public: + ColumnScheduled(); + void setStyle(const std::string&); }; #endif diff --git a/src/columns/ColStart.cpp b/src/columns/ColStart.cpp index d9ee252a4..4a4dcc1a4 100644 --- a/src/columns/ColStart.cpp +++ b/src/columns/ColStart.cpp @@ -32,58 +32,46 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnStart::ColumnStart () -{ - _name = "start"; +ColumnStart::ColumnStart() { + _name = "start"; _label = "Started"; - _styles.push_back ("active"); - _examples.push_back (Context::getContext ().config.get ("active.indicator")); + _styles.push_back("active"); + _examples.push_back(Context::getContext().config.get("active.indicator")); } //////////////////////////////////////////////////////////////////////////////// // Overriden so that style <----> label are linked. // Note that you can not determine which gets called first. -void ColumnStart::setStyle (const std::string& value) -{ - Column::setStyle (value); +void ColumnStart::setStyle(const std::string& value) { + Column::setStyle(value); - if (_style == "active" && _label == "Started") - _label = "A"; + if (_style == "active" && _label == "Started") _label = "A"; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnStart::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnStart::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { + if (task.has(_name)) { if (_style == "active") - minimum = maximum = utf8_width (Context::getContext ().config.get ("active.indicator")); + minimum = maximum = utf8_width(Context::getContext().config.get("active.indicator")); else - ColumnTypeDate::measure (task, minimum, maximum); + ColumnTypeDate::measure(task, minimum, maximum); // TODO Throw on bad format. } } //////////////////////////////////////////////////////////////////////////////// -void ColumnStart::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { - if (_style == "active") - { - if (! task.has ("end")) - renderStringRight (lines, width, color, Context::getContext ().config.get ("active.indicator")); - } - else - ColumnTypeDate::render (lines, task, width, color); +void ColumnStart::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) { + if (_style == "active") { + if (!task.has("end")) + renderStringRight(lines, width, color, + Context::getContext().config.get("active.indicator")); + } else + ColumnTypeDate::render(lines, task, width, color); } } diff --git a/src/columns/ColStart.h b/src/columns/ColStart.h index 569b411f7..2196ccabd 100644 --- a/src/columns/ColStart.h +++ b/src/columns/ColStart.h @@ -29,13 +29,12 @@ #include -class ColumnStart : public ColumnTypeDate -{ -public: - ColumnStart (); - void setStyle (const std::string&); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnStart : public ColumnTypeDate { + public: + ColumnStart(); + void setStyle(const std::string&); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); }; #endif diff --git a/src/columns/ColStatus.cpp b/src/columns/ColStatus.cpp index b2b5e6137..1fed6d73f 100644 --- a/src/columns/ColStatus.cpp +++ b/src/columns/ColStatus.cpp @@ -32,81 +32,75 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnStatus::ColumnStatus () -{ - _name = "status"; - _style = "long"; - _label = "Status"; - _styles = {"long", "short"}; - _examples = {"Pending", - "P"}; +ColumnStatus::ColumnStatus() { + _name = "status"; + _style = "long"; + _label = "Status"; + _styles = {"long", "short"}; + _examples = {"Pending", "P"}; } //////////////////////////////////////////////////////////////////////////////// // Overriden so that style <----> label are linked. // Note that you can not determine which gets called first. -void ColumnStatus::setStyle (const std::string& value) -{ - Column::setStyle (value); +void ColumnStatus::setStyle(const std::string& value) { + Column::setStyle(value); - if (_style == "short" && _label == "Status") - _label = "St"; + if (_style == "short" && _label == "Status") _label = "St"; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnStatus::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ - Task::status status = task.getStatus (); +void ColumnStatus::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { + Task::status status = task.getStatus(); - if (_style == "default" || - _style == "long") - { + if (_style == "default" || _style == "long") { if (status == Task::pending) - minimum = maximum = utf8_width ("Pending"); + minimum = maximum = utf8_width("Pending"); else if (status == Task::deleted) - minimum = maximum = utf8_width ("Deleted"); + minimum = maximum = utf8_width("Deleted"); else if (status == Task::waiting) - minimum = maximum = utf8_width ("Waiting"); + minimum = maximum = utf8_width("Waiting"); else if (status == Task::completed) - minimum = maximum = utf8_width ("Completed"); + minimum = maximum = utf8_width("Completed"); else if (status == Task::recurring) - minimum = maximum = utf8_width ("Recurring"); - } - else if (_style == "short") + minimum = maximum = utf8_width("Recurring"); + } else if (_style == "short") minimum = maximum = 1; } //////////////////////////////////////////////////////////////////////////////// -void ColumnStatus::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - Task::status status = task.getStatus (); +void ColumnStatus::render(std::vector& lines, Task& task, int width, Color& color) { + Task::status status = task.getStatus(); std::string value; - if (_style == "default" || - _style == "long") - { - if (status == Task::pending) value = "Pending"; - else if (status == Task::completed) value = "Completed"; - else if (status == Task::deleted) value = "Deleted"; - else if (status == Task::waiting) value = "Waiting"; - else if (status == Task::recurring) value = "Recurring"; + if (_style == "default" || _style == "long") { + if (status == Task::pending) + value = "Pending"; + else if (status == Task::completed) + value = "Completed"; + else if (status == Task::deleted) + value = "Deleted"; + else if (status == Task::waiting) + value = "Waiting"; + else if (status == Task::recurring) + value = "Recurring"; } - else if (_style == "short") - { - if (status == Task::pending) value = "P"; - else if (status == Task::completed) value = "C"; - else if (status == Task::deleted) value = "D"; - else if (status == Task::waiting) value = "W"; - else if (status == Task::recurring) value = "R"; + else if (_style == "short") { + if (status == Task::pending) + value = "P"; + else if (status == Task::completed) + value = "C"; + else if (status == Task::deleted) + value = "D"; + else if (status == Task::waiting) + value = "W"; + else if (status == Task::recurring) + value = "R"; } - renderStringLeft (lines, width, color, value); + renderStringLeft(lines, width, color, value); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColStatus.h b/src/columns/ColStatus.h index 99472466b..9c6f2d9a4 100644 --- a/src/columns/ColStatus.h +++ b/src/columns/ColStatus.h @@ -29,15 +29,14 @@ #include -class ColumnStatus : public ColumnTypeString -{ -public: - ColumnStatus (); - void setStyle (const std::string&); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnStatus : public ColumnTypeString { + public: + ColumnStatus(); + void setStyle(const std::string&); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -private: + private: }; #endif diff --git a/src/columns/ColTags.cpp b/src/columns/ColTags.cpp index 8f69d71e8..50ccadf9e 100644 --- a/src/columns/ColTags.cpp +++ b/src/columns/ColTags.cpp @@ -28,154 +28,119 @@ // cmake.h include header must come first #include -#include #include #include -#include #include -#include +#include #include -#include #include +#include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -ColumnTags::ColumnTags () -{ - _name = "tags"; - _style = "list"; - _label = "Tags"; - _styles = {"list", "indicator", "count"}; - _examples = {"home @chore next", - Context::getContext ().config.get ("tag.indicator"), - "[2]"}; +ColumnTags::ColumnTags() { + _name = "tags"; + _style = "list"; + _label = "Tags"; + _styles = {"list", "indicator", "count"}; + _examples = {"home @chore next", Context::getContext().config.get("tag.indicator"), "[2]"}; _hyphenate = false; } //////////////////////////////////////////////////////////////////////////////// // Overriden so that style <----> label are linked. // Note that you can not determine which gets called first. -void ColumnTags::setStyle (const std::string& value) -{ - Column::setStyle (value); +void ColumnTags::setStyle(const std::string& value) { + Column::setStyle(value); - if (_style == "indicator" && - _label == "Tags") - _label = _label.substr (0, Context::getContext ().config.get ("tag.indicator").length ()); + if (_style == "indicator" && _label == "Tags") + _label = _label.substr(0, Context::getContext().config.get("tag.indicator").length()); - else if (_style == "count" && - _label == "Tags") + else if (_style == "count" && _label == "Tags") _label = "Tag"; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnTags::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { - if (_style == "indicator") - { - minimum = maximum = utf8_width (Context::getContext ().config.get ("tag.indicator")); - } - else if (_style == "count") - { + if (task.has(_name)) { + if (_style == "indicator") { + minimum = maximum = utf8_width(Context::getContext().config.get("tag.indicator")); + } else if (_style == "count") { minimum = maximum = 3; - } - else if (_style == "default" || - _style == "list") - { - std::string tags = task.get (_name); + } else if (_style == "default" || _style == "list") { + std::string tags = task.get(_name); // Find the widest tag. - if (tags.find (',') != std::string::npos) - { - auto all = split (tags, ','); - for (const auto& tag : all) - { - auto length = utf8_width (tag); - if (length > minimum) - minimum = length; + if (tags.find(',') != std::string::npos) { + auto all = split(tags, ','); + for (const auto& tag : all) { + auto length = utf8_width(tag); + if (length > minimum) minimum = length; } - maximum = utf8_width (tags); + maximum = utf8_width(tags); } // No need to split a single tag. else - minimum = maximum = utf8_width (tags); + minimum = maximum = utf8_width(tags); } } } //////////////////////////////////////////////////////////////////////////////// -void ColumnTags::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - auto all = task.getTags (); - if (all.size() > 0) - { - if (_style == "default" || - _style == "list") - { - if (all.size () > 1) - { - std::sort (all.begin (), all.end ()); - auto tags = join (" ", all); +void ColumnTags::render(std::vector& lines, Task& task, int width, Color& color) { + auto all = task.getTags(); + if (all.size() > 0) { + if (_style == "default" || _style == "list") { + if (all.size() > 1) { + std::sort(all.begin(), all.end()); + auto tags = join(" ", all); - all.clear (); - wrapText (all, tags, width, _hyphenate); + all.clear(); + wrapText(all, tags, width, _hyphenate); - for (const auto& i : all) - renderStringLeft (lines, width, color, i); - } - else - renderStringLeft (lines, width, color, all[0]); - } - else if (_style == "indicator") - { - renderStringRight (lines, width, color, Context::getContext ().config.get ("tag.indicator")); - } - else if (_style == "count") - { - renderStringRight (lines, width, color, '[' + format (static_cast (all.size ())) + ']'); + for (const auto& i : all) renderStringLeft(lines, width, color, i); + } else + renderStringLeft(lines, width, color, all[0]); + } else if (_style == "indicator") { + renderStringRight(lines, width, color, Context::getContext().config.get("tag.indicator")); + } else if (_style == "count") { + renderStringRight(lines, width, color, '[' + format(static_cast(all.size())) + ']'); } } } //////////////////////////////////////////////////////////////////////////////// -void ColumnTags::modify (Task& task, const std::string& value) -{ +void ColumnTags::modify(Task& task, const std::string& value) { std::string label = " MODIFICATION "; std::string commasep; - std::vector tags; + std::vector tags; // If it's a DOM ref, eval it first. - Lexer lexer (value); + Lexer lexer(value); std::string domRef; Lexer::Type type; - if (lexer.token (domRef, type) && - type == Lexer::Type::dom) - { + if (lexer.token(domRef, type) && type == Lexer::Type::dom) { Eval e; - e.addSource (domSource); + e.addSource(domSource); Variant v; - e.evaluateInfixExpression (value, v); - commasep = (std::string) v; + e.evaluateInfixExpression(value, v); + commasep = (std::string)v; } else { - commasep = (std::string) value; + commasep = (std::string)value; } - for (auto& tag : split (commasep, ',')) - { - tags.push_back ((std::string) tag); - Context::getContext ().debug (label + "tags <-- '" + tag + '\''); + for (auto& tag : split(commasep, ',')) { + tags.push_back((std::string)tag); + Context::getContext().debug(label + "tags <-- '" + tag + '\''); - feedback_special_tags (task, tag); + feedback_special_tags(task, tag); } task.setTags(tags); diff --git a/src/columns/ColTags.h b/src/columns/ColTags.h index 481b01bd7..86d58e335 100644 --- a/src/columns/ColTags.h +++ b/src/columns/ColTags.h @@ -29,16 +29,15 @@ #include -class ColumnTags : public ColumnTypeString -{ -public: - ColumnTags (); - void setStyle (const std::string&); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); - void modify (Task&, const std::string&); +class ColumnTags : public ColumnTypeString { + public: + ColumnTags(); + void setStyle(const std::string&); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); + void modify(Task&, const std::string&); -private: + private: bool _hyphenate; }; diff --git a/src/columns/ColTemplate.cpp b/src/columns/ColTemplate.cpp index 584d08d42..02f536b1c 100644 --- a/src/columns/ColTemplate.cpp +++ b/src/columns/ColTemplate.cpp @@ -31,45 +31,37 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnTemplate::ColumnTemplate () -{ - _name = "template"; - _style = "long"; - _label = "Template task"; +ColumnTemplate::ColumnTemplate() { + _name = "template"; + _style = "long"; + _label = "Template task"; _modifiable = false; - _styles = {"long", "short"}; - _examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"}; + _styles = {"long", "short"}; + _examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"}; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnTemplate::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnTemplate::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { - if (_style == "default" || _style == "long") minimum = maximum = 36; - else if (_style == "short") minimum = maximum = 8; + if (task.has(_name)) { + if (_style == "default" || _style == "long") + minimum = maximum = 36; + else if (_style == "short") + minimum = maximum = 8; } } //////////////////////////////////////////////////////////////////////////////// -void ColumnTemplate::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { +void ColumnTemplate::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) { // f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default // f30cb9c3 short - if (_style == "default" || - _style == "long") - renderStringLeft (lines, width, color, task.get(_name)); + if (_style == "default" || _style == "long") + renderStringLeft(lines, width, color, task.get(_name)); else if (_style == "short") - renderStringLeft (lines, width, color, task.get (_name).substr (0, 8)); + renderStringLeft(lines, width, color, task.get(_name).substr(0, 8)); } } diff --git a/src/columns/ColTemplate.h b/src/columns/ColTemplate.h index 88c4f88b6..991f42a37 100644 --- a/src/columns/ColTemplate.h +++ b/src/columns/ColTemplate.h @@ -29,14 +29,13 @@ #include -class ColumnTemplate : public ColumnTypeString -{ -public: - ColumnTemplate (); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnTemplate : public ColumnTypeString { + public: + ColumnTemplate(); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -private: + private: }; #endif diff --git a/src/columns/ColTypeDate.cpp b/src/columns/ColTypeDate.cpp index 7a882acb6..21597d34f 100644 --- a/src/columns/ColTypeDate.cpp +++ b/src/columns/ColTypeDate.cpp @@ -32,215 +32,165 @@ #include #include #include -#include #include +#include #include //////////////////////////////////////////////////////////////////////////////// -ColumnTypeDate::ColumnTypeDate () -{ - _name = ""; - _type = "date"; - _style = "formatted"; - _label = ""; - _styles = {"formatted", - "julian", - "epoch", - "iso", - "age", - "relative", - "remaining", - "countdown"}; +ColumnTypeDate::ColumnTypeDate() { + _name = ""; + _type = "date"; + _style = "formatted"; + _label = ""; + _styles = {"formatted", "julian", "epoch", "iso", "age", "relative", "remaining", "countdown"}; Datetime now; - now -= 125; // So that "age" is non-zero. - _examples = {now.toString (Context::getContext ().config.get ("dateformat")), - format (now.toJulian (), 13, 12), - now.toEpochString (), - now.toISO (), - Duration (Datetime () - now).formatVague (true), - '-' + Duration (Datetime () - now).formatVague (true), + now -= 125; // So that "age" is non-zero. + _examples = {now.toString(Context::getContext().config.get("dateformat")), + format(now.toJulian(), 13, 12), + now.toEpochString(), + now.toISO(), + Duration(Datetime() - now).formatVague(true), + '-' + Duration(Datetime() - now).formatVague(true), "", - Duration (Datetime () - now).format ()}; + Duration(Datetime() - now).format()}; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnTypeDate::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnTypeDate::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { - Datetime date (task.get_date (_name)); + if (task.has(_name)) { + Datetime date(task.get_date(_name)); - if (_style == "default" || - _style == "formatted") - { + if (_style == "default" || _style == "formatted") { // Determine the output date format, which uses a hierarchy of definitions. // rc.report..dateformat // rc.dateformat.report // rc.dateformat. - std::string format = Context::getContext ().config.get ("report." + _report + ".dateformat"); - if (format == "") - format = Context::getContext ().config.get ("dateformat.report"); - if (format == "") - format = Context::getContext ().config.get ("dateformat"); + std::string format = Context::getContext().config.get("report." + _report + ".dateformat"); + if (format == "") format = Context::getContext().config.get("dateformat.report"); + if (format == "") format = Context::getContext().config.get("dateformat"); - minimum = maximum = Datetime::length (format); - } - else if (_style == "countdown") - { + minimum = maximum = Datetime::length(format); + } else if (_style == "countdown") { Datetime now; - minimum = maximum = Duration (date - now).formatVague (true).length (); - } - else if (_style == "julian") - { - minimum = maximum = format (date.toJulian (), 13, 12).length (); - } - else if (_style == "epoch") - { - minimum = maximum = date.toEpochString ().length (); - } - else if (_style == "iso") - { - minimum = maximum = date.toISO ().length (); - } - else if (_style == "age") - { + minimum = maximum = Duration(date - now).formatVague(true).length(); + } else if (_style == "julian") { + minimum = maximum = format(date.toJulian(), 13, 12).length(); + } else if (_style == "epoch") { + minimum = maximum = date.toEpochString().length(); + } else if (_style == "iso") { + minimum = maximum = date.toISO().length(); + } else if (_style == "age") { Datetime now; if (now > date) - minimum = maximum = Duration (now - date).formatVague (true).length (); + minimum = maximum = Duration(now - date).formatVague(true).length(); else - minimum = maximum = Duration (date - now).formatVague (true).length () + 1; - } - else if (_style == "relative") - { + minimum = maximum = Duration(date - now).formatVague(true).length() + 1; + } else if (_style == "relative") { Datetime now; if (now < date) - minimum = maximum = Duration (date - now).formatVague (true).length (); + minimum = maximum = Duration(date - now).formatVague(true).length(); else - minimum = maximum = Duration (now - date).formatVague (true).length () + 1; - } - else if (_style == "remaining") - { + minimum = maximum = Duration(now - date).formatVague(true).length() + 1; + } else if (_style == "remaining") { Datetime now; - if (date > now) - minimum = maximum = Duration (date - now).formatVague (true).length (); + if (date > now) minimum = maximum = Duration(date - now).formatVague(true).length(); } } } //////////////////////////////////////////////////////////////////////////////// -void ColumnTypeDate::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { - Datetime date (task.get_date (_name)); +void ColumnTypeDate::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) { + Datetime date(task.get_date(_name)); - if (_style == "default" || - _style == "formatted") - { + if (_style == "default" || _style == "formatted") { // Determine the output date format, which uses a hierarchy of definitions. // rc.report..dateformat // rc.dateformat.report // rc.dateformat - std::string format = Context::getContext ().config.get ("report." + _report + ".dateformat"); - if (format == "") - { - format = Context::getContext ().config.get ("dateformat.report"); - if (format == "") - format = Context::getContext ().config.get ("dateformat"); + std::string format = Context::getContext().config.get("report." + _report + ".dateformat"); + if (format == "") { + format = Context::getContext().config.get("dateformat.report"); + if (format == "") format = Context::getContext().config.get("dateformat"); } - renderStringLeft (lines, width, color, date.toString (format)); - } - else if (_style == "countdown") - { + renderStringLeft(lines, width, color, date.toString(format)); + } else if (_style == "countdown") { Datetime now; - renderStringRight (lines, width, color, Duration (date - now).formatVague (true)); - } - else if (_style == "julian") - renderStringRight (lines, width, color, format (date.toJulian (), 13, 12)); + renderStringRight(lines, width, color, Duration(date - now).formatVague(true)); + } else if (_style == "julian") + renderStringRight(lines, width, color, format(date.toJulian(), 13, 12)); else if (_style == "epoch") - renderStringRight (lines, width, color, date.toEpochString ()); + renderStringRight(lines, width, color, date.toEpochString()); else if (_style == "iso") - renderStringLeft (lines, width, color, date.toISO ()); + renderStringLeft(lines, width, color, date.toISO()); - else if (_style == "age") - { + else if (_style == "age") { Datetime now; if (now > date) - renderStringRight (lines, width, color, Duration (now - date).formatVague (true)); + renderStringRight(lines, width, color, Duration(now - date).formatVague(true)); else - renderStringRight (lines, width, color, '-' + Duration (date - now).formatVague (true)); - } - else if (_style == "relative") - { + renderStringRight(lines, width, color, '-' + Duration(date - now).formatVague(true)); + } else if (_style == "relative") { Datetime now; if (now < date) - renderStringRight (lines, width, color, Duration (date - now).formatVague (true)); + renderStringRight(lines, width, color, Duration(date - now).formatVague(true)); else - renderStringRight (lines, width, color, '-' + Duration (now - date).formatVague (true)); + renderStringRight(lines, width, color, '-' + Duration(now - date).formatVague(true)); } - else if (_style == "remaining") - { + else if (_style == "remaining") { Datetime now; if (date > now) - renderStringRight (lines, width, color, Duration (date - now).formatVague (true)); + renderStringRight(lines, width, color, Duration(date - now).formatVague(true)); } } } //////////////////////////////////////////////////////////////////////////////// -bool ColumnTypeDate::validate (const std::string& input) const -{ - return input.length () ? true : false; +bool ColumnTypeDate::validate(const std::string& input) const { + return input.length() ? true : false; } //////////////////////////////////////////////////////////////////////////////// -void ColumnTypeDate::modify (Task& task, const std::string& value) -{ +void ColumnTypeDate::modify(Task& task, const std::string& value) { // Try to evaluate 'value'. It might work. Variant evaluatedValue; - try - { + try { Eval e; - e.addSource (domSource); - e.evaluateInfixExpression (value, evaluatedValue); + e.addSource(domSource); + e.evaluateInfixExpression(value, evaluatedValue); } - catch (...) - { - evaluatedValue = Variant (value); + catch (...) { + evaluatedValue = Variant(value); } // If v is duration, we need to convert it to date (and implicitly add now), // else store as date. std::string label = " MODIFICATION "; - if (evaluatedValue.type () == Variant::type_duration) - { - Context::getContext ().debug (label + _name + " <-- '" + format ("{1}", format (evaluatedValue.get_duration ())) + "' <-- '" + (std::string) evaluatedValue + "' <-- '" + value + '\''); - evaluatedValue.cast (Variant::type_date); - } - else - { - evaluatedValue.cast (Variant::type_date); - Context::getContext ().debug (label + _name + " <-- '" + format ("{1}", evaluatedValue.get_date ()) + "' <-- '" + (std::string) evaluatedValue + "' <-- '" + value + '\''); + if (evaluatedValue.type() == Variant::type_duration) { + Context::getContext().debug(label + _name + " <-- '" + + format("{1}", format(evaluatedValue.get_duration())) + "' <-- '" + + (std::string)evaluatedValue + "' <-- '" + value + '\''); + evaluatedValue.cast(Variant::type_date); + } else { + evaluatedValue.cast(Variant::type_date); + Context::getContext().debug(label + _name + " <-- '" + + format("{1}", evaluatedValue.get_date()) + "' <-- '" + + (std::string)evaluatedValue + "' <-- '" + value + '\''); } // If a date doesn't parse (2/29/2014) then it evaluates to zero. - if (value != "" && - evaluatedValue.get_date () == 0) - throw format ("'{1}' is not a valid date in the '{2}' format.", value, Variant::dateFormat); + if (value != "" && evaluatedValue.get_date() == 0) + throw format("'{1}' is not a valid date in the '{2}' format.", value, Variant::dateFormat); - task.set (_name, evaluatedValue.get_date ()); + task.set(_name, evaluatedValue.get_date()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColTypeDate.h b/src/columns/ColTypeDate.h index 5d5a9f7b9..dc4bc9aea 100644 --- a/src/columns/ColTypeDate.h +++ b/src/columns/ColTypeDate.h @@ -27,20 +27,20 @@ #ifndef INCLUDED_COLTYPEDATE #define INCLUDED_COLTYPEDATE -#include -#include -#include #include +#include #include -class ColumnTypeDate : public Column -{ -public: - ColumnTypeDate (); - virtual void measure (Task&, unsigned int&, unsigned int&); - virtual void render (std::vector &, Task&, int, Color&); - virtual bool validate (const std::string&) const; - virtual void modify (Task&, const std::string&); +#include +#include + +class ColumnTypeDate : public Column { + public: + ColumnTypeDate(); + virtual void measure(Task&, unsigned int&, unsigned int&); + virtual void render(std::vector&, Task&, int, Color&); + virtual bool validate(const std::string&) const; + virtual void modify(Task&, const std::string&); }; #endif diff --git a/src/columns/ColTypeDuration.cpp b/src/columns/ColTypeDuration.cpp index 8a2182e58..34ae53db1 100644 --- a/src/columns/ColTypeDuration.cpp +++ b/src/columns/ColTypeDuration.cpp @@ -30,50 +30,42 @@ #include #include #include -#include #include +#include #include //////////////////////////////////////////////////////////////////////////////// -ColumnTypeDuration::ColumnTypeDuration () -{ - _type = "duration"; +ColumnTypeDuration::ColumnTypeDuration() { _type = "duration"; } + +//////////////////////////////////////////////////////////////////////////////// +bool ColumnTypeDuration::validate(const std::string& input) const { + return input.length() ? true : false; } //////////////////////////////////////////////////////////////////////////////// -bool ColumnTypeDuration::validate (const std::string& input) const -{ - return input.length () ? true : false; -} - -//////////////////////////////////////////////////////////////////////////////// -void ColumnTypeDuration::modify (Task& task, const std::string& value) -{ +void ColumnTypeDuration::modify(Task& task, const std::string& value) { // Try to evaluate 'value'. It might work. Variant evaluatedValue; - try - { + try { Eval e; - e.addSource (domSource); - e.evaluateInfixExpression (value, evaluatedValue); + e.addSource(domSource); + e.evaluateInfixExpression(value, evaluatedValue); } - catch (...) - { - evaluatedValue = Variant (value); + catch (...) { + evaluatedValue = Variant(value); } // The duration is stored in raw form, but it must still be valid, // and therefore is parsed first. 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 + " <-- '" + value + '\''); - task.set (_name, evaluatedValue); - } - else - throw format ("The duration value '{1}' is not supported.", value); + Context::getContext().debug(label + _name + " <-- " + (std::string)evaluatedValue + " <-- '" + + value + '\''); + task.set(_name, evaluatedValue); + } else + throw format("The duration value '{1}' is not supported.", value); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColTypeDuration.h b/src/columns/ColTypeDuration.h index 6ad3b1753..3d2f9544b 100644 --- a/src/columns/ColTypeDuration.h +++ b/src/columns/ColTypeDuration.h @@ -27,16 +27,16 @@ #ifndef INCLUDED_COLTYPEDURATION #define INCLUDED_COLTYPEDURATION -#include #include #include -class ColumnTypeDuration : public Column -{ -public: - ColumnTypeDuration (); - virtual bool validate (const std::string&) const; - virtual void modify (Task&, const std::string&); +#include + +class ColumnTypeDuration : public Column { + public: + ColumnTypeDuration(); + virtual bool validate(const std::string&) const; + virtual void modify(Task&, const std::string&); }; #endif diff --git a/src/columns/ColTypeNumeric.cpp b/src/columns/ColTypeNumeric.cpp index 5f4dd2d4f..52cc753a7 100644 --- a/src/columns/ColTypeNumeric.cpp +++ b/src/columns/ColTypeNumeric.cpp @@ -30,45 +30,38 @@ #include #include #include -#include #include +#include #include //////////////////////////////////////////////////////////////////////////////// -ColumnTypeNumeric::ColumnTypeNumeric () -{ - _type = "numeric"; +ColumnTypeNumeric::ColumnTypeNumeric() { _type = "numeric"; } + +//////////////////////////////////////////////////////////////////////////////// +bool ColumnTypeNumeric::validate(const std::string& input) const { + return input.length() ? true : false; } //////////////////////////////////////////////////////////////////////////////// -bool ColumnTypeNumeric::validate (const std::string& input) const -{ - return input.length () ? true : false; -} - -//////////////////////////////////////////////////////////////////////////////// -void ColumnTypeNumeric::modify (Task& task, const std::string& value) -{ +void ColumnTypeNumeric::modify(Task& task, const std::string& value) { // Try to evaluate 'value'. It might work. Variant evaluatedValue; - try - { + try { Eval e; - e.addSource (domSource); - e.evaluateInfixExpression (value, evaluatedValue); + e.addSource(domSource); + e.evaluateInfixExpression(value, evaluatedValue); } - catch (...) - { - evaluatedValue = Variant (value); + catch (...) { + evaluatedValue = Variant(value); } std::string label = " MODIFICATION "; - Context::getContext ().debug (label + _name + " <-- '" + evaluatedValue.get_string () + "' <-- '" + value + '\''); + Context::getContext().debug(label + _name + " <-- '" + evaluatedValue.get_string() + "' <-- '" + + value + '\''); // Convert the value of the expression to the correct type if needed - switch (evaluatedValue.type ()) - { + switch (evaluatedValue.type()) { // Expected variants - no conversion case Variant::type_integer: case Variant::type_real: @@ -76,16 +69,16 @@ void ColumnTypeNumeric::modify (Task& task, const std::string& value) // Convertible variants - convert to int case Variant::type_date: case Variant::type_duration: - evaluatedValue.cast (Variant::type_integer); + evaluatedValue.cast(Variant::type_integer); break; // Non-convertible variants case Variant::type_string: - throw format ("The value '{1}' is not a valid numeric value.", evaluatedValue.get_string ()); + throw format("The value '{1}' is not a valid numeric value.", evaluatedValue.get_string()); default: - throw format ("Unexpected variant type: '{1}'", evaluatedValue.type ()); + throw format("Unexpected variant type: '{1}'", evaluatedValue.type()); } - task.set (_name, evaluatedValue); + task.set(_name, evaluatedValue); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColTypeNumeric.h b/src/columns/ColTypeNumeric.h index e530d51f2..edd6508e4 100644 --- a/src/columns/ColTypeNumeric.h +++ b/src/columns/ColTypeNumeric.h @@ -27,16 +27,16 @@ #ifndef INCLUDED_COLTYPENUMERIC #define INCLUDED_COLTYPENUMERIC -#include #include #include -class ColumnTypeNumeric : public Column -{ -public: - ColumnTypeNumeric (); - virtual bool validate (const std::string&) const; - virtual void modify (Task&, const std::string&); +#include + +class ColumnTypeNumeric : public Column { + public: + ColumnTypeNumeric(); + virtual bool validate(const std::string&) const; + virtual void modify(Task&, const std::string&); }; #endif diff --git a/src/columns/ColTypeString.cpp b/src/columns/ColTypeString.cpp index cf71a42dd..538e9057f 100644 --- a/src/columns/ColTypeString.cpp +++ b/src/columns/ColTypeString.cpp @@ -30,64 +30,51 @@ #include #include #include -#include #include +#include #include -#define STRING_INVALID_MOD "The '{1}' attribute does not allow a value of '{2}'." +#define STRING_INVALID_MOD "The '{1}' attribute does not allow a value of '{2}'." //////////////////////////////////////////////////////////////////////////////// -ColumnTypeString::ColumnTypeString () -{ - _type = "string"; +ColumnTypeString::ColumnTypeString() { _type = "string"; } + +//////////////////////////////////////////////////////////////////////////////// +bool ColumnTypeString::validate(const std::string& input) const { + return input.length() ? true : false; } //////////////////////////////////////////////////////////////////////////////// -bool ColumnTypeString::validate (const std::string& input) const -{ - return input.length () ? true : false; -} - -//////////////////////////////////////////////////////////////////////////////// -void ColumnTypeString::modify (Task& task, const std::string& value) -{ +void ColumnTypeString::modify(Task& task, const std::string& value) { std::string label = " MODIFICATION "; // Only if it's a DOM ref, eval it first. - Lexer lexer (value); + Lexer lexer(value); std::string domRef; Lexer::Type type; - if (lexer.token (domRef, type) && - type == Lexer::Type::dom && + if (lexer.token(domRef, type) && type == Lexer::Type::dom && // Ensure 'value' contains only the DOM reference and no other tokens // The lexer.token returns false for end-of-string. // This works as long as all the DOM references we should support consist // only of a single token. - lexer.token (domRef, type) == false) - { + lexer.token(domRef, type) == false) { Eval e; - e.addSource (domSource); + e.addSource(domSource); Variant v; - e.evaluateInfixExpression (value, v); - std::string strValue = (std::string) v; - if (validate (strValue)) - { - task.set (_name, strValue); - Context::getContext ().debug (label + _name + " <-- '" + strValue + "' <-- '" + value + '\''); - } - else - throw format (STRING_INVALID_MOD, _name, value); - } - else - { - if (validate (value)) - { - task.set (_name, value); - Context::getContext ().debug (label + _name + " <-- '" + value + '\''); - } - else - throw format (STRING_INVALID_MOD, _name, value); + e.evaluateInfixExpression(value, v); + std::string strValue = (std::string)v; + if (validate(strValue)) { + task.set(_name, strValue); + Context::getContext().debug(label + _name + " <-- '" + strValue + "' <-- '" + value + '\''); + } else + throw format(STRING_INVALID_MOD, _name, value); + } else { + if (validate(value)) { + task.set(_name, value); + Context::getContext().debug(label + _name + " <-- '" + value + '\''); + } else + throw format(STRING_INVALID_MOD, _name, value); } } diff --git a/src/columns/ColTypeString.h b/src/columns/ColTypeString.h index 4bf5477fa..691a6916e 100644 --- a/src/columns/ColTypeString.h +++ b/src/columns/ColTypeString.h @@ -27,16 +27,16 @@ #ifndef INCLUDED_COLTYPESTRING #define INCLUDED_COLTYPESTRING -#include #include #include -class ColumnTypeString : public Column -{ -public: - ColumnTypeString (); - virtual bool validate (const std::string&) const; - virtual void modify (Task&, const std::string&); +#include + +class ColumnTypeString : public Column { + public: + ColumnTypeString(); + virtual bool validate(const std::string&) const; + virtual void modify(Task&, const std::string&); }; #endif diff --git a/src/columns/ColUDA.cpp b/src/columns/ColUDA.cpp index 95244562c..48686ba70 100644 --- a/src/columns/ColUDA.cpp +++ b/src/columns/ColUDA.cpp @@ -31,34 +31,30 @@ #include #include #include -#include #include -#include +#include #include +#include //////////////////////////////////////////////////////////////////////////////// -ColumnUDAString::ColumnUDAString () -{ - _name = ""; // Gets overwritten at runtime. - _style = "default"; - _label = ""; +ColumnUDAString::ColumnUDAString() { + _name = ""; // Gets overwritten at runtime. + _style = "default"; + _label = ""; _modifiable = true; - _uda = true; - _hyphenate = true; - _styles = {_style, "indicator"}; + _uda = true; + _hyphenate = true; + _styles = {_style, "indicator"}; } //////////////////////////////////////////////////////////////////////////////// -bool ColumnUDAString::validate (const std::string& value) const -{ +bool ColumnUDAString::validate(const std::string& value) const { // No restrictions. - if (_values.size () == 0) - return true; + if (_values.size() == 0) return true; // Look for exact match value. for (auto& i : _values) - if (i == value) - return true; + if (i == value) return true; // Fail if not found. return false; @@ -67,83 +63,61 @@ bool ColumnUDAString::validate (const std::string& value) const //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. // -void ColumnUDAString::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnUDAString::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { - if (_style == "default") - { - std::string value = task.get (_name); - if (value != "") - { - auto stripped = Color::strip (value); - maximum = longestLine (stripped); - minimum = longestWord (stripped); + if (task.has(_name)) { + if (_style == "default") { + std::string value = task.get(_name); + if (value != "") { + auto stripped = Color::strip(value); + maximum = longestLine(stripped); + minimum = longestWord(stripped); } - } - else if (_style == "indicator") - { - auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator"); - if (indicator == "") - indicator = "U"; + } else if (_style == "indicator") { + auto indicator = Context::getContext().config.get("uda." + _name + ".indicator"); + if (indicator == "") indicator = "U"; - minimum = maximum = utf8_width (indicator); + minimum = maximum = utf8_width(indicator); } } } //////////////////////////////////////////////////////////////////////////////// -void ColumnUDAString::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { - if (_style == "default") - { - std::string value = task.get (_name); - std::vector raw; - wrapText (raw, value, width, _hyphenate); +void ColumnUDAString::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) { + if (_style == "default") { + std::string value = task.get(_name); + std::vector raw; + wrapText(raw, value, width, _hyphenate); - for (auto& i : raw) - renderStringLeft (lines, width, color, i); - } - else if (_style == "indicator") - { - auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator"); - if (indicator == "") - indicator = "U"; + for (auto& i : raw) renderStringLeft(lines, width, color, i); + } else if (_style == "indicator") { + auto indicator = Context::getContext().config.get("uda." + _name + ".indicator"); + if (indicator == "") indicator = "U"; - renderStringRight (lines, width, color, indicator); + renderStringRight(lines, width, color, indicator); } } } //////////////////////////////////////////////////////////////////////////////// -ColumnUDANumeric::ColumnUDANumeric () -{ - _name = ""; - _type = "numeric"; - _style = "default"; - _label = ""; - _uda = true; - _styles = {_style, "indicator"}; +ColumnUDANumeric::ColumnUDANumeric() { + _name = ""; + _type = "numeric"; + _style = "default"; + _label = ""; + _uda = true; + _styles = {_style, "indicator"}; } //////////////////////////////////////////////////////////////////////////////// -bool ColumnUDANumeric::validate (const std::string& value) const -{ +bool ColumnUDANumeric::validate(const std::string& value) const { // No restrictions. - if (_values.size () == 0) - return true; + if (_values.size() == 0) return true; // Look for exact match value. for (auto& i : _values) - if (i == value) - return true; + if (i == value) return true; // Fail if not found. return false; @@ -152,75 +126,55 @@ bool ColumnUDANumeric::validate (const std::string& value) const //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. // -void ColumnUDANumeric::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnUDANumeric::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { - if (_style == "default") - { - auto value = task.get (_name); - if (value != "") - minimum = maximum = value.length (); - } - else if (_style == "indicator") - { - auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator"); - if (indicator == "") - indicator = "U"; + if (task.has(_name)) { + if (_style == "default") { + auto value = task.get(_name); + if (value != "") minimum = maximum = value.length(); + } else if (_style == "indicator") { + auto indicator = Context::getContext().config.get("uda." + _name + ".indicator"); + if (indicator == "") indicator = "U"; - minimum = maximum = utf8_width (indicator); + minimum = maximum = utf8_width(indicator); } } } //////////////////////////////////////////////////////////////////////////////// -void ColumnUDANumeric::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { - if (_style == "default") - { - auto value = task.get (_name); - renderStringRight (lines, width, color, value); - } - else if (_style == "indicator") - { - auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator"); - if (indicator == "") - indicator = "U"; +void ColumnUDANumeric::render(std::vector& lines, Task& task, int width, + Color& color) { + if (task.has(_name)) { + if (_style == "default") { + auto value = task.get(_name); + renderStringRight(lines, width, color, value); + } else if (_style == "indicator") { + auto indicator = Context::getContext().config.get("uda." + _name + ".indicator"); + if (indicator == "") indicator = "U"; - renderStringRight (lines, width, color, indicator); + renderStringRight(lines, width, color, indicator); } } } //////////////////////////////////////////////////////////////////////////////// -ColumnUDADate::ColumnUDADate () -{ - _name = ""; - _type = "date"; - _style = "default"; - _label = ""; - _uda = true; - _styles = {_style, "indicator"}; +ColumnUDADate::ColumnUDADate() { + _name = ""; + _type = "date"; + _style = "default"; + _label = ""; + _uda = true; + _styles = {_style, "indicator"}; } //////////////////////////////////////////////////////////////////////////////// -bool ColumnUDADate::validate (const std::string& value) const -{ +bool ColumnUDADate::validate(const std::string& value) const { // No restrictions. - if (_values.size () == 0) - return true; + if (_values.size() == 0) return true; // Look for exact match value. for (auto& i : _values) - if (i == value) - return true; + if (i == value) return true; // Fail if not found. return false; @@ -229,101 +183,77 @@ bool ColumnUDADate::validate (const std::string& value) const //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. // -void ColumnUDADate::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnUDADate::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { - if (_style == "default") - { - auto value = task.get (_name); - if (value != "") - { + if (task.has(_name)) { + if (_style == "default") { + auto value = task.get(_name); + if (value != "") { // Determine the output date format, which uses a hierarchy of definitions. // rc.report..dateformat // rc.dateformat.report // rc.dateformat - Datetime date (strtoll (value.c_str (), nullptr, 10)); - auto format = Context::getContext ().config.get ("report." + _report + ".dateformat"); - if (format == "") - format = Context::getContext ().config.get ("dateformat.report"); - if (format == "") - format = Context::getContext ().config.get ("dateformat"); + Datetime date(strtoll(value.c_str(), nullptr, 10)); + auto format = Context::getContext().config.get("report." + _report + ".dateformat"); + if (format == "") format = Context::getContext().config.get("dateformat.report"); + if (format == "") format = Context::getContext().config.get("dateformat"); - minimum = maximum = Datetime::length (format); + minimum = maximum = Datetime::length(format); } - } - else if (_style == "indicator") - { - auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator"); - if (indicator == "") - indicator = "U"; + } else if (_style == "indicator") { + auto indicator = Context::getContext().config.get("uda." + _name + ".indicator"); + if (indicator == "") indicator = "U"; - minimum = maximum = utf8_width (indicator); + minimum = maximum = utf8_width(indicator); } } } //////////////////////////////////////////////////////////////////////////////// -void ColumnUDADate::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { - if (_style == "default") - { - auto value = task.get (_name); +void ColumnUDADate::render(std::vector& lines, Task& task, int width, Color& color) { + if (task.has(_name)) { + if (_style == "default") { + auto value = task.get(_name); // Determine the output date format, which uses a hierarchy of definitions. // rc.report..dateformat // rc.dateformat.report // rc.dateformat. - auto format = Context::getContext ().config.get ("report." + _report + ".dateformat"); - if (format == "") - { - format = Context::getContext ().config.get ("dateformat.report"); - if (format == "") - format = Context::getContext ().config.get ("dateformat"); + auto format = Context::getContext().config.get("report." + _report + ".dateformat"); + if (format == "") { + format = Context::getContext().config.get("dateformat.report"); + if (format == "") format = Context::getContext().config.get("dateformat"); } - renderStringLeft (lines, width, color, Datetime (strtoll (value.c_str (), nullptr, 10)).toString (format)); - } - else if (_style == "indicator") - { - auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator"); - if (indicator == "") - indicator = "U"; + renderStringLeft(lines, width, color, + Datetime(strtoll(value.c_str(), nullptr, 10)).toString(format)); + } else if (_style == "indicator") { + auto indicator = Context::getContext().config.get("uda." + _name + ".indicator"); + if (indicator == "") indicator = "U"; - renderStringRight (lines, width, color, indicator); + renderStringRight(lines, width, color, indicator); } } } //////////////////////////////////////////////////////////////////////////////// -ColumnUDADuration::ColumnUDADuration () -{ - _name = ""; - _type = "duration"; - _style = "default"; - _label = ""; - _uda = true; - _styles = {_style, "indicator"}; +ColumnUDADuration::ColumnUDADuration() { + _name = ""; + _type = "duration"; + _style = "default"; + _label = ""; + _uda = true; + _styles = {_style, "indicator"}; } //////////////////////////////////////////////////////////////////////////////// -bool ColumnUDADuration::validate (const std::string& value) const -{ +bool ColumnUDADuration::validate(const std::string& value) const { // No restrictions. - if (_values.size () == 0) - return true; + if (_values.size() == 0) return true; // Look for exact match value. for (auto& i : _values) - if (i == value) - return true; + if (i == value) return true; // Fail if not found. return false; @@ -332,54 +262,36 @@ bool ColumnUDADuration::validate (const std::string& value) const //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. // -void ColumnUDADuration::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnUDADuration::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { minimum = maximum = 0; - if (task.has (_name)) - { - if (_style == "default") - { - auto value = task.get (_name); - if (value != "") - minimum = maximum = Duration (value).formatISO ().length (); - } - else if (_style == "indicator") - { - if (task.has (_name)) - { - auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator"); - if (indicator == "") - indicator = "U"; + if (task.has(_name)) { + if (_style == "default") { + auto value = task.get(_name); + if (value != "") minimum = maximum = Duration(value).formatISO().length(); + } else if (_style == "indicator") { + if (task.has(_name)) { + auto indicator = Context::getContext().config.get("uda." + _name + ".indicator"); + if (indicator == "") indicator = "U"; - minimum = maximum = utf8_width (indicator); - } - else + minimum = maximum = utf8_width(indicator); + } else minimum = maximum = 0; } } } //////////////////////////////////////////////////////////////////////////////// -void ColumnUDADuration::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ - if (task.has (_name)) - { - if (_style == "default") - { - auto value = task.get (_name); - renderStringRight (lines, width, color, Duration (value).formatISO ()); - } - else if (_style == "indicator") - { - auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator"); - if (indicator == "") - indicator = "U"; +void ColumnUDADuration::render(std::vector& lines, Task& task, int width, + Color& color) { + if (task.has(_name)) { + if (_style == "default") { + auto value = task.get(_name); + renderStringRight(lines, width, color, Duration(value).formatISO()); + } else if (_style == "indicator") { + auto indicator = Context::getContext().config.get("uda." + _name + ".indicator"); + if (indicator == "") indicator = "U"; - renderStringRight (lines, width, color, indicator); + renderStringRight(lines, width, color, indicator); } } } diff --git a/src/columns/ColUDA.h b/src/columns/ColUDA.h index 6e21802a0..ce8aaef97 100644 --- a/src/columns/ColUDA.h +++ b/src/columns/ColUDA.h @@ -27,64 +27,60 @@ #ifndef INCLUDED_COLUDA #define INCLUDED_COLUDA -#include -#include #include #include +#include +#include //////////////////////////////////////////////////////////////////////////////// -class ColumnUDAString : public ColumnTypeString -{ -public: - ColumnUDAString (); - bool validate (const std::string&) const; - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnUDAString : public ColumnTypeString { + public: + ColumnUDAString(); + bool validate(const std::string&) const; + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -public: - std::vector _values; + public: + std::vector _values; -private: + private: bool _hyphenate; }; //////////////////////////////////////////////////////////////////////////////// -class ColumnUDANumeric : public ColumnTypeNumeric -{ -public: - ColumnUDANumeric (); - bool validate (const std::string&) const; - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnUDANumeric : public ColumnTypeNumeric { + public: + ColumnUDANumeric(); + bool validate(const std::string&) const; + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -public: - std::vector _values; + public: + std::vector _values; }; //////////////////////////////////////////////////////////////////////////////// -class ColumnUDADate : public ColumnTypeDate -{ -public: - ColumnUDADate (); - bool validate (const std::string&) const; - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnUDADate : public ColumnTypeDate { + public: + ColumnUDADate(); + bool validate(const std::string&) const; + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -public: - std::vector _values; + public: + std::vector _values; }; //////////////////////////////////////////////////////////////////////////////// -class ColumnUDADuration : public ColumnTypeDuration -{ -public: - ColumnUDADuration (); - bool validate (const std::string&) const; - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnUDADuration : public ColumnTypeDuration { + public: + ColumnUDADuration(); + bool validate(const std::string&) const; + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -public: - std::vector _values; + public: + std::vector _values; }; #endif diff --git a/src/columns/ColUUID.cpp b/src/columns/ColUUID.cpp index ac7822dc0..3b81a4419 100644 --- a/src/columns/ColUUID.cpp +++ b/src/columns/ColUUID.cpp @@ -31,43 +31,37 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnUUID::ColumnUUID () -{ - _name = "uuid"; - _style = "long"; - _label = "UUID"; +ColumnUUID::ColumnUUID() { + _name = "uuid"; + _style = "long"; + _label = "UUID"; _modifiable = false; - _styles = {"long", "short"}; - _examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"}; + _styles = {"long", "short"}; + _examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"}; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnUUID::measure (Task&, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnUUID::measure(Task&, unsigned int& minimum, unsigned int& maximum) { // Mandatory attribute, no need to check the value. - if (_style == "default" || _style == "long") minimum = maximum = 36; - else if (_style == "short") minimum = maximum = 8; + if (_style == "default" || _style == "long") + minimum = maximum = 36; + else if (_style == "short") + minimum = maximum = 8; } //////////////////////////////////////////////////////////////////////////////// -void ColumnUUID::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ +void ColumnUUID::render(std::vector& lines, Task& task, int width, Color& color) { // No need to check the presence of UUID - all tasks have one. // f30cb9c3-3fc0-483f-bfb2-3bf134f00694 default // f30cb9c3 short - if (_style == "default" || - _style == "long") - renderStringLeft (lines, width, color, task.get (_name)); + if (_style == "default" || _style == "long") + renderStringLeft(lines, width, color, task.get(_name)); else if (_style == "short") - renderStringLeft (lines, width, color, task.get (_name).substr (0, 8)); + renderStringLeft(lines, width, color, task.get(_name).substr(0, 8)); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColUUID.h b/src/columns/ColUUID.h index 5adadcc56..7a8232a41 100644 --- a/src/columns/ColUUID.h +++ b/src/columns/ColUUID.h @@ -29,14 +29,13 @@ #include -class ColumnUUID : public ColumnTypeString -{ -public: - ColumnUUID (); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnUUID : public ColumnTypeString { + public: + ColumnUUID(); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -private: + private: }; #endif diff --git a/src/columns/ColUntil.cpp b/src/columns/ColUntil.cpp index fada3aea0..238c2f2df 100644 --- a/src/columns/ColUntil.cpp +++ b/src/columns/ColUntil.cpp @@ -30,9 +30,8 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnUntil::ColumnUntil () -{ - _name = "until"; +ColumnUntil::ColumnUntil() { + _name = "until"; _label = "Until"; } diff --git a/src/columns/ColUntil.h b/src/columns/ColUntil.h index 0161fda2e..5f180fba6 100644 --- a/src/columns/ColUntil.h +++ b/src/columns/ColUntil.h @@ -29,10 +29,9 @@ #include -class ColumnUntil : public ColumnTypeDate -{ -public: - ColumnUntil (); +class ColumnUntil : public ColumnTypeDate { + public: + ColumnUntil(); }; #endif diff --git a/src/columns/ColUrgency.cpp b/src/columns/ColUrgency.cpp index cc19b03d3..d202a7d91 100644 --- a/src/columns/ColUrgency.cpp +++ b/src/columns/ColUrgency.cpp @@ -31,39 +31,32 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnUrgency::ColumnUrgency () -{ - _name = "urgency"; - _style = "real"; - _label = "Urgency"; +ColumnUrgency::ColumnUrgency() { + _name = "urgency"; + _style = "real"; + _label = "Urgency"; _modifiable = false; - _styles = {"real", "integer"}; - _examples = {"4.6", "4"}; + _styles = {"real", "integer"}; + _examples = {"4.6", "4"}; } //////////////////////////////////////////////////////////////////////////////// // Set the minimum and maximum widths for the value. -void ColumnUrgency::measure (Task& task, unsigned int& minimum, unsigned int& maximum) -{ +void ColumnUrgency::measure(Task& task, unsigned int& minimum, unsigned int& maximum) { if (_style == "default" || _style == "real") - minimum = maximum = format (task.urgency (), 4, 3).length (); + minimum = maximum = format(task.urgency(), 4, 3).length(); else if (_style == "integer") - minimum = maximum = format ((int)task.urgency ()).length (); + minimum = maximum = format((int)task.urgency()).length(); } //////////////////////////////////////////////////////////////////////////////// -void ColumnUrgency::render ( - std::vector & lines, - Task& task, - int width, - Color& color) -{ +void ColumnUrgency::render(std::vector& lines, Task& task, int width, Color& color) { if (_style == "default" || _style == "real") - renderDouble (lines, width, color, task.urgency ()); + renderDouble(lines, width, color, task.urgency()); else if (_style == "integer") - renderInteger (lines, width, color, static_cast (task.urgency ())); + renderInteger(lines, width, color, static_cast(task.urgency())); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColUrgency.h b/src/columns/ColUrgency.h index 918334cd7..c4c31ab0d 100644 --- a/src/columns/ColUrgency.h +++ b/src/columns/ColUrgency.h @@ -29,14 +29,13 @@ #include -class ColumnUrgency : public ColumnTypeNumeric -{ -public: - ColumnUrgency (); - void measure (Task&, unsigned int&, unsigned int&); - void render (std::vector &, Task&, int, Color&); +class ColumnUrgency : public ColumnTypeNumeric { + public: + ColumnUrgency(); + void measure(Task&, unsigned int&, unsigned int&); + void render(std::vector&, Task&, int, Color&); -private: + private: }; #endif diff --git a/src/columns/ColWait.cpp b/src/columns/ColWait.cpp index cd7b1633b..1f2c97e83 100644 --- a/src/columns/ColWait.cpp +++ b/src/columns/ColWait.cpp @@ -30,9 +30,8 @@ #include //////////////////////////////////////////////////////////////////////////////// -ColumnWait::ColumnWait () -{ - _name = "wait"; +ColumnWait::ColumnWait() { + _name = "wait"; _label = "Wait"; } diff --git a/src/columns/ColWait.h b/src/columns/ColWait.h index f5a848b25..7c4b40435 100644 --- a/src/columns/ColWait.h +++ b/src/columns/ColWait.h @@ -29,10 +29,9 @@ #include -class ColumnWait : public ColumnTypeDate -{ -public: - ColumnWait (); +class ColumnWait : public ColumnTypeDate { + public: + ColumnWait(); }; #endif diff --git a/src/columns/Column.cpp b/src/columns/Column.cpp index 5620f6c65..f9231af1a 100644 --- a/src/columns/Column.cpp +++ b/src/columns/Column.cpp @@ -27,10 +27,6 @@ #include // cmake.h include header must come first -#include -#include -#include -#include #include #include #include @@ -43,294 +39,283 @@ #include #include #include -#include #include +#include #include #include #include #include #include +#include +#include #include #include -#include -#include #include -#include +#include +#include #include +#include + +#include +#include //////////////////////////////////////////////////////////////////////////////// // Supports the complete column definition: // // [.] // -Column* Column::factory (const std::string& name, const std::string& report) -{ +Column* Column::factory(const std::string& name, const std::string& report) { // Decompose name into type and style. - auto dot = name.find ('.'); + auto dot = name.find('.'); std::string column_name; std::string column_style; - if (dot != std::string::npos) - { - column_name = name.substr (0, dot); - column_style = name.substr (dot + 1); - } - else - { + if (dot != std::string::npos) { + column_name = name.substr(0, dot); + column_style = name.substr(dot + 1); + } else { column_name = name; column_style = "default"; } Column* c; - if (column_name == "depends") c = new ColumnDepends (); - else if (column_name == "description") c = new ColumnDescription (); - else if (column_name == "due") c = new ColumnDue (); - else if (column_name == "end") c = new ColumnEnd (); - else if (column_name == "entry") c = new ColumnEntry (); - else if (column_name == "id") c = new ColumnID (); - else if (column_name == "imask") c = new ColumnIMask (); - else if (column_name == "last") c = new ColumnLast (); - else if (column_name == "mask") c = new ColumnMask (); - else if (column_name == "modified") c = new ColumnModified (); - else if (column_name == "parent") c = new ColumnParent (); - else if (column_name == "project") c = new ColumnProject (); - else if (column_name == "recur") c = new ColumnRecur (); - else if (column_name == "rtype") c = new ColumnRType (); - else if (column_name == "scheduled") c = new ColumnScheduled (); - else if (column_name == "start") c = new ColumnStart (); - else if (column_name == "status") c = new ColumnStatus (); - else if (column_name == "tags") c = new ColumnTags (); - else if (column_name == "template") c = new ColumnTemplate (); - else if (column_name == "until") c = new ColumnUntil (); - else if (column_name == "urgency") c = new ColumnUrgency (); - else if (column_name == "uuid") c = new ColumnUUID (); - else if (column_name == "wait") c = new ColumnWait (); + if (column_name == "depends") + c = new ColumnDepends(); + else if (column_name == "description") + c = new ColumnDescription(); + else if (column_name == "due") + c = new ColumnDue(); + else if (column_name == "end") + c = new ColumnEnd(); + else if (column_name == "entry") + c = new ColumnEntry(); + else if (column_name == "id") + c = new ColumnID(); + else if (column_name == "imask") + c = new ColumnIMask(); + else if (column_name == "last") + c = new ColumnLast(); + else if (column_name == "mask") + c = new ColumnMask(); + else if (column_name == "modified") + c = new ColumnModified(); + else if (column_name == "parent") + c = new ColumnParent(); + else if (column_name == "project") + c = new ColumnProject(); + else if (column_name == "recur") + c = new ColumnRecur(); + else if (column_name == "rtype") + c = new ColumnRType(); + else if (column_name == "scheduled") + c = new ColumnScheduled(); + else if (column_name == "start") + c = new ColumnStart(); + else if (column_name == "status") + c = new ColumnStatus(); + else if (column_name == "tags") + c = new ColumnTags(); + else if (column_name == "template") + c = new ColumnTemplate(); + else if (column_name == "until") + c = new ColumnUntil(); + else if (column_name == "urgency") + c = new ColumnUrgency(); + else if (column_name == "uuid") + c = new ColumnUUID(); + else if (column_name == "wait") + c = new ColumnWait(); // UDA. - else if (Context::getContext ().config.has ("uda." + column_name + ".type")) - c = Column::uda (column_name); + else if (Context::getContext().config.has("uda." + column_name + ".type")) + c = Column::uda(column_name); else - throw format ("Unrecognized column name '{1}'.", column_name); + throw format("Unrecognized column name '{1}'.", column_name); - c->setReport (report); - c->setStyle (column_style); + c->setReport(report); + c->setStyle(column_style); return c; } //////////////////////////////////////////////////////////////////////////////// // Bulk column instantiation. -void Column::factory (std::map & all) -{ +void Column::factory(std::map& all) { Column* c; - c = new ColumnDepends (); all[c->_name] = c; - c = new ColumnDescription (); all[c->_name] = c; - c = new ColumnDue (); all[c->_name] = c; - c = new ColumnEnd (); all[c->_name] = c; - c = new ColumnEntry (); all[c->_name] = c; - c = new ColumnID (); all[c->_name] = c; - c = new ColumnIMask (); all[c->_name] = c; - c = new ColumnLast (); all[c->_name] = c; - c = new ColumnMask (); all[c->_name] = c; - c = new ColumnModified (); all[c->_name] = c; - c = new ColumnParent (); all[c->_name] = c; - c = new ColumnProject (); all[c->_name] = c; - c = new ColumnRecur (); all[c->_name] = c; - c = new ColumnRType (); all[c->_name] = c; - c = new ColumnScheduled (); all[c->_name] = c; - c = new ColumnStart (); all[c->_name] = c; - c = new ColumnStatus (); all[c->_name] = c; - c = new ColumnTags (); all[c->_name] = c; - c = new ColumnTemplate (); all[c->_name] = c; - c = new ColumnUntil (); all[c->_name] = c; - c = new ColumnUrgency (); all[c->_name] = c; - c = new ColumnUUID (); all[c->_name] = c; - c = new ColumnWait (); all[c->_name] = c; + c = new ColumnDepends(); + all[c->_name] = c; + c = new ColumnDescription(); + all[c->_name] = c; + c = new ColumnDue(); + all[c->_name] = c; + c = new ColumnEnd(); + all[c->_name] = c; + c = new ColumnEntry(); + all[c->_name] = c; + c = new ColumnID(); + all[c->_name] = c; + c = new ColumnIMask(); + all[c->_name] = c; + c = new ColumnLast(); + all[c->_name] = c; + c = new ColumnMask(); + all[c->_name] = c; + c = new ColumnModified(); + all[c->_name] = c; + c = new ColumnParent(); + all[c->_name] = c; + c = new ColumnProject(); + all[c->_name] = c; + c = new ColumnRecur(); + all[c->_name] = c; + c = new ColumnRType(); + all[c->_name] = c; + c = new ColumnScheduled(); + all[c->_name] = c; + c = new ColumnStart(); + all[c->_name] = c; + c = new ColumnStatus(); + all[c->_name] = c; + c = new ColumnTags(); + all[c->_name] = c; + c = new ColumnTemplate(); + all[c->_name] = c; + c = new ColumnUntil(); + all[c->_name] = c; + c = new ColumnUrgency(); + all[c->_name] = c; + c = new ColumnUUID(); + all[c->_name] = c; + c = new ColumnWait(); + all[c->_name] = c; - Column::uda (all); + Column::uda(all); } //////////////////////////////////////////////////////////////////////////////// -void Column::uda (std::map & all) -{ +void Column::uda(std::map& all) { // For each UDA, instantiate and initialize ColumnUDA. - std::set udas; + std::set udas; - for (const auto& i : Context::getContext ().config) - { - if (i.first.substr (0, 4) == "uda.") - { - std::string::size_type period = 4; // One byte after the first '.'. + for (const auto& i : Context::getContext().config) { + if (i.first.substr(0, 4) == "uda.") { + std::string::size_type period = 4; // One byte after the first '.'. - if ((period = i.first.find ('.', period)) != std::string::npos) - udas.insert (i.first.substr (4, period - 4)); + if ((period = i.first.find('.', period)) != std::string::npos) + udas.insert(i.first.substr(4, period - 4)); } } - for (const auto& uda : udas) - { - if (all.find (uda) != all.end ()) - throw format ("The UDA named '{1}' is the same as a core attribute, and is not permitted.", uda); + for (const auto& uda : udas) { + if (all.find(uda) != all.end()) + throw format("The UDA named '{1}' is the same as a core attribute, and is not permitted.", + uda); - Column* c = Column::uda (uda); - if (c) - all[c->_name] = c; + Column* c = Column::uda(uda); + if (c) all[c->_name] = c; } } //////////////////////////////////////////////////////////////////////////////// -Column* Column::uda (const std::string& name) -{ - auto type = Context::getContext ().config.get ("uda." + name + ".type"); - auto label = Context::getContext ().config.get ("uda." + name + ".label"); - auto values = Context::getContext ().config.get ("uda." + name + ".values"); +Column* Column::uda(const std::string& name) { + auto type = Context::getContext().config.get("uda." + name + ".type"); + auto label = Context::getContext().config.get("uda." + name + ".label"); + auto values = Context::getContext().config.get("uda." + name + ".values"); - if (type == "string") - { - auto c = new ColumnUDAString (); + if (type == "string") { + auto c = new ColumnUDAString(); c->_name = name; c->_label = label; - if (values != "") - c->_values = split (values, ','); + if (values != "") c->_values = split(values, ','); return c; - } - else if (type == "numeric") - { - auto c = new ColumnUDANumeric (); + } else if (type == "numeric") { + auto c = new ColumnUDANumeric(); c->_name = name; c->_label = label; - if (values != "") - c->_values = split (values, ','); + if (values != "") c->_values = split(values, ','); return c; - } - else if (type == "date") - { - auto c = new ColumnUDADate (); + } else if (type == "date") { + auto c = new ColumnUDADate(); c->_name = name; c->_label = label; - if (values != "") - c->_values = split (values, ','); + if (values != "") c->_values = split(values, ','); return c; - } - else if (type == "duration") - { - auto c = new ColumnUDADuration (); + } else if (type == "duration") { + auto c = new ColumnUDADuration(); c->_name = name; c->_label = label; - if (values != "") - c->_values = split (values, ','); + if (values != "") c->_values = split(values, ','); return c; - } - else if (type != "") - throw std::string ("User defined attributes may only be of type 'string', 'date', 'duration' or 'numeric'."); + } else if (type != "") + throw std::string( + "User defined attributes may only be of type 'string', 'date', 'duration' or 'numeric'."); return nullptr; } //////////////////////////////////////////////////////////////////////////////// -Column::Column () -: _name ("") -, _type ("string") -, _style ("default") -, _label ("") -, _report ("") -, _modifiable (true) -, _uda (false) -, _fixed_width (false) -{ -} +Column::Column() + : _name(""), + _type("string"), + _style("default"), + _label(""), + _report(""), + _modifiable(true), + _uda(false), + _fixed_width(false) {} //////////////////////////////////////////////////////////////////////////////// -void Column::renderHeader ( - std::vector & lines, - int width, - Color& color) -{ - if (Context::getContext ().verbose ("label") && - _label != "") - { +void Column::renderHeader(std::vector& lines, int width, Color& color) { + if (Context::getContext().verbose("label") && _label != "") { // Create a basic label. std::string header; - header.reserve (width); + header.reserve(width); header = _label; // Create a fungible copy. Color c = color; // Now underline the header, or add a dashed line. - if (Context::getContext ().color () && - Context::getContext ().config.getBoolean ("fontunderline")) - { - c.blend (Color (Color::nocolor, Color::nocolor, true, false, false)); - lines.push_back (c.colorize (leftJustify (header, width))); - } - else - { - lines.push_back (c.colorize (leftJustify (header, width))); - lines.push_back (c.colorize (std::string (width, '-'))); + if (Context::getContext().color() && Context::getContext().config.getBoolean("fontunderline")) { + c.blend(Color(Color::nocolor, Color::nocolor, true, false, false)); + lines.push_back(c.colorize(leftJustify(header, width))); + } else { + lines.push_back(c.colorize(leftJustify(header, width))); + lines.push_back(c.colorize(std::string(width, '-'))); } } } //////////////////////////////////////////////////////////////////////////////// -void Column::setStyle (const std::string& style) -{ - if (style != "default" && - std::find (_styles.begin (), _styles.end (), style) == _styles.end ()) - throw format ("Unrecognized column format '{1}.{2}'", _name, style); +void Column::setStyle(const std::string& style) { + if (style != "default" && std::find(_styles.begin(), _styles.end(), style) == _styles.end()) + throw format("Unrecognized column format '{1}.{2}'", _name, style); _style = style; } //////////////////////////////////////////////////////////////////////////////// // All integer values are right-justified. -void Column::renderInteger ( - std::vector & lines, - int width, - Color& color, - int value) -{ - lines.push_back ( - color.colorize ( - rightJustify (value, width))); +void Column::renderInteger(std::vector& lines, int width, Color& color, int value) { + lines.push_back(color.colorize(rightJustify(value, width))); } //////////////////////////////////////////////////////////////////////////////// // All floating point values are right-justified. -void Column::renderDouble ( - std::vector & lines, - int width, - Color& color, - double value) -{ - lines.push_back ( - color.colorize ( - rightJustify ( - format (value, 4, 3), width))); +void Column::renderDouble(std::vector& lines, int width, Color& color, double value) { + lines.push_back(color.colorize(rightJustify(format(value, 4, 3), width))); } //////////////////////////////////////////////////////////////////////////////// -void Column::renderStringLeft ( - std::vector & lines, - int width, - Color& color, - const std::string& value) -{ - lines.push_back ( - color.colorize ( - leftJustify (value, width))); +void Column::renderStringLeft(std::vector& lines, int width, Color& color, + const std::string& value) { + lines.push_back(color.colorize(leftJustify(value, width))); } //////////////////////////////////////////////////////////////////////////////// -void Column::renderStringRight ( - std::vector & lines, - int width, - Color& color, - const std::string& value) -{ - lines.push_back ( - color.colorize ( - rightJustify (value, width))); +void Column::renderStringRight(std::vector& lines, int width, Color& color, + const std::string& value) { + lines.push_back(color.colorize(rightJustify(value, width))); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/Column.h b/src/columns/Column.h index 45cf0f613..20dad26fb 100644 --- a/src/columns/Column.h +++ b/src/columns/Column.h @@ -27,51 +27,51 @@ #ifndef INCLUDED_COLUMN #define INCLUDED_COLUMN -#include -#include #include #include -class Column -{ -public: - static Column* factory (const std::string&, const std::string&); - static void factory (std::map &); - static void uda (std::map &); - static Column* uda (const std::string&); +#include +#include - Column (); - virtual ~Column () = default; +class Column { + public: + static Column* factory(const std::string&, const std::string&); + static void factory(std::map&); + static void uda(std::map&); + static Column* uda(const std::string&); - const std::string& name () const { return _name; } - const std::string& style () const { return _style; } - const std::string& label () const { return _label; } - const std::string& type () const { return _type; } - bool modifiable () const { return _modifiable; } - bool is_uda () const { return _uda; } - bool is_fixed_width () const { return _fixed_width; } - std::vector styles () const { return _styles; } - std::vector examples () const { return _examples; } + Column(); + virtual ~Column() = default; - virtual void setStyle (const std::string&); - virtual void setLabel (const std::string& value) { _label = value; } - virtual void setReport (const std::string& value) { _report = value; } + const std::string& name() const { return _name; } + const std::string& style() const { return _style; } + const std::string& label() const { return _label; } + const std::string& type() const { return _type; } + bool modifiable() const { return _modifiable; } + bool is_uda() const { return _uda; } + bool is_fixed_width() const { return _fixed_width; } + std::vector styles() const { return _styles; } + std::vector examples() const { return _examples; } - virtual void measure (const std::string&, unsigned int&, unsigned int&) {}; - virtual void measure (Task&, unsigned int&, unsigned int&) {}; - virtual void renderHeader (std::vector &, int, Color&); - virtual void render (std::vector &, const std::string&, int, Color&) {}; - virtual void render (std::vector &, Task&, int, Color&) {}; - virtual bool validate (const std::string&) const {return false;}; - virtual void modify (Task&, const std::string&) {}; + virtual void setStyle(const std::string&); + virtual void setLabel(const std::string& value) { _label = value; } + virtual void setReport(const std::string& value) { _report = value; } -protected: - void renderInteger (std::vector &, int, Color&, int); - void renderDouble (std::vector &, int, Color&, double); - void renderStringLeft (std::vector &, int, Color&, const std::string&); - void renderStringRight (std::vector &, int, Color&, const std::string&); + virtual void measure(const std::string&, unsigned int&, unsigned int&) {}; + virtual void measure(Task&, unsigned int&, unsigned int&) {}; + virtual void renderHeader(std::vector&, int, Color&); + virtual void render(std::vector&, const std::string&, int, Color&) {}; + virtual void render(std::vector&, Task&, int, Color&) {}; + virtual bool validate(const std::string&) const { return false; }; + virtual void modify(Task&, const std::string&) {}; -protected: + protected: + void renderInteger(std::vector&, int, Color&, int); + void renderDouble(std::vector&, int, Color&, double); + void renderStringLeft(std::vector&, int, Color&, const std::string&); + void renderStringRight(std::vector&, int, Color&, const std::string&); + + protected: std::string _name; std::string _type; std::string _style; @@ -80,8 +80,8 @@ protected: bool _modifiable; bool _uda; bool _fixed_width; - std::vector _styles; - std::vector _examples; + std::vector _styles; + std::vector _examples; }; #endif diff --git a/src/commands/CmdAdd.cpp b/src/commands/CmdAdd.cpp index 749c53499..6661c3d2f 100644 --- a/src/commands/CmdAdd.cpp +++ b/src/commands/CmdAdd.cpp @@ -33,61 +33,55 @@ #include //////////////////////////////////////////////////////////////////////////////// -CmdAdd::CmdAdd () -{ - _keyword = "add"; - _usage = "task add "; - _description = "Adds a new task"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = true; - _accepts_filter = false; +CmdAdd::CmdAdd() { + _keyword = "add"; + _usage = "task add "; + _description = "Adds a new task"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = true; + _accepts_filter = false; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdAdd::execute (std::string& output) -{ +int CmdAdd::execute(std::string& output) { // Apply the command line modifications to the new task. Task task; // the task is empty, but DOM references can refer to earlier parts of the // command line, e.g., `task add due:20110101 wait:due`. - task.modify (Task::modReplace, true); - Context::getContext ().tdb2.add (task); + task.modify(Task::modReplace, true); + Context::getContext().tdb2.add(task); // Do not display ID 0, users cannot query by that - auto status = task.getStatus (); + auto status = task.getStatus(); // We may have a situation where both new-id and new-uuid config // variables are set. In that case, we'll show the new-uuid, as // it's enduring and never changes, and it's unlikely the caller // asked for this if they just wanted a human-friendly number. - if (Context::getContext ().verbose ("new-uuid") && - status == Task::recurring) - output += format ("Created task {1} (recurrence template).\n", task.get ("uuid")); + if (Context::getContext().verbose("new-uuid") && status == Task::recurring) + output += format("Created task {1} (recurrence template).\n", task.get("uuid")); - else if (Context::getContext ().verbose ("new-uuid") || - (Context::getContext ().verbose ("new-id") && - (status == Task::completed || - status == Task::deleted))) - output += format ("Created task {1}.\n", task.get ("uuid")); + else if (Context::getContext().verbose("new-uuid") || + (Context::getContext().verbose("new-id") && + (status == Task::completed || status == Task::deleted))) + output += format("Created task {1}.\n", task.get("uuid")); - else if (Context::getContext ().verbose ("new-id") && - (status == Task::pending || - status == Task::waiting)) - output += format ("Created task {1}.\n", task.id); + else if (Context::getContext().verbose("new-id") && + (status == Task::pending || status == Task::waiting)) + output += format("Created task {1}.\n", task.id); - else if (Context::getContext ().verbose ("new-id") && - status == Task::recurring) - output += format ("Created task {1} (recurrence template).\n", task.id); + else if (Context::getContext().verbose("new-id") && status == Task::recurring) + output += format("Created task {1} (recurrence template).\n", task.id); - if (Context::getContext ().verbose ("project")) - Context::getContext ().footnote (onProjectChange (task)); + if (Context::getContext().verbose("project")) + Context::getContext().footnote(onProjectChange(task)); return 0; } diff --git a/src/commands/CmdAdd.h b/src/commands/CmdAdd.h index 995938091..e67f7d17e 100644 --- a/src/commands/CmdAdd.h +++ b/src/commands/CmdAdd.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDADD #define INCLUDED_CMDADD -#include #include -class CmdAdd : public Command -{ -public: - CmdAdd (); - int execute (std::string&); +#include + +class CmdAdd : public Command { + public: + CmdAdd(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdAliases.cpp b/src/commands/CmdAliases.cpp index b6b6074c4..3554a7ee6 100644 --- a/src/commands/CmdAliases.cpp +++ b/src/commands/CmdAliases.cpp @@ -28,31 +28,28 @@ // cmake.h include header must come first #include -#include #include +#include //////////////////////////////////////////////////////////////////////////////// -CmdCompletionAliases::CmdCompletionAliases () -{ - _keyword = "_aliases"; - _usage = "task _aliases"; - _description = "Generates a list of all aliases, for autocompletion purposes"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdCompletionAliases::CmdCompletionAliases() { + _keyword = "_aliases"; + _usage = "task _aliases"; + _description = "Generates a list of all aliases, for autocompletion purposes"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionAliases::execute (std::string& output) -{ - for (const auto& alias : Context::getContext ().config) - if (alias.first.substr (0, 6) == "alias.") - output += alias.first.substr (6) + '\n'; +int CmdCompletionAliases::execute(std::string& output) { + for (const auto& alias : Context::getContext().config) + if (alias.first.substr(0, 6) == "alias.") output += alias.first.substr(6) + '\n'; return 0; } diff --git a/src/commands/CmdAliases.h b/src/commands/CmdAliases.h index a6fc9bf2a..e8fdd7c9e 100644 --- a/src/commands/CmdAliases.h +++ b/src/commands/CmdAliases.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDALIASES #define INCLUDED_CMDALIASES -#include #include -class CmdCompletionAliases : public Command -{ -public: - CmdCompletionAliases (); - int execute (std::string&); +#include + +class CmdCompletionAliases : public Command { + public: + CmdCompletionAliases(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdAnnotate.cpp b/src/commands/CmdAnnotate.cpp index 45827aeab..73bbb8882 100644 --- a/src/commands/CmdAnnotate.cpp +++ b/src/commands/CmdAnnotate.cpp @@ -28,111 +28,100 @@ // cmake.h include header must come first #include -#include #include #include +#include #include #include -#include + +#include //////////////////////////////////////////////////////////////////////////////// -CmdAnnotate::CmdAnnotate () -{ - _keyword = "annotate"; - _usage = "task annotate "; - _description = "Adds an annotation to an existing task"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = true; +CmdAnnotate::CmdAnnotate() { + _keyword = "annotate"; + _usage = "task annotate "; + _description = "Adds an annotation to an existing task"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdAnnotate::execute (std::string&) -{ +int CmdAnnotate::execute(std::string&) { int rc = 0; int count = 0; // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + std::vector filtered; + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // TODO Complain when no modifications are specified. // Accumulated project change notifications. - std::map projectChanges; + std::map projectChanges; - if(filtered.size() > 1) { + if (filtered.size() > 1) { feedback_affected("This command will alter {1} tasks.", filtered.size()); } - for (auto& task : filtered) - { - Task before (task); + for (auto& task : filtered) { + Task before(task); // Annotate the specified task. - std::string question = format ("Annotate task {1} '{2}'?", - task.identifier (true), - task.get ("description")); + std::string question = + format("Annotate task {1} '{2}'?", task.identifier(true), task.get("description")); - task.modify (Task::modAnnotate, true); + task.modify(Task::modAnnotate, true); - if (permission (before.diff (task) + question, filtered.size ())) - { - Context::getContext ().tdb2.modify (task); + if (permission(before.diff(task) + question, filtered.size())) { + Context::getContext().tdb2.modify(task); ++count; - feedback_affected ("Annotating task {1} '{2}'.", task); - if (Context::getContext ().verbose ("project")) - projectChanges[task.get ("project")] = onProjectChange (task, false); + feedback_affected("Annotating task {1} '{2}'.", task); + if (Context::getContext().verbose("project")) + projectChanges[task.get("project")] = onProjectChange(task, false); // Annotate siblings. - if (task.has ("parent")) - { - if ((Context::getContext ().config.get ("recurrence.confirmation") == "prompt" - && confirm ("This is a recurring task. Do you want to annotate all pending recurrences of this same task?")) || - Context::getContext ().config.getBoolean ("recurrence.confirmation")) - { - auto siblings = Context::getContext ().tdb2.siblings (task); - for (auto& sibling : siblings) - { - sibling.modify (Task::modAnnotate, true); - Context::getContext ().tdb2.modify (sibling); + if (task.has("parent")) { + if ((Context::getContext().config.get("recurrence.confirmation") == "prompt" && + confirm("This is a recurring task. Do you want to annotate all pending recurrences " + "of this same task?")) || + Context::getContext().config.getBoolean("recurrence.confirmation")) { + auto siblings = Context::getContext().tdb2.siblings(task); + for (auto& sibling : siblings) { + sibling.modify(Task::modAnnotate, true); + Context::getContext().tdb2.modify(sibling); ++count; - feedback_affected ("Annotating recurring task {1} '{2}'.", sibling); + feedback_affected("Annotating recurring task {1} '{2}'.", sibling); } // Annotate the parent Task parent; - Context::getContext ().tdb2.get (task.get ("parent"), parent); - parent.modify (Task::modAnnotate, true); - Context::getContext ().tdb2.modify (parent); + Context::getContext().tdb2.get(task.get("parent"), parent); + parent.modify(Task::modAnnotate, true); + Context::getContext().tdb2.modify(parent); } } - } - else - { + } else { std::cout << "Task not annotated.\n"; rc = 1; - if (_permission_quit) - break; + if (_permission_quit) break; } } // Now list the project changes. for (const auto& change : projectChanges) - if (change.first != "") - Context::getContext ().footnote (change.second); + if (change.first != "") Context::getContext().footnote(change.second); - feedback_affected (count == 1 ? "Annotated {1} task." : "Annotated {1} tasks.", count); + feedback_affected(count == 1 ? "Annotated {1} task." : "Annotated {1} tasks.", count); return rc; } diff --git a/src/commands/CmdAnnotate.h b/src/commands/CmdAnnotate.h index db0c47c4e..97f3973df 100644 --- a/src/commands/CmdAnnotate.h +++ b/src/commands/CmdAnnotate.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDANNOTATE #define INCLUDED_CMDANNOTATE -#include #include -class CmdAnnotate : public Command -{ -public: - CmdAnnotate (); - int execute (std::string&); +#include + +class CmdAnnotate : public Command { + public: + CmdAnnotate(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdAppend.cpp b/src/commands/CmdAppend.cpp index 3cfd820db..d6b68fab2 100644 --- a/src/commands/CmdAppend.cpp +++ b/src/commands/CmdAppend.cpp @@ -28,111 +28,100 @@ // cmake.h include header must come first #include -#include #include #include -#include #include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -CmdAppend::CmdAppend () -{ - _keyword = "append"; - _usage = "task append "; - _description = "Appends text to an existing task description"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = true; +CmdAppend::CmdAppend() { + _keyword = "append"; + _usage = "task append "; + _description = "Appends text to an existing task description"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdAppend::execute (std::string&) -{ +int CmdAppend::execute(std::string&) { int rc = 0; int count = 0; // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + std::vector filtered; + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // TODO Complain when no modifications are specified. // Accumulated project change notifications. - std::map projectChanges; + std::map projectChanges; - if(filtered.size() > 1) { + if (filtered.size() > 1) { feedback_affected("This command will alter {1} tasks.", filtered.size()); } - for (auto& task : filtered) - { - Task before (task); + for (auto& task : filtered) { + Task before(task); // Append to the specified task. - auto question = format ("Append to task {1} '{2}'?", - task.identifier (true), - task.get ("description")); + auto question = + format("Append to task {1} '{2}'?", task.identifier(true), task.get("description")); - task.modify (Task::modAppend, true); + task.modify(Task::modAppend, true); - if (permission (before.diff (task) + question, filtered.size ())) - { - Context::getContext ().tdb2.modify (task); + if (permission(before.diff(task) + question, filtered.size())) { + Context::getContext().tdb2.modify(task); ++count; - feedback_affected ("Appending to task {1} '{2}'.", task); - if (Context::getContext ().verbose ("project")) - projectChanges[task.get ("project")] = onProjectChange (task, false); + feedback_affected("Appending to task {1} '{2}'.", task); + if (Context::getContext().verbose("project")) + projectChanges[task.get("project")] = onProjectChange(task, false); // Append to siblings. - if (task.has ("parent")) - { - if ((Context::getContext ().config.get ("recurrence.confirmation") == "prompt" - && confirm ("This is a recurring task. Do you want to append to all pending recurrences of this same task?")) || - Context::getContext ().config.getBoolean ("recurrence.confirmation")) - { - std::vector siblings = Context::getContext ().tdb2.siblings (task); - for (auto& sibling : siblings) - { - sibling.modify (Task::modAppend, true); - Context::getContext ().tdb2.modify (sibling); + if (task.has("parent")) { + if ((Context::getContext().config.get("recurrence.confirmation") == "prompt" && + confirm("This is a recurring task. Do you want to append to all pending recurrences " + "of this same task?")) || + Context::getContext().config.getBoolean("recurrence.confirmation")) { + std::vector siblings = Context::getContext().tdb2.siblings(task); + for (auto& sibling : siblings) { + sibling.modify(Task::modAppend, true); + Context::getContext().tdb2.modify(sibling); ++count; - feedback_affected ("Appending to recurring task {1} '{2}'.", sibling); + feedback_affected("Appending to recurring task {1} '{2}'.", sibling); } // Append to the parent Task parent; - Context::getContext ().tdb2.get (task.get ("parent"), parent); - parent.modify (Task::modAppend, true); - Context::getContext ().tdb2.modify (parent); + Context::getContext().tdb2.get(task.get("parent"), parent); + parent.modify(Task::modAppend, true); + Context::getContext().tdb2.modify(parent); } } - } - else - { + } else { std::cout << "Task not appended.\n"; rc = 1; - if (_permission_quit) - break; + if (_permission_quit) break; } } // Now list the project changes. for (const auto& change : projectChanges) - if (change.first != "") - Context::getContext ().footnote (change.second); + if (change.first != "") Context::getContext().footnote(change.second); - feedback_affected (count == 1 ? "Appended {1} task." : "Appended {1} tasks.", count); + feedback_affected(count == 1 ? "Appended {1} task." : "Appended {1} tasks.", count); return rc; } diff --git a/src/commands/CmdAppend.h b/src/commands/CmdAppend.h index a3a6b3a86..41428926f 100644 --- a/src/commands/CmdAppend.h +++ b/src/commands/CmdAppend.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDAPPEND #define INCLUDED_CMDAPPEND -#include #include -class CmdAppend : public Command -{ -public: - CmdAppend (); - int execute (std::string&); +#include + +class CmdAppend : public Command { + public: + CmdAppend(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdAttributes.cpp b/src/commands/CmdAttributes.cpp index 1ae4d69f0..bc7778572 100644 --- a/src/commands/CmdAttributes.cpp +++ b/src/commands/CmdAttributes.cpp @@ -28,39 +28,37 @@ // cmake.h include header must come first #include -#include -#include -#include #include +#include + +#include +#include //////////////////////////////////////////////////////////////////////////////// -CmdZshAttributes::CmdZshAttributes () -{ - _keyword = "_zshattributes"; - _usage = "task _zshattributes"; - _description = "Generates a list of all attributes, for zsh autocompletion purposes"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdZshAttributes::CmdZshAttributes() { + _keyword = "_zshattributes"; + _usage = "task _zshattributes"; + _description = "Generates a list of all attributes, for zsh autocompletion purposes"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdZshAttributes::execute (std::string& output) -{ +int CmdZshAttributes::execute(std::string& output) { // Get a list of all columns, sort them. - auto columns = Context::getContext ().getColumns (); - std::sort (columns.begin (), columns.end ()); + auto columns = Context::getContext().getColumns(); + std::sort(columns.begin(), columns.end()); std::stringstream out; - for (const auto& col : columns) - out << col << ':' << col << '\n'; + for (const auto& col : columns) out << col << ':' << col << '\n'; - output = out.str (); + output = out.str(); return 0; } diff --git a/src/commands/CmdAttributes.h b/src/commands/CmdAttributes.h index 72c56779a..fa3ef8d81 100644 --- a/src/commands/CmdAttributes.h +++ b/src/commands/CmdAttributes.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDATTRIBUTES #define INCLUDED_CMDATTRIBUTES -#include #include -class CmdZshAttributes : public Command -{ -public: - CmdZshAttributes (); - int execute (std::string&); +#include + +class CmdZshAttributes : public Command { + public: + CmdZshAttributes(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdBurndown.cpp b/src/commands/CmdBurndown.cpp index 2a3006504..eb2a5e2e9 100644 --- a/src/commands/CmdBurndown.cpp +++ b/src/commands/CmdBurndown.cpp @@ -28,62 +28,57 @@ // cmake.h include header must come first #include -#include -#include -#include -#include -#include -#include #include -#include #include #include -#include -#include +#include #include +#include +#include +#include +#include + +#include +#include +#include +#include // Helper macro. -#define LOC(y,x) (((y) * (_width + 1)) + (x)) +#define LOC(y, x) (((y) * (_width + 1)) + (x)) //////////////////////////////////////////////////////////////////////////////// -class Bar -{ -public: - Bar () = default; - Bar (const Bar&); - Bar& operator= (const Bar&); - ~Bar () = default; +class Bar { + public: + Bar() = default; + Bar(const Bar&); + Bar& operator=(const Bar&); + ~Bar() = default; -public: - int _offset {0}; // from left of chart - std::string _major_label {""}; // x-axis label, major (year/-/month) - std::string _minor_label {""}; // x-axis label, minor (month/week/day) - int _pending {0}; // Number of pending tasks in period - int _started {0}; // Number of started tasks in period - int _done {0}; // Number of done tasks in period - int _added {0}; // Number added in period - int _removed {0}; // Number removed in period + public: + int _offset{0}; // from left of chart + std::string _major_label{""}; // x-axis label, major (year/-/month) + std::string _minor_label{""}; // x-axis label, minor (month/week/day) + int _pending{0}; // Number of pending tasks in period + int _started{0}; // Number of started tasks in period + int _done{0}; // Number of done tasks in period + int _added{0}; // Number added in period + int _removed{0}; // Number removed in period }; //////////////////////////////////////////////////////////////////////////////// -Bar::Bar (const Bar& other) -{ - *this = other; -} +Bar::Bar(const Bar& other) { *this = other; } //////////////////////////////////////////////////////////////////////////////// -Bar& Bar::operator= (const Bar& other) -{ - if (this != &other) - { - _offset = other._offset; +Bar& Bar::operator=(const Bar& other) { + if (this != &other) { + _offset = other._offset; _major_label = other._major_label; _minor_label = other._minor_label; - _pending = other._pending; - _started = other._started; - _done = other._done; - _added = other._added; - _removed = other._removed; + _pending = other._pending; + _started = other._started; + _done = other._done; + _added = other._added; + _removed = other._removed; } return *this; @@ -120,62 +115,60 @@ Bar& Bar::operator= (const Bar& other) // 30 31 01 02 03 04 05 06 07 08 09 10 // Oct Nov // -class Chart -{ -public: - Chart (char); - Chart (const Chart&); // Unimplemented - Chart& operator= (const Chart&); // Unimplemented - ~Chart () = default; +class Chart { + public: + Chart(char); + Chart(const Chart&); // Unimplemented + Chart& operator=(const Chart&); // Unimplemented + ~Chart() = default; - void scan (std::vector &); - void scanForPeak (std::vector &); - std::string render (); + void scan(std::vector&); + void scanForPeak(std::vector&); + std::string render(); -private: - void generateBars (); - void optimizeGrid (); - Datetime quantize (const Datetime&, char); + private: + void generateBars(); + void optimizeGrid(); + Datetime quantize(const Datetime&, char); - Datetime increment (const Datetime&, char); - Datetime decrement (const Datetime&, char); - void maxima (); - void yLabels (std::vector &); - void calculateRates (); - unsigned round_up_to (unsigned, unsigned); - unsigned burndown_size (unsigned); + Datetime increment(const Datetime&, char); + Datetime decrement(const Datetime&, char); + void maxima(); + void yLabels(std::vector&); + void calculateRates(); + unsigned round_up_to(unsigned, unsigned); + unsigned burndown_size(unsigned); -public: - int _width {}; // Terminal width - int _height {}; // Terminal height - int _graph_width {}; // Width of plot area - int _graph_height {}; // Height of plot area - int _max_value {0}; // Largest combined bar value - int _max_label {1}; // Longest y-axis label - std::vector _labels {}; // Y-axis labels - int _estimated_bars {}; // Estimated bar count - int _actual_bars {0}; // Calculated bar count - std::map _bars {}; // Epoch-indexed set of bars - Datetime _earliest {}; // Date of earliest estimated bar - int _carryover_done {0}; // Number of 'done' tasks prior to chart range - char _period {}; // D, W, M - std::string _grid {}; // String representing grid of characters - time_t _peak_epoch {}; // Quantized (D) date of highest pending peak - int _peak_count {0}; // Corresponding peak pending count - int _current_count {0}; // Current pending count - float _net_fix_rate {0.0}; // Calculated fix rate - std::string _completion {}; // Estimated completion date + public: + int _width{}; // Terminal width + int _height{}; // Terminal height + int _graph_width{}; // Width of plot area + int _graph_height{}; // Height of plot area + int _max_value{0}; // Largest combined bar value + int _max_label{1}; // Longest y-axis label + std::vector _labels{}; // Y-axis labels + int _estimated_bars{}; // Estimated bar count + int _actual_bars{0}; // Calculated bar count + std::map _bars{}; // Epoch-indexed set of bars + Datetime _earliest{}; // Date of earliest estimated bar + int _carryover_done{0}; // Number of 'done' tasks prior to chart range + char _period{}; // D, W, M + std::string _grid{}; // String representing grid of characters + time_t _peak_epoch{}; // Quantized (D) date of highest pending peak + int _peak_count{0}; // Corresponding peak pending count + int _current_count{0}; // Current pending count + float _net_fix_rate{0.0}; // Calculated fix rate + std::string _completion{}; // Estimated completion date }; //////////////////////////////////////////////////////////////////////////////// -Chart::Chart (char type) -{ +Chart::Chart(char type) { // How much space is there to render in? This chart will occupy the // maximum space, and the width drives various other parameters. - _width = Context::getContext ().getWidth (); - _height = Context::getContext ().getHeight () - - Context::getContext ().config.getInteger ("reserved.lines") - - 1; // Allow for new line with prompt. + _width = Context::getContext().getWidth(); + _height = Context::getContext().getHeight() - + Context::getContext().config.getInteger("reserved.lines") - + 1; // Allow for new line with prompt. _graph_height = _height - 7; _graph_width = _width - _max_label - 14; @@ -189,39 +182,34 @@ Chart::Chart (char type) //////////////////////////////////////////////////////////////////////////////// // Scan all tasks, quantize the dates by day, and find the peak pending count // and corresponding epoch. -void Chart::scanForPeak (std::vector & tasks) -{ - std::map pending; +void Chart::scanForPeak(std::vector& tasks) { + std::map pending; _current_count = 0; - for (auto& task : tasks) - { + for (auto& task : tasks) { // The entry date is when the counting starts. - Datetime entry (task.get_date ("entry")); + Datetime entry(task.get_date("entry")); Datetime end; - if (task.has ("end")) - end = Datetime (task.get_date ("end")); + if (task.has("end")) + end = Datetime(task.get_date("end")); else ++_current_count; - while (entry < end) - { - time_t epoch = quantize (entry.toEpoch (), 'D').toEpoch (); - if (pending.find (epoch) != pending.end ()) + while (entry < end) { + time_t epoch = quantize(entry.toEpoch(), 'D').toEpoch(); + if (pending.find(epoch) != pending.end()) ++pending[epoch]; else pending[epoch] = 1; - entry = increment (entry, 'D'); + entry = increment(entry, 'D'); } } // Find the peak and peak date. - for (auto& count : pending) - { - if (count.second > _peak_count) - { + for (auto& count : pending) { + if (count.second > _peak_count) { _peak_count = count.second; _peak_epoch = count.first; } @@ -229,120 +217,93 @@ void Chart::scanForPeak (std::vector & tasks) } //////////////////////////////////////////////////////////////////////////////// -void Chart::scan (std::vector & tasks) -{ - generateBars (); +void Chart::scan(std::vector& tasks) { + generateBars(); // Not quantized, so that "while (xxx < now)" is inclusive. Datetime now; time_t epoch; - auto& config = Context::getContext ().config; + auto& config = Context::getContext().config; bool cumulative; - if (config.has ("burndown.cumulative")) - { - cumulative = config.getBoolean ("burndown.cumulative"); - } - else - { + if (config.has("burndown.cumulative")) { + cumulative = config.getBoolean("burndown.cumulative"); + } else { cumulative = true; } - for (auto& task : tasks) - { + for (auto& task : tasks) { // The entry date is when the counting starts. - Datetime from = quantize (Datetime (task.get_date ("entry")), _period); - epoch = from.toEpoch (); + Datetime from = quantize(Datetime(task.get_date("entry")), _period); + epoch = from.toEpoch(); - if (_bars.find (epoch) != _bars.end ()) - ++_bars[epoch]._added; + if (_bars.find(epoch) != _bars.end()) ++_bars[epoch]._added; // e--> e--s--> // ppp> pppsss> - Task::status status = task.getStatus (); - if (status == Task::pending || - status == Task::waiting) - { - if (task.has ("start")) - { - Datetime start = quantize (Datetime (task.get_date ("start")), _period); - while (from < start) - { - epoch = from.toEpoch (); - if (_bars.find (epoch) != _bars.end ()) - ++_bars[epoch]._pending; - from = increment (from, _period); + Task::status status = task.getStatus(); + if (status == Task::pending || status == Task::waiting) { + if (task.has("start")) { + Datetime start = quantize(Datetime(task.get_date("start")), _period); + while (from < start) { + epoch = from.toEpoch(); + if (_bars.find(epoch) != _bars.end()) ++_bars[epoch]._pending; + from = increment(from, _period); } - while (from < now) - { - epoch = from.toEpoch (); - if (_bars.find (epoch) != _bars.end ()) - ++_bars[epoch]._started; - from = increment (from, _period); + while (from < now) { + epoch = from.toEpoch(); + if (_bars.find(epoch) != _bars.end()) ++_bars[epoch]._started; + from = increment(from, _period); } - } - else - { - while (from < now) - { - epoch = from.toEpoch (); - if (_bars.find (epoch) != _bars.end ()) - ++_bars[epoch]._pending; - from = increment (from, _period); + } else { + while (from < now) { + epoch = from.toEpoch(); + if (_bars.find(epoch) != _bars.end()) ++_bars[epoch]._pending; + from = increment(from, _period); } } } // e--C e--s--C // pppd> pppsssd> - else if (status == Task::completed) - { + else if (status == Task::completed) { // Truncate history so it starts at 'earliest' for completed tasks. - Datetime end = quantize (Datetime (task.get_date ("end")), _period); - epoch = end.toEpoch (); + Datetime end = quantize(Datetime(task.get_date("end")), _period); + epoch = end.toEpoch(); - if (_bars.find (epoch) != _bars.end ()) - ++_bars[epoch]._removed; + if (_bars.find(epoch) != _bars.end()) ++_bars[epoch]._removed; - while (from < end) - { - epoch = from.toEpoch (); - if (_bars.find (epoch) != _bars.end ()) - ++_bars[epoch]._pending; - from = increment (from, _period); + while (from < end) { + epoch = from.toEpoch(); + if (_bars.find(epoch) != _bars.end()) ++_bars[epoch]._pending; + from = increment(from, _period); } - if (cumulative) - { - while (from < now) - { - epoch = from.toEpoch (); - if (_bars.find (epoch) != _bars.end ()) - ++_bars[epoch]._done; - from = increment (from, _period); + if (cumulative) { + while (from < now) { + epoch = from.toEpoch(); + if (_bars.find(epoch) != _bars.end()) ++_bars[epoch]._done; + from = increment(from, _period); } // Maintain a running total of 'done' tasks that are off the left of the // chart. - if (end < _earliest) - { + if (end < _earliest) { ++_carryover_done; continue; } } - else - { - epoch = from.toEpoch (); - if (_bars.find (epoch) != _bars.end ()) - ++_bars[epoch]._done; + else { + epoch = from.toEpoch(); + if (_bars.find(epoch) != _bars.end()) ++_bars[epoch]._done; } } } // Size the data. - maxima (); + maxima(); } //////////////////////////////////////////////////////////////////////////////// @@ -364,153 +325,146 @@ void Chart::scan (std::vector & tasks) // | ADD rate 1.7/d Estimated completion 8/12/2010 | // | Don/Delete rate 1.3/d | // +---------------------------------------------------------------------+ -std::string Chart::render () -{ - if (_graph_height < 5 || // a 4-line graph is essentially unreadable. - _graph_width < 2) // A single-bar graph is useless. +std::string Chart::render() { + if (_graph_height < 5 || // a 4-line graph is essentially unreadable. + _graph_width < 2) // A single-bar graph is useless. { - return std::string ("Terminal window too small to draw a graph.\n"); + return std::string("Terminal window too small to draw a graph.\n"); } - else if (_graph_height > 1000 || // each line is a string allloc - _graph_width > 1000) - { - return std::string ("Terminal window too large to draw a graph.\n"); + else if (_graph_height > 1000 || // each line is a string allloc + _graph_width > 1000) { + return std::string("Terminal window too large to draw a graph.\n"); } - if (_max_value == 0) - Context::getContext ().footnote ("No matches."); + if (_max_value == 0) Context::getContext().footnote("No matches."); // Create a grid, folded into a string. _grid = ""; - for (int i = 0; i < _height; ++i) - _grid += std::string (_width, ' ') + '\n'; + for (int i = 0; i < _height; ++i) _grid += std::string(_width, ' ') + '\n'; // Title. - std::string title = _period == 'D' ? "Daily" - : _period == 'W' ? "Weekly" - : "Monthly"; - title += std::string (" Burndown"); - _grid.replace (LOC (0, (_width - title.length ()) / 2), title.length (), title); + std::string title = _period == 'D' ? "Daily" : _period == 'W' ? "Weekly" : "Monthly"; + title += std::string(" Burndown"); + _grid.replace(LOC(0, (_width - title.length()) / 2), title.length(), title); // Legend. - _grid.replace (LOC (_graph_height / 2 - 1, _width - 10), 10, "DD " + leftJustify ("Done", 7)); - _grid.replace (LOC (_graph_height / 2, _width - 10), 10, "SS " + leftJustify ("Started", 7)); - _grid.replace (LOC (_graph_height / 2 + 1, _width - 10), 10, "PP " + leftJustify ("Pending", 7)); + _grid.replace(LOC(_graph_height / 2 - 1, _width - 10), 10, "DD " + leftJustify("Done", 7)); + _grid.replace(LOC(_graph_height / 2, _width - 10), 10, "SS " + leftJustify("Started", 7)); + _grid.replace(LOC(_graph_height / 2 + 1, _width - 10), 10, "PP " + leftJustify("Pending", 7)); // Determine y-axis labelling. - std::vector _labels; - yLabels (_labels); - _max_label = (int) log10 ((double) _labels[2]) + 1; + std::vector _labels; + yLabels(_labels); + _max_label = (int)log10((double)_labels[2]) + 1; // Draw y-axis. - for (int i = 0; i < _graph_height; ++i) - _grid.replace (LOC (i + 1, _max_label + 1), 1, "|"); + for (int i = 0; i < _graph_height; ++i) _grid.replace(LOC(i + 1, _max_label + 1), 1, "|"); // Draw y-axis labels. - char label [12]; - snprintf (label, 12, "%*d", _max_label, _labels[2]); - _grid.replace (LOC (1, _max_label - strlen (label)), strlen (label), label); - snprintf (label, 12, "%*d", _max_label, _labels[1]); - _grid.replace (LOC (1 + (_graph_height / 2), _max_label - strlen (label)), strlen (label), label); - _grid.replace (LOC (_graph_height + 1, _max_label - 1), 1, "0"); + char label[12]; + snprintf(label, 12, "%*d", _max_label, _labels[2]); + _grid.replace(LOC(1, _max_label - strlen(label)), strlen(label), label); + snprintf(label, 12, "%*d", _max_label, _labels[1]); + _grid.replace(LOC(1 + (_graph_height / 2), _max_label - strlen(label)), strlen(label), label); + _grid.replace(LOC(_graph_height + 1, _max_label - 1), 1, "0"); // Draw x-axis. - _grid.replace (LOC (_height - 6, _max_label + 1), 1, "+"); - _grid.replace (LOC (_height - 6, _max_label + 2), _graph_width, std::string (_graph_width, '-')); + _grid.replace(LOC(_height - 6, _max_label + 1), 1, "+"); + _grid.replace(LOC(_height - 6, _max_label + 2), _graph_width, std::string(_graph_width, '-')); // Draw x-axis labels. - std::vector bars_in_sequence; - for (auto& bar : _bars) - bars_in_sequence.push_back (bar.first); + std::vector bars_in_sequence; + for (auto& bar : _bars) bars_in_sequence.push_back(bar.first); - std::sort (bars_in_sequence.begin (), bars_in_sequence.end ()); + std::sort(bars_in_sequence.begin(), bars_in_sequence.end()); std::string _major_label; - for (auto& seq : bars_in_sequence) - { + for (auto& seq : bars_in_sequence) { Bar bar = _bars[seq]; // If it fits within the allowed space. - if (bar._offset < _actual_bars) - { - _grid.replace (LOC (_height - 5, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), bar._minor_label.length (), bar._minor_label); + if (bar._offset < _actual_bars) { + _grid.replace(LOC(_height - 5, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), + bar._minor_label.length(), bar._minor_label); if (_major_label != bar._major_label) - _grid.replace (LOC (_height - 4, _max_label + 2 + ((_actual_bars - bar._offset - 1) * 3)), bar._major_label.length (), ' ' + bar._major_label); + _grid.replace(LOC(_height - 4, _max_label + 2 + ((_actual_bars - bar._offset - 1) * 3)), + bar._major_label.length(), ' ' + bar._major_label); _major_label = bar._major_label; } } // Draw bars. - for (auto& seq : bars_in_sequence) - { + for (auto& seq : bars_in_sequence) { Bar bar = _bars[seq]; // If it fits within the allowed space. - if (bar._offset < _actual_bars) - { - int pending = ( bar._pending * _graph_height) / _labels[2]; - int started = ((bar._pending + bar._started) * _graph_height) / _labels[2]; - int done = ((bar._pending + bar._started + bar._done + _carryover_done) * _graph_height) / _labels[2]; + if (bar._offset < _actual_bars) { + int pending = (bar._pending * _graph_height) / _labels[2]; + int started = ((bar._pending + bar._started) * _graph_height) / _labels[2]; + int done = ((bar._pending + bar._started + bar._done + _carryover_done) * _graph_height) / + _labels[2]; for (int b = 0; b < pending; ++b) - _grid.replace (LOC (_graph_height - b, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), 2, "PP"); + _grid.replace( + LOC(_graph_height - b, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), 2, + "PP"); for (int b = pending; b < started; ++b) - _grid.replace (LOC (_graph_height - b, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), 2, "SS"); + _grid.replace( + LOC(_graph_height - b, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), 2, + "SS"); for (int b = started; b < done; ++b) - _grid.replace (LOC (_graph_height - b, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), 2, "DD"); + _grid.replace( + LOC(_graph_height - b, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), 2, + "DD"); } } // Draw rates. - calculateRates (); + calculateRates(); char rate[12]; if (_net_fix_rate != 0.0) - snprintf (rate, 12, "%.1f/d", _net_fix_rate); + snprintf(rate, 12, "%.1f/d", _net_fix_rate); else - strcpy (rate, "-"); + strcpy(rate, "-"); - _grid.replace (LOC (_height - 2, _max_label + 3), 22 + strlen (rate), std::string ("Net Fix Rate: ") + rate); + _grid.replace(LOC(_height - 2, _max_label + 3), 22 + strlen(rate), + std::string("Net Fix Rate: ") + rate); // Draw completion date. - if (_completion.length ()) - _grid.replace (LOC (_height - 1, _max_label + 3), 22 + _completion.length (), "Estimated completion: " + _completion); + if (_completion.length()) + _grid.replace(LOC(_height - 1, _max_label + 3), 22 + _completion.length(), + "Estimated completion: " + _completion); - optimizeGrid (); + optimizeGrid(); - if (Context::getContext ().color ()) - { + if (Context::getContext().color()) { // Colorize the grid. - Color color_pending (Context::getContext ().config.get ("color.burndown.pending")); - Color color_done (Context::getContext ().config.get ("color.burndown.done")); - Color color_started (Context::getContext ().config.get ("color.burndown.started")); + Color color_pending(Context::getContext().config.get("color.burndown.pending")); + Color color_done(Context::getContext().config.get("color.burndown.done")); + Color color_started(Context::getContext().config.get("color.burndown.started")); // Replace DD, SS, PP with colored strings. std::string::size_type i; - while ((i = _grid.find ("PP")) != std::string::npos) - _grid.replace (i, 2, color_pending.colorize (" ")); + while ((i = _grid.find("PP")) != std::string::npos) + _grid.replace(i, 2, color_pending.colorize(" ")); - while ((i = _grid.find ("SS")) != std::string::npos) - _grid.replace (i, 2, color_started.colorize (" ")); + while ((i = _grid.find("SS")) != std::string::npos) + _grid.replace(i, 2, color_started.colorize(" ")); - while ((i = _grid.find ("DD")) != std::string::npos) - _grid.replace (i, 2, color_done.colorize (" ")); - } - else - { + while ((i = _grid.find("DD")) != std::string::npos) + _grid.replace(i, 2, color_done.colorize(" ")); + } else { // Replace DD, SS, PP with ./+/X strings. std::string::size_type i; - while ((i = _grid.find ("PP")) != std::string::npos) - _grid.replace (i, 2, " X"); + while ((i = _grid.find("PP")) != std::string::npos) _grid.replace(i, 2, " X"); - while ((i = _grid.find ("SS")) != std::string::npos) - _grid.replace (i, 2, " +"); + while ((i = _grid.find("SS")) != std::string::npos) _grid.replace(i, 2, " +"); - while ((i = _grid.find ("DD")) != std::string::npos) - _grid.replace (i, 2, " ."); + while ((i = _grid.find("DD")) != std::string::npos) _grid.replace(i, 2, " ."); } return _grid; @@ -518,213 +472,189 @@ std::string Chart::render () //////////////////////////////////////////////////////////////////////////////// // _grid =~ /\s+$//g -void Chart::optimizeGrid () -{ +void Chart::optimizeGrid() { std::string::size_type ws; - while ((ws = _grid.find (" \n")) != std::string::npos) - { + while ((ws = _grid.find(" \n")) != std::string::npos) { auto non_ws = ws; - while (_grid[non_ws] == ' ') - --non_ws; + while (_grid[non_ws] == ' ') --non_ws; - _grid.replace (non_ws + 1, ws - non_ws + 1, "\n"); + _grid.replace(non_ws + 1, ws - non_ws + 1, "\n"); } } //////////////////////////////////////////////////////////////////////////////// -Datetime Chart::quantize (const Datetime& input, char period) -{ - if (period == 'D') return input.startOfDay (); - if (period == 'W') return input.startOfWeek (); - if (period == 'M') return input.startOfMonth (); +Datetime Chart::quantize(const Datetime& input, char period) { + if (period == 'D') return input.startOfDay(); + if (period == 'W') return input.startOfWeek(); + if (period == 'M') return input.startOfMonth(); return input; } //////////////////////////////////////////////////////////////////////////////// -Datetime Chart::increment (const Datetime& input, char period) -{ +Datetime Chart::increment(const Datetime& input, char period) { // Move to the next period. - int d = input.day (); - int m = input.month (); - int y = input.year (); + int d = input.day(); + int m = input.month(); + int y = input.year(); int days; - switch (period) - { - case 'D': - if (++d > Datetime::daysInMonth (y, m)) - { + switch (period) { + case 'D': + if (++d > Datetime::daysInMonth(y, m)) { + d = 1; + + if (++m == 13) { + m = 1; + ++y; + } + } + break; + + case 'W': + d += 7; + days = Datetime::daysInMonth(y, m); + if (d > days) { + d -= days; + + if (++m == 13) { + m = 1; + ++y; + } + } + break; + + case 'M': d = 1; - - if (++m == 13) - { + if (++m == 13) { m = 1; ++y; } - } - break; - - case 'W': - d += 7; - days = Datetime::daysInMonth (y, m); - if (d > days) - { - d -= days; - - if (++m == 13) - { - m = 1; - ++y; - } - } - break; - - case 'M': - d = 1; - if (++m == 13) - { - m = 1; - ++y; - } - break; + break; } - return Datetime (y, m, d, 0, 0, 0); + return Datetime(y, m, d, 0, 0, 0); } //////////////////////////////////////////////////////////////////////////////// -Datetime Chart::decrement (const Datetime& input, char period) -{ +Datetime Chart::decrement(const Datetime& input, char period) { // Move to the previous period. - int d = input.day (); - int m = input.month (); - int y = input.year (); + int d = input.day(); + int m = input.month(); + int y = input.year(); - switch (period) - { - case 'D': - if (--d == 0) - { - if (--m == 0) - { + switch (period) { + case 'D': + if (--d == 0) { + if (--m == 0) { + m = 12; + --y; + } + + d = Datetime::daysInMonth(y, m); + } + break; + + case 'W': + d -= 7; + if (d < 1) { + if (--m == 0) { + m = 12; + y--; + } + + d += Datetime::daysInMonth(y, m); + } + break; + + case 'M': + d = 1; + if (--m == 0) { m = 12; --y; } - - d = Datetime::daysInMonth (y, m); - } - break; - - case 'W': - d -= 7; - if (d < 1) - { - if (--m == 0) - { - m = 12; - y--; - } - - d += Datetime::daysInMonth (y, m); - } - break; - - case 'M': - d = 1; - if (--m == 0) - { - m = 12; - --y; - } - break; + break; } - return Datetime (y, m, d, 0, 0, 0); + return Datetime(y, m, d, 0, 0, 0); } //////////////////////////////////////////////////////////////////////////////// // Do '_bars[epoch] = Bar' for every bar that may appear on a chart. -void Chart::generateBars () -{ +void Chart::generateBars() { Bar bar; // Determine the last bar date. Datetime cursor; - switch (_period) - { - case 'D': cursor = Datetime ().startOfDay (); break; - case 'W': cursor = Datetime ().startOfWeek (); break; - case 'M': cursor = Datetime ().startOfMonth (); break; + switch (_period) { + case 'D': + cursor = Datetime().startOfDay(); + break; + case 'W': + cursor = Datetime().startOfWeek(); + break; + case 'M': + cursor = Datetime().startOfMonth(); + break; } // Iterate and determine all the other bar dates. char str[12]; - for (int i = 0; i < _estimated_bars; ++i) - { + for (int i = 0; i < _estimated_bars; ++i) { // Create the major and minor labels. - switch (_period) - { - case 'D': // month/day + switch (_period) { + case 'D': // month/day { - std::string month = Datetime::monthName (cursor.month ()); - bar._major_label = month.substr (0, 3); + std::string month = Datetime::monthName(cursor.month()); + bar._major_label = month.substr(0, 3); - snprintf (str, 12, "%02d", cursor.day ()); + snprintf(str, 12, "%02d", cursor.day()); bar._minor_label = str; - } - break; + } break; - case 'W': // year/week - snprintf (str, 12, "%d", cursor.year ()); - bar._major_label = str; + case 'W': // year/week + snprintf(str, 12, "%d", cursor.year()); + bar._major_label = str; - snprintf (str, 12, "%02d", cursor.week ()); - bar._minor_label = str; - break; + snprintf(str, 12, "%02d", cursor.week()); + bar._minor_label = str; + break; - case 'M': // year/month - snprintf (str, 12, "%d", cursor.year ()); - bar._major_label = str; + case 'M': // year/month + snprintf(str, 12, "%d", cursor.year()); + bar._major_label = str; - snprintf (str, 12, "%02d", cursor.month ()); - bar._minor_label = str; - break; + snprintf(str, 12, "%02d", cursor.month()); + bar._minor_label = str; + break; } bar._offset = i; - _bars[cursor.toEpoch ()] = bar; + _bars[cursor.toEpoch()] = bar; // Record the earliest date, for use as a cutoff when scanning data. _earliest = cursor; // Move to the previous period. - cursor = decrement (cursor, _period); + cursor = decrement(cursor, _period); } } //////////////////////////////////////////////////////////////////////////////// -void Chart::maxima () -{ +void Chart::maxima() { _max_value = 0; _max_label = 1; - for (auto& bar : _bars) - { + for (auto& bar : _bars) { // Determine _max_label. - int total = bar.second._pending + - bar.second._started + - bar.second._done + - _carryover_done; + int total = bar.second._pending + bar.second._started + bar.second._done + _carryover_done; // Determine _max_value. - if (total > _max_value) - _max_value = total; + if (total > _max_value) _max_value = total; - int length = (int) log10 ((double) total) + 1; - if (length > _max_label) - _max_label = length; + int length = (int)log10((double)total) + 1; + if (length > _max_label) _max_label = length; } // How many bars can be shown? @@ -735,241 +665,199 @@ void Chart::maxima () //////////////////////////////////////////////////////////////////////////////// // Given the vertical chart area size (graph_height), the largest value // (_max_value), populate a vector of labels for the y axis. -void Chart::yLabels (std::vector & labels) -{ +void Chart::yLabels(std::vector& labels) { // Calculate may Y using a nice algorithm that rounds the data. - int high = burndown_size (_max_value); + int high = burndown_size(_max_value); int half = high / 2; - labels.push_back (0); - labels.push_back (half); - labels.push_back (high); + labels.push_back(0); + labels.push_back(half); + labels.push_back(high); } //////////////////////////////////////////////////////////////////////////////// -void Chart::calculateRates () -{ +void Chart::calculateRates() { // Q: Why is this equation written out as a debug message? // A: People are going to want to know how the rates and the completion date // are calculated. This may also help debugging. std::stringstream peak_message; - peak_message << "Chart::calculateRates Maximum of " - << _peak_count - << " pending tasks on " - << (Datetime (_peak_epoch).toISO ()) - << ", with currently " - << _current_count + peak_message << "Chart::calculateRates Maximum of " << _peak_count << " pending tasks on " + << (Datetime(_peak_epoch).toISO()) << ", with currently " << _current_count << " pending tasks"; - Context::getContext ().debug (peak_message.str ()); + Context::getContext().debug(peak_message.str()); // If there are no current pending tasks, then it is meaningless to find // rates or estimated completion date. - if (_current_count == 0) - return; + if (_current_count == 0) return; // If there is a net fix rate, and the peak was at least three days ago. Datetime now; - Datetime peak (_peak_epoch); - if (_peak_count > _current_count && - (now - peak) > 3 * 86400) - { + Datetime peak(_peak_epoch); + if (_peak_count > _current_count && (now - peak) > 3 * 86400) { // Fixes per second. Not a large number. - auto fix_rate = 1.0 * (_peak_count - _current_count) / (now.toEpoch () - _peak_epoch); + auto fix_rate = 1.0 * (_peak_count - _current_count) / (now.toEpoch() - _peak_epoch); _net_fix_rate = fix_rate * 86400; std::stringstream rate_message; - rate_message << "Chart::calculateRates Net reduction is " - << (_peak_count - _current_count) - << " tasks in " - << Duration (now.toEpoch () - _peak_epoch).formatISO () - << " = " - << _net_fix_rate - << " tasks/d"; - Context::getContext ().debug (rate_message.str ()); + rate_message << "Chart::calculateRates Net reduction is " << (_peak_count - _current_count) + << " tasks in " << Duration(now.toEpoch() - _peak_epoch).formatISO() << " = " + << _net_fix_rate << " tasks/d"; + Context::getContext().debug(rate_message.str()); - Duration delta (static_cast (_current_count / fix_rate)); - Datetime end = now + delta.toTime_t (); + Duration delta(static_cast(_current_count / fix_rate)); + Datetime end = now + delta.toTime_t(); // Prefer dateformat.report over dateformat. - std::string format = Context::getContext ().config.get ("dateformat.report"); - if (format == "") - { - format = Context::getContext ().config.get ("dateformat"); - if (format == "") - format = "Y-M-D"; + std::string format = Context::getContext().config.get("dateformat.report"); + if (format == "") { + format = Context::getContext().config.get("dateformat"); + if (format == "") format = "Y-M-D"; } - _completion = end.toString (format) - + " (" - + delta.formatVague () - + ')'; + _completion = end.toString(format) + " (" + delta.formatVague() + ')'; std::stringstream completion_message; - completion_message << "Chart::calculateRates (" - << _current_count - << " tasks / " - << _net_fix_rate - << ") = " - << delta.format () - << " --> " - << end.toISO (); - Context::getContext ().debug (completion_message.str ()); - } - else - { + completion_message << "Chart::calculateRates (" << _current_count << " tasks / " + << _net_fix_rate << ") = " << delta.format() << " --> " << end.toISO(); + Context::getContext().debug(completion_message.str()); + } else { _completion = "No convergence"; } } //////////////////////////////////////////////////////////////////////////////// -unsigned Chart::round_up_to (unsigned n, unsigned target) -{ - return n + target - (n % target); -} +unsigned Chart::round_up_to(unsigned n, unsigned target) { return n + target - (n % target); } //////////////////////////////////////////////////////////////////////////////// -unsigned Chart::burndown_size (unsigned ntasks) -{ +unsigned Chart::burndown_size(unsigned ntasks) { // Nearest 2 - if (ntasks < 20) - return round_up_to (ntasks, 2); + if (ntasks < 20) return round_up_to(ntasks, 2); // Nearest 10 - if (ntasks < 50) - return round_up_to (ntasks, 10); + if (ntasks < 50) return round_up_to(ntasks, 10); // Nearest 20 - if (ntasks < 100) - return round_up_to (ntasks, 20); + if (ntasks < 100) return round_up_to(ntasks, 20); // Choose the number from here rounded up to the nearest 10% of the next // highest power of 10 or half of power of 10. - const auto count = (unsigned) log10 (static_cast(std::numeric_limits::max ())); + const auto count = (unsigned)log10(static_cast(std::numeric_limits::max())); unsigned half = 500; unsigned full = 1000; // We start at two because we handle 5, 10, 50, and 100 above. - for (unsigned i = 2; i < count; ++i) - { - if (ntasks < half) - return round_up_to (ntasks, half / 10); + for (unsigned i = 2; i < count; ++i) { + if (ntasks < half) return round_up_to(ntasks, half / 10); - if (ntasks < full) - return round_up_to (ntasks, full / 10); + if (ntasks < full) return round_up_to(ntasks, full / 10); half *= 10; full *= 10; } // Round up to max of unsigned. - return std::numeric_limits::max (); + return std::numeric_limits::max(); } //////////////////////////////////////////////////////////////////////////////// -CmdBurndownMonthly::CmdBurndownMonthly () -{ - _keyword = "burndown.monthly"; - _usage = "task burndown.monthly"; - _description = "Shows a graphical burndown chart, by month"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = true; - _accepts_filter = true; +CmdBurndownMonthly::CmdBurndownMonthly() { + _keyword = "burndown.monthly"; + _usage = "task burndown.monthly"; + _description = "Shows a graphical burndown chart, by month"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::graphs; + _category = Command::Category::graphs; } //////////////////////////////////////////////////////////////////////////////// -int CmdBurndownMonthly::execute (std::string& output) -{ +int CmdBurndownMonthly::execute(std::string& output) { int rc = 0; // Scan the pending tasks, applying any filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); // Create a chart, scan the tasks, then render. - Chart chart ('M'); - chart.scanForPeak (filtered); - chart.scan (filtered); - output = chart.render (); + Chart chart('M'); + chart.scanForPeak(filtered); + chart.scan(filtered); + output = chart.render(); return rc; } //////////////////////////////////////////////////////////////////////////////// -CmdBurndownWeekly::CmdBurndownWeekly () -{ - _keyword = "burndown.weekly"; - _usage = "task burndown.weekly"; - _description = "Shows a graphical burndown chart, by week"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = true; - _accepts_filter = true; +CmdBurndownWeekly::CmdBurndownWeekly() { + _keyword = "burndown.weekly"; + _usage = "task burndown.weekly"; + _description = "Shows a graphical burndown chart, by week"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::graphs; + _category = Command::Category::graphs; } //////////////////////////////////////////////////////////////////////////////// -int CmdBurndownWeekly::execute (std::string& output) -{ +int CmdBurndownWeekly::execute(std::string& output) { int rc = 0; // Scan the pending tasks, applying any filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); // Create a chart, scan the tasks, then render. - Chart chart ('W'); - chart.scanForPeak (filtered); - chart.scan (filtered); - output = chart.render (); + Chart chart('W'); + chart.scanForPeak(filtered); + chart.scan(filtered); + output = chart.render(); return rc; } //////////////////////////////////////////////////////////////////////////////// -CmdBurndownDaily::CmdBurndownDaily () -{ - _keyword = "burndown.daily"; - _usage = "task burndown.daily"; - _description = "Shows a graphical burndown chart, by day"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = true; - _accepts_filter = true; +CmdBurndownDaily::CmdBurndownDaily() { + _keyword = "burndown.daily"; + _usage = "task burndown.daily"; + _description = "Shows a graphical burndown chart, by day"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::graphs; + _category = Command::Category::graphs; } //////////////////////////////////////////////////////////////////////////////// -int CmdBurndownDaily::execute (std::string& output) -{ +int CmdBurndownDaily::execute(std::string& output) { int rc = 0; // Scan the pending tasks, applying any filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); // Create a chart, scan the tasks, then render. - Chart chart ('D'); - chart.scanForPeak (filtered); - chart.scan (filtered); - output = chart.render (); + Chart chart('D'); + chart.scanForPeak(filtered); + chart.scan(filtered); + output = chart.render(); return rc; } diff --git a/src/commands/CmdBurndown.h b/src/commands/CmdBurndown.h index 50a2f5336..818b01ea2 100644 --- a/src/commands/CmdBurndown.h +++ b/src/commands/CmdBurndown.h @@ -27,28 +27,26 @@ #ifndef INCLUDED_CMDBURNDOWN #define INCLUDED_CMDBURNDOWN -#include #include -class CmdBurndownMonthly : public Command -{ -public: - CmdBurndownMonthly (); - int execute (std::string&); +#include + +class CmdBurndownMonthly : public Command { + public: + CmdBurndownMonthly(); + int execute(std::string&); }; -class CmdBurndownWeekly : public Command -{ -public: - CmdBurndownWeekly (); - int execute (std::string&); +class CmdBurndownWeekly : public Command { + public: + CmdBurndownWeekly(); + int execute(std::string&); }; -class CmdBurndownDaily : public Command -{ -public: - CmdBurndownDaily (); - int execute (std::string&); +class CmdBurndownDaily : public Command { + public: + CmdBurndownDaily(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdCalc.cpp b/src/commands/CmdCalc.cpp index bb79a54a4..3ed55e408 100644 --- a/src/commands/CmdCalc.cpp +++ b/src/commands/CmdCalc.cpp @@ -24,53 +24,49 @@ // //////////////////////////////////////////////////////////////////////////////// -#include #include -#include +#include #include +#include //////////////////////////////////////////////////////////////////////////////// -CmdCalc::CmdCalc () -{ - _keyword = "calc"; - _usage = "task calc "; - _description = "Calculator"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdCalc::CmdCalc() { + _keyword = "calc"; + _usage = "task calc "; + _description = "Calculator"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::misc; + _category = Command::Category::misc; } //////////////////////////////////////////////////////////////////////////////// -int CmdCalc::execute (std::string& output) -{ +int CmdCalc::execute(std::string& output) { // Configurable infix/postfix - bool infix {true}; - if (Context::getContext ().config.get ("expressions") == "postfix") - infix = false; + bool infix{true}; + if (Context::getContext().config.get("expressions") == "postfix") infix = false; // Create an evaluator with DOM access. Eval e; - e.addSource (domSource); - e.debug (Context::getContext ().config.getBoolean ("debug")); + e.addSource(domSource); + e.debug(Context::getContext().config.getBoolean("debug")); // Compile all the args into one expression. std::string expression; - for (const auto& word : Context::getContext ().cli2.getWords ()) - expression += word + ' '; + for (const auto& word : Context::getContext().cli2.getWords()) expression += word + ' '; // Evaluate according to preference. Variant result; if (infix) - e.evaluateInfixExpression (expression, result); + e.evaluateInfixExpression(expression, result); else - e.evaluatePostfixExpression (expression, result); + e.evaluatePostfixExpression(expression, result); - output = (std::string) result + '\n'; + output = (std::string)result + '\n'; return 0; } diff --git a/src/commands/CmdCalc.h b/src/commands/CmdCalc.h index 442e043b9..930abd45b 100644 --- a/src/commands/CmdCalc.h +++ b/src/commands/CmdCalc.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDCALC #define INCLUDED_CMDCALC -#include #include -class CmdCalc : public Command -{ -public: - CmdCalc (); - int execute (std::string&); +#include + +class CmdCalc : public Command { + public: + CmdCalc(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdCalendar.cpp b/src/commands/CmdCalendar.cpp index 179ea8a6c..3fae4394d 100644 --- a/src/commands/CmdCalendar.cpp +++ b/src/commands/CmdCalendar.cpp @@ -28,51 +28,50 @@ // cmake.h include header must come first #include -#include -#include -#include #include -#include #include -#include +#include #include -#include -#include #include +#include +#include +#include +#include + +#include +#include //////////////////////////////////////////////////////////////////////////////// -CmdCalendar::CmdCalendar () -{ - _keyword = "calendar"; - _usage = "task calendar [due| |] [y]"; - _description = "Shows a calendar, with due tasks marked"; - _read_only = true; - _displays_id = true; - _needs_gc = true; - _uses_context = false; - _accepts_filter = false; +CmdCalendar::CmdCalendar() { + _keyword = "calendar"; + _usage = "task calendar [due| |] [y]"; + _description = "Shows a calendar, with due tasks marked"; + _read_only = true; + _displays_id = true; + _needs_gc = true; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::graphs; + _category = Command::Category::graphs; } //////////////////////////////////////////////////////////////////////////////// -int CmdCalendar::execute (std::string& output) -{ +int CmdCalendar::execute(std::string& output) { int rc = 0; - auto& config = Context::getContext ().config; + auto& config = Context::getContext().config; // Each month requires 28 text columns width. See how many will actually // fit. But if a preference is specified, and it fits, use it. - auto width = Context::getContext ().getWidth (); + auto width = Context::getContext().getWidth(); int preferredMonthsPerLine; - if (config.has ("calendar.monthsperline")) - preferredMonthsPerLine = config.getInteger ("calendar.monthsperline"); + if (config.has("calendar.monthsperline")) + preferredMonthsPerLine = config.getInteger("calendar.monthsperline"); else // Legacy configuration variable value - preferredMonthsPerLine = config.getInteger ("monthsperline"); + preferredMonthsPerLine = config.getInteger("monthsperline"); auto monthsThatFit = width / 26; @@ -81,78 +80,75 @@ int CmdCalendar::execute (std::string& output) monthsPerLine = preferredMonthsPerLine; // Load the pending tasks. - handleUntil (); - handleRecurrence (); - auto tasks = Context::getContext ().tdb2.pending_tasks (); + handleUntil(); + handleRecurrence(); + auto tasks = Context::getContext().tdb2.pending_tasks(); Datetime today; auto getPendingDate = false; auto monthsToDisplay = 1; - auto mFrom = today.month (); - auto yFrom = today.year (); + auto mFrom = today.month(); + auto yFrom = today.year(); auto mTo = mFrom; auto yTo = yFrom; // Defaults. monthsToDisplay = monthsPerLine; - mFrom = today.month (); - yFrom = today.year (); + mFrom = today.month(); + yFrom = today.year(); // Set up a vector of commands (1), for autoComplete. - std::vector commandNames {"calendar"}; + std::vector commandNames{"calendar"}; // Set up a vector of keywords, for autoComplete. - std::vector keywordNames {"due"}; + std::vector keywordNames{"due"}; // Set up a vector of months, for autoComplete. - std::vector monthNames; - for (int i = 1; i <= 12; ++i) - monthNames.push_back (Lexer::lowerCase (Datetime::monthName (i))); + std::vector monthNames; + for (int i = 1; i <= 12; ++i) monthNames.push_back(Lexer::lowerCase(Datetime::monthName(i))); // For autoComplete results. - std::vector matches; + std::vector matches; // Look at all args, regardless of sequence. auto argMonth = 0; auto argYear = 0; auto argWholeYear = false; - for (auto& arg : Context::getContext ().cli2.getWords ()) - { + for (auto& arg : Context::getContext().cli2.getWords()) { // Some version of "calendar". - if (autoComplete (Lexer::lowerCase (arg), commandNames, matches, config.getInteger ("abbreviation.minimum")) == 1) + if (autoComplete(Lexer::lowerCase(arg), commandNames, matches, + config.getInteger("abbreviation.minimum")) == 1) continue; // "due". - else if (autoComplete (Lexer::lowerCase (arg), keywordNames, matches, config.getInteger ("abbreviation.minimum")) == 1) + else if (autoComplete(Lexer::lowerCase(arg), keywordNames, matches, + config.getInteger("abbreviation.minimum")) == 1) getPendingDate = true; // "y". - else if (Lexer::lowerCase (arg) == "y") + else if (Lexer::lowerCase(arg) == "y") argWholeYear = true; // YYYY. - else if (Lexer::isAllDigits (arg) && arg.length () == 4) - argYear = strtol (arg.c_str (), nullptr, 10); + else if (Lexer::isAllDigits(arg) && arg.length() == 4) + argYear = strtol(arg.c_str(), nullptr, 10); // MM. - else if (Lexer::isAllDigits (arg) && arg.length () <= 2) - { - argMonth = strtol (arg.c_str (), nullptr, 10); - if (argMonth < 1 || argMonth > 12) - throw format ("Argument '{1}' is not a valid month.", arg); + else if (Lexer::isAllDigits(arg) && arg.length() <= 2) { + argMonth = strtol(arg.c_str(), nullptr, 10); + if (argMonth < 1 || argMonth > 12) throw format("Argument '{1}' is not a valid month.", arg); } // "January" etc. - else if (autoComplete (Lexer::lowerCase (arg), monthNames, matches, config.getInteger ("abbreviation.minimum")) == 1) - { - argMonth = Datetime::monthOfYear (matches[0]); - if (argMonth == -1) - throw format ("Argument '{1}' is not a valid month.", arg); + else if (autoComplete(Lexer::lowerCase(arg), monthNames, matches, + config.getInteger("abbreviation.minimum")) == 1) { + argMonth = Datetime::monthOfYear(matches[0]); + if (argMonth == -1) throw format("Argument '{1}' is not a valid month.", arg); } else - throw format ("Could not recognize argument '{1}'.", arg); + throw format("Could not recognize argument '{1}'.", arg); } // Supported combinations: @@ -167,56 +163,45 @@ int CmdCalendar::execute (std::string& output) // cal MM YYYY monthsPerLine arg arg false // cal MM YYYY y 12 arg arg false - if (argWholeYear || (argYear && !argMonth && !argWholeYear)) - monthsToDisplay = 12; + if (argWholeYear || (argYear && !argMonth && !argWholeYear)) monthsToDisplay = 12; if (!argMonth && argYear) mFrom = 1; else if (argMonth && argYear) mFrom = argMonth; - if (argYear) - yFrom = argYear; + if (argYear) yFrom = argYear; // Now begin the data subset and rendering. - if (getPendingDate == true) - { + if (getPendingDate == true) { // Find the oldest pending due date. - Datetime oldest (9999, 12, 31); - for (auto& task : tasks) - { - auto status = task.getStatus (); - if (status == Task::pending || status == Task::waiting) - { - if (task.has ("due") && - !task.hasTag ("nocal")) - { - Datetime d (task.get ("due")); + Datetime oldest(9999, 12, 31); + for (auto& task : tasks) { + auto status = task.getStatus(); + if (status == Task::pending || status == Task::waiting) { + if (task.has("due") && !task.hasTag("nocal")) { + Datetime d(task.get("due")); if (d < oldest) oldest = d; } } } // Default to current month if no due date is present - if (oldest != Datetime (9999, 12, 31)) { + if (oldest != Datetime(9999, 12, 31)) { mFrom = oldest.month(); yFrom = oldest.year(); } } - if (config.getBoolean ("calendar.offset")) - { - auto moffset = config.getInteger ("calendar.offset.value") % 12; - auto yoffset = config.getInteger ("calendar.offset.value") / 12; + if (config.getBoolean("calendar.offset")) { + auto moffset = config.getInteger("calendar.offset.value") % 12; + auto yoffset = config.getInteger("calendar.offset.value") / 12; mFrom += moffset; yFrom += yoffset; - if (mFrom < 1) - { + if (mFrom < 1) { mFrom += 12; yFrom--; - } - else if (mFrom > 12) - { + } else if (mFrom > 12) { mFrom -= 12; yFrom++; } @@ -224,8 +209,7 @@ int CmdCalendar::execute (std::string& output) mTo = mFrom + monthsToDisplay - 1; yTo = yFrom; - if (mTo > 12) - { + if (mTo > 12) { mTo -= 12; yTo++; } @@ -236,15 +220,13 @@ int CmdCalendar::execute (std::string& output) std::stringstream out; out << '\n'; - while (yFrom < yTo || (yFrom == yTo && mFrom <= mTo)) - { + while (yFrom < yTo || (yFrom == yTo && mFrom <= mTo)) { auto nextM = mFrom; auto nextY = yFrom; // Print month headers (cheating on the width settings, yes) - for (int i = 0 ; i < monthsPerLine ; i++) - { - auto month = Datetime::monthName (nextM); + for (int i = 0; i < monthsPerLine; i++) { + auto month = Datetime::monthName(nextM); // 12345678901234567890123456 = 26 chars wide // ^^ = center @@ -261,253 +243,207 @@ int CmdCalendar::execute (std::string& output) // +--------------------------+ auto totalWidth = 26; - auto labelWidth = month.length () + 5; // 5 = " 2009" + auto labelWidth = month.length() + 5; // 5 = " 2009" auto leftGap = (totalWidth / 2) - (labelWidth / 2); auto rightGap = totalWidth - leftGap - labelWidth; - out << std::setw (leftGap) << ' ' - << month - << ' ' - << nextY - << std::setw (rightGap) << ' '; + out << std::setw(leftGap) << ' ' << month << ' ' << nextY << std::setw(rightGap) << ' '; - if (++nextM > 12) - { + if (++nextM > 12) { nextM = 1; nextY++; } } out << '\n' - << optionalBlankLine () - << renderMonths (mFrom, yFrom, today, tasks, monthsPerLine) - << '\n'; + << optionalBlankLine() << renderMonths(mFrom, yFrom, today, tasks, monthsPerLine) << '\n'; mFrom += monthsPerLine; - if (mFrom > 12) - { + if (mFrom > 12) { mFrom -= 12; ++yFrom; } } - Color color_today (config.get ("color.calendar.today")); - Color color_due (config.get ("color.calendar.due")); - Color color_duetoday (config.get ("color.calendar.due.today")); - Color color_overdue (config.get ("color.calendar.overdue")); - Color color_weekend (config.get ("color.calendar.weekend")); - Color color_holiday (config.get ("color.calendar.holiday")); - Color color_scheduled (config.get ("color.calendar.scheduled")); - Color color_weeknumber (config.get ("color.calendar.weeknumber")); + Color color_today(config.get("color.calendar.today")); + Color color_due(config.get("color.calendar.due")); + Color color_duetoday(config.get("color.calendar.due.today")); + Color color_overdue(config.get("color.calendar.overdue")); + Color color_weekend(config.get("color.calendar.weekend")); + Color color_holiday(config.get("color.calendar.holiday")); + Color color_scheduled(config.get("color.calendar.scheduled")); + Color color_weeknumber(config.get("color.calendar.weeknumber")); - if (Context::getContext ().color () && config.getBoolean ("calendar.legend")) - { - out << "Legend: " - << color_today.colorize ("today") - << ", " - << color_weekend.colorize ("weekend") + if (Context::getContext().color() && config.getBoolean("calendar.legend")) { + out << "Legend: " << color_today.colorize("today") << ", " << color_weekend.colorize("weekend") << ", "; // If colorizing due dates, print legend - if (config.get ("calendar.details") != "none") - out << color_due.colorize ("due") - << ", " - << color_duetoday.colorize ("due-today") - << ", " - << color_overdue.colorize ("overdue") - << ", " - << color_scheduled.colorize ("scheduled") + if (config.get("calendar.details") != "none") + out << color_due.colorize("due") << ", " << color_duetoday.colorize("due-today") << ", " + << color_overdue.colorize("overdue") << ", " << color_scheduled.colorize("scheduled") << ", "; // If colorizing holidays, print legend - if (config.get ("calendar.holidays") != "none") - out << color_holiday.colorize ("holiday") << ", "; + if (config.get("calendar.holidays") != "none") out << color_holiday.colorize("holiday") << ", "; - out << color_weeknumber.colorize ("weeknumber") - << '.' - << optionalBlankLine () - << '\n'; + out << color_weeknumber.colorize("weeknumber") << '.' << optionalBlankLine() << '\n'; } - if (config.get ("calendar.details") == "full" || config.get ("calendar.holidays") == "full") - { + if (config.get("calendar.details") == "full" || config.get("calendar.holidays") == "full") { --details_mFrom; - if (details_mFrom == 0) - { + if (details_mFrom == 0) { details_mFrom = 12; --details_yFrom; } - int details_dFrom = Datetime::daysInMonth (details_yFrom, details_mFrom); + int details_dFrom = Datetime::daysInMonth(details_yFrom, details_mFrom); ++mTo; - if (mTo == 13) - { + if (mTo == 13) { mTo = 1; ++yTo; } - Datetime date_after (details_yFrom, details_mFrom, details_dFrom); - auto after = date_after.toString (config.get ("dateformat")); + Datetime date_after(details_yFrom, details_mFrom, details_dFrom); + auto after = date_after.toString(config.get("dateformat")); - Datetime date_before (yTo, mTo, 1); - auto before = date_before.toString (config.get ("dateformat")); + Datetime date_before(yTo, mTo, 1); + auto before = date_before.toString(config.get("dateformat")); // Table with due date information - if (config.get ("calendar.details") == "full") - { + if (config.get("calendar.details") == "full") { // Assert that 'report' is a valid report. - auto report = config.get ("calendar.details.report"); - if (Context::getContext ().commands.find (report) == Context::getContext ().commands.end ()) - throw std::string ("The setting 'calendar.details.report' must contain a single report name."); + auto report = config.get("calendar.details.report"); + if (Context::getContext().commands.find(report) == Context::getContext().commands.end()) + throw std::string( + "The setting 'calendar.details.report' must contain a single report name."); // TODO Fix this: cal --> task // calendar --> taskendar // If the executable was "cal" or equivalent, replace it with "task". - auto executable = Context::getContext ().cli2._original_args[0].attribute ("raw"); - auto cal = executable.find ("cal"); - if (cal != std::string::npos) - executable = executable.substr (0, cal) + PACKAGE; + auto executable = Context::getContext().cli2._original_args[0].attribute("raw"); + auto cal = executable.find("cal"); + if (cal != std::string::npos) executable = executable.substr(0, cal) + PACKAGE; - std::vector args; - args.push_back ("rc:" + Context::getContext ().rc_file._data); - args.push_back ("rc.due:0"); - args.push_back ("rc.verbose:label,affected,blank"); - if (Context::getContext ().color ()) - args.push_back ("rc._forcecolor:on"); - args.push_back ("due.after:" + after); - args.push_back ("due.before:" + before); - args.push_back ("-nocal"); - args.push_back (report); + std::vector args; + args.push_back("rc:" + Context::getContext().rc_file._data); + args.push_back("rc.due:0"); + args.push_back("rc.verbose:label,affected,blank"); + if (Context::getContext().color()) args.push_back("rc._forcecolor:on"); + args.push_back("due.after:" + after); + args.push_back("due.before:" + before); + args.push_back("-nocal"); + args.push_back(report); std::string output; - ::execute (executable, args, "", output); + ::execute(executable, args, "", output); out << output; } // Table with holiday information - if (config.get ("calendar.holidays") == "full") - { + if (config.get("calendar.holidays") == "full") { Table holTable; - holTable.width (Context::getContext ().getWidth ()); - holTable.add ("Date"); - holTable.add ("Holiday"); - setHeaderUnderline (holTable); + holTable.width(Context::getContext().getWidth()); + holTable.add("Date"); + holTable.add("Holiday"); + setHeaderUnderline(holTable); - auto dateFormat = config.get ("dateformat.holiday"); + auto dateFormat = config.get("dateformat.holiday"); - std::map > hm; // we need to store multiple holidays per day + std::map> hm; // we need to store multiple holidays per day for (auto& it : config) - if (it.first.substr (0, 8) == "holiday.") - if (it.first.substr (it.first.size () - 4) == "name") - { + if (it.first.substr(0, 8) == "holiday.") + if (it.first.substr(it.first.size() - 4) == "name") { auto holName = it.second; - auto date = config.get ("holiday." + it.first.substr (8, it.first.size () - 13) + ".date"); - auto start = config.get ("holiday." + it.first.substr (8, it.first.size () - 13) + ".start"); - auto end = config.get ("holiday." + it.first.substr (8, it.first.size () - 13) + ".end"); - if (!date.empty ()) - { - Datetime holDate (date.c_str (), dateFormat); + auto date = config.get("holiday." + it.first.substr(8, it.first.size() - 13) + ".date"); + auto start = + config.get("holiday." + it.first.substr(8, it.first.size() - 13) + ".start"); + auto end = config.get("holiday." + it.first.substr(8, it.first.size() - 13) + ".end"); + if (!date.empty()) { + Datetime holDate(date.c_str(), dateFormat); if (date_after < holDate && holDate < date_before) - hm[holDate.toEpoch()].push_back (holName); + hm[holDate.toEpoch()].push_back(holName); } - if (!start.empty () && !end.empty ()) - { - Datetime holStart (start.c_str (), dateFormat); - Datetime holEnd (end.c_str (), dateFormat); + if (!start.empty() && !end.empty()) { + Datetime holStart(start.c_str(), dateFormat); + Datetime holEnd(end.c_str(), dateFormat); if (date_after < holStart && holStart < date_before) - hm[holStart.toEpoch()].push_back ("Start of " + holName); + hm[holStart.toEpoch()].push_back("Start of " + holName); if (date_after < holEnd && holEnd < date_before) - hm[holEnd.toEpoch()].push_back ("End of " + holName); + hm[holEnd.toEpoch()].push_back("End of " + holName); } } - auto format = config.get ("report." + - config.get ("calendar.details.report") + - ".dateformat"); - if (format == "") - format = config.get ("dateformat.report"); - if (format == "") - format = config.get ("dateformat"); + auto format = config.get("report." + config.get("calendar.details.report") + ".dateformat"); + if (format == "") format = config.get("dateformat.report"); + if (format == "") format = config.get("dateformat"); - for (auto& hm_it : hm) - { + for (auto& hm_it : hm) { auto v = hm_it.second; - Datetime hDate (hm_it.first); - auto d = hDate.toString (format); - for (const auto& i : v) - { - auto row = holTable.addRow (); - holTable.set (row, 0, d); - holTable.set (row, 1, i); + Datetime hDate(hm_it.first); + auto d = hDate.toString(format); + for (const auto& i : v) { + auto row = holTable.addRow(); + holTable.set(row, 0, d); + holTable.set(row, 1, i); } } - out << optionalBlankLine () - << holTable.render () - << '\n'; + out << optionalBlankLine() << holTable.render() << '\n'; } } - output = out.str (); + output = out.str(); return rc; } //////////////////////////////////////////////////////////////////////////////// -std::string CmdCalendar::renderMonths ( - int firstMonth, - int firstYear, - const Datetime& today, - std::vector & all, - int monthsPerLine) -{ - - auto& config = Context::getContext ().config; +std::string CmdCalendar::renderMonths(int firstMonth, int firstYear, const Datetime& today, + std::vector& all, int monthsPerLine) { + auto& config = Context::getContext().config; // What day of the week does the user consider the first? - auto weekStart = Datetime::dayOfWeek (config.get ("weekstart")); + auto weekStart = Datetime::dayOfWeek(config.get("weekstart")); if (weekStart != 0 && weekStart != 1) - throw std::string ("The 'weekstart' configuration variable may only contain 'Sunday' or 'Monday'."); + throw std::string( + "The 'weekstart' configuration variable may only contain 'Sunday' or 'Monday'."); // Build table for the number of months to be displayed. Table view; - setHeaderUnderline (view); - view.width (Context::getContext ().getWidth ()); - for (int i = 0 ; i < (monthsPerLine * 8); i += 8) - { - if (weekStart == 1) - { - view.add ("", false); - view.add (utf8_substr (Datetime::dayName (1), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (2), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (3), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (4), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (5), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (6), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (0), 0, 2), false); - } - else - { - view.add ("", false); - view.add (utf8_substr (Datetime::dayName (0), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (1), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (2), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (3), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (4), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (5), 0, 2), false); - view.add (utf8_substr (Datetime::dayName (6), 0, 2), false); + setHeaderUnderline(view); + view.width(Context::getContext().getWidth()); + for (int i = 0; i < (monthsPerLine * 8); i += 8) { + if (weekStart == 1) { + view.add("", false); + view.add(utf8_substr(Datetime::dayName(1), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(2), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(3), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(4), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(5), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(6), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(0), 0, 2), false); + } else { + view.add("", false); + view.add(utf8_substr(Datetime::dayName(0), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(1), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(2), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(3), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(4), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(5), 0, 2), false); + view.add(utf8_substr(Datetime::dayName(6), 0, 2), false); } } // At most, we need 6 rows. - view.addRow (); - view.addRow (); - view.addRow (); - view.addRow (); - view.addRow (); - view.addRow (); + view.addRow(); + view.addRow(); + view.addRow(); + view.addRow(); + view.addRow(); + view.addRow(); // Set number of days per month, months to render, and years to render. std::vector years; @@ -515,153 +451,126 @@ std::string CmdCalendar::renderMonths ( std::vector daysInMonth; int thisYear = firstYear; int thisMonth = firstMonth; - for (int i = 0 ; i < monthsPerLine ; i++) - { - if (thisMonth < 13) - { - years.push_back (thisYear); - } - else - { + for (int i = 0; i < monthsPerLine; i++) { + if (thisMonth < 13) { + years.push_back(thisYear); + } else { thisMonth -= 12; - years.push_back (++thisYear); + years.push_back(++thisYear); } - months.push_back (thisMonth); - daysInMonth.push_back (Datetime::daysInMonth (thisYear, thisMonth++)); + months.push_back(thisMonth); + daysInMonth.push_back(Datetime::daysInMonth(thisYear, thisMonth++)); } auto row = 0; - Color color_today (config.get ("color.calendar.today")); - Color color_due (config.get ("color.calendar.due")); - Color color_duetoday (config.get ("color.calendar.due.today")); - Color color_overdue (config.get ("color.calendar.overdue")); - Color color_weekend (config.get ("color.calendar.weekend")); - Color color_holiday (config.get ("color.calendar.holiday")); - Color color_scheduled (config.get ("color.calendar.scheduled")); - Color color_weeknumber (config.get ("color.calendar.weeknumber")); + Color color_today(config.get("color.calendar.today")); + Color color_due(config.get("color.calendar.due")); + Color color_duetoday(config.get("color.calendar.due.today")); + Color color_overdue(config.get("color.calendar.overdue")); + Color color_weekend(config.get("color.calendar.weekend")); + Color color_holiday(config.get("color.calendar.holiday")); + Color color_scheduled(config.get("color.calendar.scheduled")); + Color color_weeknumber(config.get("color.calendar.weeknumber")); // Loop through months to be added on this line. - for (int mpl = 0; mpl < monthsPerLine ; mpl++) - { + for (int mpl = 0; mpl < monthsPerLine; mpl++) { // Reset row counter for subsequent months - if (mpl != 0) - row = 0; + if (mpl != 0) row = 0; // Loop through days in month and add to table. - for (int d = 1; d <= daysInMonth[mpl]; ++d) - { - Datetime date (years[mpl], months[mpl], d); - auto dow = date.dayOfWeek (); - auto woy = date.week (); + for (int d = 1; d <= daysInMonth[mpl]; ++d) { + Datetime date(years[mpl], months[mpl], d); + auto dow = date.dayOfWeek(); + auto woy = date.week(); - if (config.getBoolean ("displayweeknumber")) - view.set (row, - (8 * mpl), - // Make sure the week number is always 4 columns, space-padded. - format ((woy < 10 ? " {1}" : " {1}"), woy), - color_weeknumber); + if (config.getBoolean("displayweeknumber")) + view.set(row, (8 * mpl), + // Make sure the week number is always 4 columns, space-padded. + format((woy < 10 ? " {1}" : " {1}"), woy), color_weeknumber); // Calculate column id. auto thisCol = dow + // 0 = Sunday (weekStart == 1 ? 0 : 1) + // Offset for weekStart (8 * mpl); // Columns in 1 month - if (thisCol == (8 * mpl)) - thisCol += 7; + if (thisCol == (8 * mpl)) thisCol += 7; - view.set (row, thisCol, d); + view.set(row, thisCol, d); - if (Context::getContext ().color ()) - { + if (Context::getContext().color()) { Color cellColor; // colorize weekends - if (dow == 0 || dow == 6) - cellColor.blend (color_weekend); + if (dow == 0 || dow == 6) cellColor.blend(color_weekend); // colorize holidays - if (config.get ("calendar.holidays") != "none") - { - auto dateFormat = config.get ("dateformat.holiday"); - for (auto& hol : config) - { - if (hol.first.substr (0, 8) == "holiday.") - { - if (hol.first.substr (hol.first.size () - 4) == "date") - { + if (config.get("calendar.holidays") != "none") { + auto dateFormat = config.get("dateformat.holiday"); + for (auto& hol : config) { + if (hol.first.substr(0, 8) == "holiday.") { + if (hol.first.substr(hol.first.size() - 4) == "date") { auto value = hol.second; - Datetime holDate (value.c_str (), dateFormat); - if (holDate.sameDay (date)) - cellColor.blend (color_holiday); + Datetime holDate(value.c_str(), dateFormat); + if (holDate.sameDay(date)) cellColor.blend(color_holiday); } - if (hol.first.substr (hol.first.size () - 5) == "start" && - config.has ("holiday." + hol.first.substr (8, hol.first.size () - 14) + ".end")) - { + if (hol.first.substr(hol.first.size() - 5) == "start" && + config.has("holiday." + hol.first.substr(8, hol.first.size() - 14) + ".end")) { auto start = hol.second; - auto end = config.get ("holiday." + hol.first.substr (8, hol.first.size () - 14) + ".end"); - Datetime holStart (start.c_str (), dateFormat); - Datetime holEnd (end.c_str (), dateFormat); - if (holStart <= date && date <= holEnd) - cellColor.blend (color_holiday); + auto end = + config.get("holiday." + hol.first.substr(8, hol.first.size() - 14) + ".end"); + Datetime holStart(start.c_str(), dateFormat); + Datetime holEnd(end.c_str(), dateFormat); + if (holStart <= date && date <= holEnd) cellColor.blend(color_holiday); } } } } // colorize today - if (today.sameDay (date)) - cellColor.blend (color_today); + if (today.sameDay(date)) cellColor.blend(color_today); // colorize due and scheduled tasks - if (config.get ("calendar.details") != "none") - { - config.set ("due", 0); - config.set ("scheduled", 0); + if (config.get("calendar.details") != "none") { + config.set("due", 0); + config.set("scheduled", 0); // if a date has a task that is due on that day, the due color // takes precedence over the scheduled color bool coloredWithDue = false; - for (auto& task : all) - { - auto status = task.getStatus (); - if ((status == Task::pending || - status == Task::waiting ) && - !task.hasTag ("nocal")) - { - if(task.has("scheduled") && !coloredWithDue) { - std::string scheduled = task.get ("scheduled"); - Datetime scheduleddmy (strtoll (scheduled.c_str(), nullptr, 10)); + for (auto& task : all) { + auto status = task.getStatus(); + if ((status == Task::pending || status == Task::waiting) && !task.hasTag("nocal")) { + if (task.has("scheduled") && !coloredWithDue) { + std::string scheduled = task.get("scheduled"); + Datetime scheduleddmy(strtoll(scheduled.c_str(), nullptr, 10)); - if (scheduleddmy.sameDay (date)) - { + if (scheduleddmy.sameDay(date)) { cellColor.blend(color_scheduled); } } - if(task.has("due")) { - std::string due = task.get ("due"); - Datetime duedmy (strtoll (due.c_str(), nullptr, 10)); + if (task.has("due")) { + std::string due = task.get("due"); + Datetime duedmy(strtoll(due.c_str(), nullptr, 10)); - if (duedmy.sameDay (date)) - { + if (duedmy.sameDay(date)) { coloredWithDue = true; - switch (task.getDateState ("due")) - { - case Task::dateNotDue: - break; + switch (task.getDateState("due")) { + case Task::dateNotDue: + break; - case Task::dateAfterToday: - cellColor.blend (color_due); - break; + case Task::dateAfterToday: + cellColor.blend(color_due); + break; - case Task::dateLaterToday: - cellColor.blend (color_duetoday); - break; + case Task::dateLaterToday: + cellColor.blend(color_duetoday); + break; - case Task::dateEarlierToday: - case Task::dateBeforeToday: - cellColor.blend (color_overdue); - break; + case Task::dateEarlierToday: + case Task::dateBeforeToday: + cellColor.blend(color_overdue); + break; } } } @@ -669,19 +578,17 @@ std::string CmdCalendar::renderMonths ( } } - view.set (row, thisCol, cellColor); + view.set(row, thisCol, cellColor); } // Check for end of week, and... int eow = 6; - if (weekStart == 1) - eow = 0; - if (dow == eow && d < daysInMonth[mpl]) - row++; + if (weekStart == 1) eow = 0; + if (dow == eow && d < daysInMonth[mpl]) row++; } } - return view.render (); + return view.render(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdCalendar.h b/src/commands/CmdCalendar.h index d2645ef3b..afd1f9d27 100644 --- a/src/commands/CmdCalendar.h +++ b/src/commands/CmdCalendar.h @@ -27,20 +27,20 @@ #ifndef INCLUDED_CMDCALENDAR #define INCLUDED_CMDCALENDAR -#include -#include +#include #include #include -#include -class CmdCalendar : public Command -{ -public: - CmdCalendar (); - int execute (std::string&); +#include +#include -private: - std::string renderMonths (int, int, const Datetime&, std::vector &, int); +class CmdCalendar : public Command { + public: + CmdCalendar(); + int execute(std::string&); + + private: + std::string renderMonths(int, int, const Datetime&, std::vector&, int); }; #endif diff --git a/src/commands/CmdColor.cpp b/src/commands/CmdColor.cpp index 8ce3c576a..bb0d7f548 100644 --- a/src/commands/CmdColor.cpp +++ b/src/commands/CmdColor.cpp @@ -28,161 +28,137 @@ // cmake.h include header must come first #include -#include -#include -#include -#include #include +#include +#include #include +#include #include +#include + //////////////////////////////////////////////////////////////////////////////// -CmdColor::CmdColor () -{ - _keyword = "colors"; - _usage = "task colors [sample | legend]"; - _description = "All colors, a sample, or a legend"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdColor::CmdColor() { + _keyword = "colors"; + _usage = "task colors [sample | legend]"; + _description = "All colors, a sample, or a legend"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::misc; + _category = Command::Category::misc; } //////////////////////////////////////////////////////////////////////////////// -int CmdColor::execute (std::string& output) -{ +int CmdColor::execute(std::string& output) { int rc = 0; // Get the non-attribute, non-fancy command line arguments. auto legend = false; - auto words = Context::getContext ().cli2.getWords (); + auto words = Context::getContext().cli2.getWords(); for (auto& word : words) - if (closeEnough ("legend", word)) - legend = true; + if (closeEnough("legend", word)) legend = true; std::stringstream out; - if (Context::getContext ().color ()) - { + if (Context::getContext().color()) { // If the description contains 'legend', show all the colors currently in // use. - if (legend) - { + if (legend) { out << "\nHere are the colors currently in use:\n"; Table view; - view.width (Context::getContext ().getWidth ()); - if (Context::getContext ().config.getBoolean ("color")) - view.forceColor (); - view.add ("Color"); - view.add ("Definition"); + view.width(Context::getContext().getWidth()); + if (Context::getContext().config.getBoolean("color")) view.forceColor(); + view.add("Color"); + view.add("Definition"); - for (auto& item : Context::getContext ().config) - { + for (auto& item : Context::getContext().config) { // Skip items with 'color' in their name, that are not referring to // actual colors. - if (item.first != "_forcecolor" && - item.first != "color" && - item.first.find ("color") == 0) - { - Color color (Context::getContext ().config.get (item.first)); - int row = view.addRow (); - view.set (row, 0, item.first, color); - view.set (row, 1, item.second, color); + if (item.first != "_forcecolor" && item.first != "color" && item.first.find("color") == 0) { + Color color(Context::getContext().config.get(item.first)); + int row = view.addRow(); + view.set(row, 0, item.first, color); + view.set(row, 1, item.second, color); } } - out << view.render () - << '\n'; + out << view.render() << '\n'; } // If there is something in the description, then assume that is a color, // and display it as a sample. - else if (words.size ()) - { - Color one ("black on bright yellow"); - Color two ("underline cyan on bright blue"); - Color three ("color214 on color202"); - Color four ("rgb150 on rgb020"); - Color five ("underline grey10 on grey3"); - Color six ("red on color173"); + else if (words.size()) { + Color one("black on bright yellow"); + Color two("underline cyan on bright blue"); + Color three("color214 on color202"); + Color four("rgb150 on rgb020"); + Color five("underline grey10 on grey3"); + Color six("red on color173"); std::string swatch; - for (auto word = words.begin (); word != words.end (); ++word) - { - if (word != words.begin ()) - swatch += ' '; + for (auto word = words.begin(); word != words.end(); ++word) { + if (word != words.begin()) swatch += ' '; swatch += *word; } - Color sample (swatch); + Color sample(swatch); out << '\n' << "Use this command to see how colors are displayed by your terminal.\n" << "\n\n" << "16-color usage (supports underline, bold text, bright background):\n" - << " " << one.colorize ("task color black on bright yellow") << '\n' - << " " << two.colorize ("task color underline cyan on bright blue") << '\n' + << " " << one.colorize("task color black on bright yellow") << '\n' + << " " << two.colorize("task color underline cyan on bright blue") << '\n' << '\n' << "256-color usage (supports underline):\n" - << " " << three.colorize ("task color color214 on color202") << '\n' - << " " << four.colorize ("task color rgb150 on rgb020") << '\n' - << " " << five.colorize ("task color underline grey10 on grey3") << '\n' - << " " << six.colorize ("task color red on color173") << '\n' + << " " << three.colorize("task color color214 on color202") << '\n' + << " " << four.colorize("task color rgb150 on rgb020") << '\n' + << " " << five.colorize("task color underline grey10 on grey3") << '\n' + << " " << six.colorize("task color red on color173") << '\n' << '\n' << "Your sample:\n\n" - << " " << sample.colorize ("task color " + swatch) << "\n\n"; + << " " << sample.colorize("task color " + swatch) << "\n\n"; } // Show all supported colors. Possibly show some unsupported ones too. - else - { + else { out << '\n' << "Basic colors\n" - << ' ' << Color::colorize (" black ", "black") - << ' ' << Color::colorize (" red ", "red") - << ' ' << Color::colorize (" blue ", "blue") - << ' ' << Color::colorize (" green ", "green") - << ' ' << Color::colorize (" magenta ", "magenta") - << ' ' << Color::colorize (" cyan ", "cyan") - << ' ' << Color::colorize (" yellow ", "yellow") - << ' ' << Color::colorize (" white ", "white") - << '\n' - << ' ' << Color::colorize (" black ", "white on black") - << ' ' << Color::colorize (" red ", "white on red") - << ' ' << Color::colorize (" blue ", "white on blue") - << ' ' << Color::colorize (" green ", "black on green") - << ' ' << Color::colorize (" magenta ", "black on magenta") - << ' ' << Color::colorize (" cyan ", "black on cyan") - << ' ' << Color::colorize (" yellow ", "black on yellow") - << ' ' << Color::colorize (" white ", "black on white") - << "\n\n"; + << ' ' << Color::colorize(" black ", "black") << ' ' << Color::colorize(" red ", "red") + << ' ' << Color::colorize(" blue ", "blue") << ' ' << Color::colorize(" green ", "green") + << ' ' << Color::colorize(" magenta ", "magenta") << ' ' + << Color::colorize(" cyan ", "cyan") << ' ' << Color::colorize(" yellow ", "yellow") + << ' ' << Color::colorize(" white ", "white") << '\n' + << ' ' << Color::colorize(" black ", "white on black") << ' ' + << Color::colorize(" red ", "white on red") << ' ' + << Color::colorize(" blue ", "white on blue") << ' ' + << Color::colorize(" green ", "black on green") << ' ' + << Color::colorize(" magenta ", "black on magenta") << ' ' + << Color::colorize(" cyan ", "black on cyan") << ' ' + << Color::colorize(" yellow ", "black on yellow") << ' ' + << Color::colorize(" white ", "black on white") << "\n\n"; out << "Effects\n" - << ' ' << Color::colorize (" red ", "red") - << ' ' << Color::colorize (" bold red ", "bold red") - << ' ' << Color::colorize (" underline on blue ", "underline on blue") - << ' ' << Color::colorize (" on green ", "black on green") - << ' ' << Color::colorize (" on bright green ", "black on bright green") - << ' ' << Color::colorize (" inverse ", "inverse") - << "\n\n"; + << ' ' << Color::colorize(" red ", "red") << ' ' + << Color::colorize(" bold red ", "bold red") << ' ' + << Color::colorize(" underline on blue ", "underline on blue") << ' ' + << Color::colorize(" on green ", "black on green") << ' ' + << Color::colorize(" on bright green ", "black on bright green") << ' ' + << Color::colorize(" inverse ", "inverse") << "\n\n"; // 16 system colors. - out << "color0 - color15" - << '\n' - << " 0 1 2 . . .\n"; - for (int r = 0; r < 2; ++r) - { + out << "color0 - color15" << '\n' << " 0 1 2 . . .\n"; + for (int r = 0; r < 2; ++r) { out << " "; - for (int c = 0; c < 8; ++c) - { + for (int c = 0; c < 8; ++c) { std::stringstream s; - s << "on color" << (r*8 + c); - out << Color::colorize (" ", s.str ()); + s << "on color" << (r * 8 + c); + out << Color::colorize(" ", s.str()); } out << '\n'; @@ -191,43 +167,40 @@ int CmdColor::execute (std::string& output) out << " . . . 15\n\n"; // Color cube. - out << "Color cube rgb" - << Color::colorize ("0", "bold red") - << Color::colorize ("0", "bold green") - << Color::colorize ("0", "bold blue") - << " - rgb" - << Color::colorize ("5", "bold red") - << Color::colorize ("5", "bold green") - << Color::colorize ("5", "bold blue") - << " (also color16 - color231)" + out << "Color cube rgb" << Color::colorize("0", "bold red") + << Color::colorize("0", "bold green") << Color::colorize("0", "bold blue") << " - rgb" + << Color::colorize("5", "bold red") << Color::colorize("5", "bold green") + << Color::colorize("5", "bold blue") << " (also color16 - color231)" << '\n' + << " " + << Color::colorize( + "0 " + "1 " + "2 " + "3 " + "4 " + "5", + "bold red") << '\n' - << " " << Color::colorize ("0 " - "1 " - "2 " - "3 " - "4 " - "5", "bold red") - << '\n' - << " " << Color::colorize ("0 1 2 3 4 5 " - "0 1 2 3 4 5 " - "0 1 2 3 4 5 " - "0 1 2 3 4 5 " - "0 1 2 3 4 5 " - "0 1 2 3 4 5", "bold blue") + << " " + << Color::colorize( + "0 1 2 3 4 5 " + "0 1 2 3 4 5 " + "0 1 2 3 4 5 " + "0 1 2 3 4 5 " + "0 1 2 3 4 5 " + "0 1 2 3 4 5", + "bold blue") << '\n'; - char label [12]; - for (int g = 0; g < 6; ++g) - { - snprintf (label, 12, " %d", g); - out << Color::colorize (label, "bold green"); - for (int r = 0; r < 6; ++r) - { - for (int b = 0; b < 6; ++b) - { + char label[12]; + for (int g = 0; g < 6; ++g) { + snprintf(label, 12, " %d", g); + out << Color::colorize(label, "bold green"); + for (int r = 0; r < 6; ++r) { + for (int b = 0; b < 6; ++b) { std::stringstream s; s << "on rgb" << r << g << b; - out << Color::colorize (" ", s.str ()); + out << Color::colorize(" ", s.str()); } out << ' '; @@ -242,25 +215,23 @@ int CmdColor::execute (std::string& output) out << "Gray ramp gray0 - gray23 (also color232 - color255)\n" << " 0 1 2 . . . . . . 23\n" << " "; - for (int g = 0; g < 24; ++g) - { + for (int g = 0; g < 24; ++g) { std::stringstream s; s << "on gray" << g; - out << Color::colorize (" ", s.str ()); + out << Color::colorize(" ", s.str()); } out << "\n\n" << "Try running 'task color white on red'.\n" << '\n'; } - } - else - { - out << "Color is currently turned off in your .taskrc file. To enable color, remove the line 'color=off', or change the 'off' to 'on'.\n"; + } else { + out << "Color is currently turned off in your .taskrc file. To enable color, remove the line " + "'color=off', or change the 'off' to 'on'.\n"; rc = 1; } - output = out.str (); + output = out.str(); return rc; } diff --git a/src/commands/CmdColor.h b/src/commands/CmdColor.h index 34c6cd674..785de4a74 100644 --- a/src/commands/CmdColor.h +++ b/src/commands/CmdColor.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDCOLOR #define INCLUDED_CMDCOLOR -#include #include -class CmdColor : public Command -{ -public: - CmdColor (); - int execute (std::string&); +#include + +class CmdColor : public Command { + public: + CmdColor(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdColumns.cpp b/src/commands/CmdColumns.cpp index 6502882df..2b2e32bfb 100644 --- a/src/commands/CmdColumns.cpp +++ b/src/commands/CmdColumns.cpp @@ -28,123 +28,114 @@ // cmake.h include header must come first #include -#include +#include #include #include -#include +#include #include #include -#include + +#include //////////////////////////////////////////////////////////////////////////////// -CmdColumns::CmdColumns () -{ - _keyword = "columns"; - _usage = "task columns [substring]"; - _description = "All supported columns and formatting styles"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdColumns::CmdColumns() { + _keyword = "columns"; + _usage = "task columns [substring]"; + _description = "All supported columns and formatting styles"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::config; + _category = Command::Category::config; } //////////////////////////////////////////////////////////////////////////////// -int CmdColumns::execute (std::string& output) -{ +int CmdColumns::execute(std::string& output) { // Obtain the arguments from the description. That way, things like '--' // have already been handled. - auto words = Context::getContext ().cli2.getWords (); - if (words.size () > 1) - throw std::string ("You can only specify one search string."); + auto words = Context::getContext().cli2.getWords(); + if (words.size() > 1) throw std::string("You can only specify one search string."); // Include all columns in the table. - std::vector names; - for (const auto& col : Context::getContext ().columns) - names.push_back (col.first); + std::vector names; + for (const auto& col : Context::getContext().columns) names.push_back(col.first); - std::sort (names.begin (), names.end ()); + std::sort(names.begin(), names.end()); // Render a list of column names, formats and examples. Table formats; - formats.width (Context::getContext ().getWidth ()); - formats.add ("Columns"); - formats.add ("Type"); - formats.add ("Modifiable"); - formats.add ("Supported Formats"); - formats.add ("Example"); - setHeaderUnderline (formats); + formats.width(Context::getContext().getWidth()); + formats.add("Columns"); + formats.add("Type"); + formats.add("Modifiable"); + formats.add("Supported Formats"); + formats.add("Example"); + setHeaderUnderline(formats); - for (const auto& name : names) - { - if (words.size () == 0 || - find (name, words[0], false) != std::string::npos) - { - auto styles = Context::getContext ().columns[name]->styles (); - auto examples = Context::getContext ().columns[name]->examples (); + for (const auto& name : names) { + if (words.size() == 0 || find(name, words[0], false) != std::string::npos) { + auto styles = Context::getContext().columns[name]->styles(); + auto examples = Context::getContext().columns[name]->examples(); - for (unsigned int i = 0; i < styles.size (); ++i) - { - auto row = formats.addRow (); - formats.set (row, 0, i == 0 ? name : ""); - formats.set (row, 1, i == 0 ? Context::getContext ().columns[name]->type () : ""); - formats.set (row, 2, i == 0 ? (Context::getContext ().columns[name]->modifiable () ? "Modifiable" : "Read Only") : ""); - formats.set (row, 3, styles[i] + (i == 0 ? "*" : "")); - formats.set (row, 4, i < examples.size () ? examples[i] : ""); + for (unsigned int i = 0; i < styles.size(); ++i) { + auto row = formats.addRow(); + formats.set(row, 0, i == 0 ? name : ""); + formats.set(row, 1, i == 0 ? Context::getContext().columns[name]->type() : ""); + formats.set(row, 2, + i == 0 ? (Context::getContext().columns[name]->modifiable() ? "Modifiable" + : "Read Only") + : ""); + formats.set(row, 3, styles[i] + (i == 0 ? "*" : "")); + formats.set(row, 4, i < examples.size() ? examples[i] : ""); } } } - auto row = formats.addRow (); - formats.set (row, 0, ""); - formats.set (row, 1, ""); - formats.set (row, 2, "Modifiable"); - formats.set (row, 3, "default*"); + auto row = formats.addRow(); + formats.set(row, 0, ""); + formats.set(row, 1, ""); + formats.set(row, 2, "Modifiable"); + formats.set(row, 3, "default*"); - row = formats.addRow (); - formats.set (row, 0, ""); - formats.set (row, 3, "indicator"); + row = formats.addRow(); + formats.set(row, 0, ""); + formats.set(row, 3, "indicator"); - output = optionalBlankLine () - + formats.render () - + '\n' - + "* Means default format, and therefore optional. For example, 'due' and 'due.formatted' are equivalent.\n"; + output = optionalBlankLine() + formats.render() + '\n' + + "* Means default format, and therefore optional. For example, 'due' and " + "'due.formatted' are equivalent.\n"; return 0; } //////////////////////////////////////////////////////////////////////////////// -CmdCompletionColumns::CmdCompletionColumns () -{ - _keyword = "_columns"; - _usage = "task _columns"; - _description = "Displays only a list of supported columns"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdCompletionColumns::CmdCompletionColumns() { + _keyword = "_columns"; + _usage = "task _columns"; + _description = "Displays only a list of supported columns"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionColumns::execute (std::string& output) -{ +int CmdCompletionColumns::execute(std::string& output) { // Include all columns. - std::vector names; - for (const auto& col : Context::getContext ().columns) - names.push_back (col.first); + std::vector names; + for (const auto& col : Context::getContext().columns) names.push_back(col.first); - std::sort (names.begin (), names.end ()); + std::sort(names.begin(), names.end()); // Render only the column names. - for (const auto& name : names) - output += name + '\n'; + for (const auto& name : names) output += name + '\n'; return 0; } diff --git a/src/commands/CmdColumns.h b/src/commands/CmdColumns.h index 9d4d0cedc..7a12caf13 100644 --- a/src/commands/CmdColumns.h +++ b/src/commands/CmdColumns.h @@ -27,21 +27,20 @@ #ifndef INCLUDED_CMDCOLUMNS #define INCLUDED_CMDCOLUMNS -#include #include -class CmdColumns : public Command -{ -public: - CmdColumns (); - int execute (std::string&); +#include + +class CmdColumns : public Command { + public: + CmdColumns(); + int execute(std::string&); }; -class CmdCompletionColumns : public Command -{ -public: - CmdCompletionColumns (); - int execute (std::string&); +class CmdCompletionColumns : public Command { + public: + CmdCompletionColumns(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdCommands.cpp b/src/commands/CmdCommands.cpp index 6382d5d52..4676f57ae 100644 --- a/src/commands/CmdCommands.cpp +++ b/src/commands/CmdCommands.cpp @@ -28,145 +28,128 @@ // cmake.h include header must come first #include -#include -#include -#include +#include #include #include -#include +#include #include +#include +#include + //////////////////////////////////////////////////////////////////////////////// -CmdCommands::CmdCommands () -{ - _keyword = "commands"; - _usage = "task commands"; - _description = "Generates a list of all commands, with behavior details"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdCommands::CmdCommands() { + _keyword = "commands"; + _usage = "task commands"; + _description = "Generates a list of all commands, with behavior details"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::metadata; + _category = Command::Category::metadata; } //////////////////////////////////////////////////////////////////////////////// -int CmdCommands::execute (std::string& output) -{ +int CmdCommands::execute(std::string& output) { Table view; - view.width (Context::getContext ().getWidth ()); - view.add ("Command"); - view.add ("Category"); - view.add ("R/W", false); - view.add ("ID", false); - view.add ("GC", false); - view.add ("Context", false); - view.add ("Filter", false); - view.add ("Mods", false); - view.add ("Misc", false); - view.add ("Description"); - view.leftMargin (Context::getContext ().config.getInteger ("indent.report")); - view.extraPadding (Context::getContext ().config.getInteger ("row.padding")); - view.intraPadding (Context::getContext ().config.getInteger ("column.padding")); - setHeaderUnderline (view); + view.width(Context::getContext().getWidth()); + view.add("Command"); + view.add("Category"); + view.add("R/W", false); + view.add("ID", false); + view.add("GC", false); + view.add("Context", false); + view.add("Filter", false); + view.add("Mods", false); + view.add("Misc", false); + view.add("Description"); + view.leftMargin(Context::getContext().config.getInteger("indent.report")); + view.extraPadding(Context::getContext().config.getInteger("row.padding")); + view.intraPadding(Context::getContext().config.getInteger("column.padding")); + setHeaderUnderline(view); - for (auto& command : Context::getContext ().commands) - { - auto row = view.addRow (); - view.set (row, 0, command.first); - view.set (row, 1, Command::categoryNames.at (command.second->category ())); + for (auto& command : Context::getContext().commands) { + auto row = view.addRow(); + view.set(row, 0, command.first); + view.set(row, 1, Command::categoryNames.at(command.second->category())); - if (command.second->read_only ()) - view.set (row, 2, "RO"); + if (command.second->read_only()) + view.set(row, 2, "RO"); else - view.set (row, 2, "RW"); + view.set(row, 2, "RW"); - if (command.second->displays_id ()) - view.set (row, 3, "ID"); + if (command.second->displays_id()) view.set(row, 3, "ID"); - if (command.second->needs_gc ()) - view.set (row, 4, "GC"); + if (command.second->needs_gc()) view.set(row, 4, "GC"); - if (command.second->uses_context ()) - view.set (row, 5, "Ctxt"); + if (command.second->uses_context()) view.set(row, 5, "Ctxt"); - if (command.second->accepts_filter ()) - view.set (row, 6, "Filt"); + if (command.second->accepts_filter()) view.set(row, 6, "Filt"); - if (command.second->accepts_modifications ()) - view.set (row, 7, "Mods"); + if (command.second->accepts_modifications()) view.set(row, 7, "Mods"); - if (command.second->accepts_miscellaneous ()) - view.set (row, 8, "Misc"); + if (command.second->accepts_miscellaneous()) view.set(row, 8, "Misc"); - view.set (row, 9, command.second->description ()); + view.set(row, 9, command.second->description()); } - output = optionalBlankLine () - + view.render () - + optionalBlankLine () - + '\n'; + output = optionalBlankLine() + view.render() + optionalBlankLine() + '\n'; return 0; } //////////////////////////////////////////////////////////////////////////////// -CmdCompletionCommands::CmdCompletionCommands () -{ - _keyword = "_commands"; - _usage = "task _commands"; - _description = "Generates a list of all commands, for autocompletion purposes"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdCompletionCommands::CmdCompletionCommands() { + _keyword = "_commands"; + _usage = "task _commands"; + _description = "Generates a list of all commands, for autocompletion purposes"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionCommands::execute (std::string& output) -{ +int CmdCompletionCommands::execute(std::string& output) { // Get a list of all commands. - std::vector commands; - for (const auto& command : Context::getContext ().commands) - commands.push_back (command.first); + std::vector commands; + for (const auto& command : Context::getContext().commands) commands.push_back(command.first); // Sort alphabetically. - std::sort (commands.begin (), commands.end ()); + std::sort(commands.begin(), commands.end()); std::stringstream out; - for (const auto& c : commands) - out << c << '\n'; + for (const auto& c : commands) out << c << '\n'; - output = out.str (); + output = out.str(); return 0; } //////////////////////////////////////////////////////////////////////////////// -CmdZshCommands::CmdZshCommands () -{ - _keyword = "_zshcommands"; - _usage = "task _zshcommands"; - _description = "Generates a list of all commands, for zsh autocompletion purposes"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdZshCommands::CmdZshCommands() { + _keyword = "_zshcommands"; + _usage = "task _zshcommands"; + _description = "Generates a list of all commands, for zsh autocompletion purposes"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -struct ZshCommand -{ - bool operator< (const struct ZshCommand&) const; +struct ZshCommand { + bool operator<(const struct ZshCommand&) const; Command::Category _category; std::string _command; @@ -174,49 +157,40 @@ struct ZshCommand }; //////////////////////////////////////////////////////////////////////////////// -bool ZshCommand::operator< (const struct ZshCommand& other) const -{ +bool ZshCommand::operator<(const struct ZshCommand& other) const { // Lexicographical comparison. - if (_category != other._category) - return (_category < other._category); + if (_category != other._category) return (_category < other._category); - if (_command != other._command) - return (_command < other._command); + if (_command != other._command) return (_command < other._command); - if (_description != other._description) - return (_description < other._description); + if (_description != other._description) return (_description < other._description); return false; } //////////////////////////////////////////////////////////////////////////////// -int CmdZshCommands::execute (std::string& output) -{ +int CmdZshCommands::execute(std::string& output) { // Get a list of all command descriptions, sorted by category and then // alphabetically by command name. // Since not all supported compilers support tuples, we use a least common // denominator alternative: a custom struct type. - std::vector commands; - for (auto& command : Context::getContext ().commands) - { - ZshCommand zshCommand {command.second->category (), - command.first, - command.second->description ()}; - commands.push_back (zshCommand); + std::vector commands; + for (auto& command : Context::getContext().commands) { + ZshCommand zshCommand{command.second->category(), command.first, command.second->description()}; + commands.push_back(zshCommand); } - std::sort (commands.begin (), commands.end ()); + std::sort(commands.begin(), commands.end()); // Emit the commands in order. std::stringstream out; for (const auto& zc : commands) - out << zc._command << ':' - << Command::categoryNames.at (zc._category) << ':' - << zc._description << '\n'; + out << zc._command << ':' << Command::categoryNames.at(zc._category) << ':' << zc._description + << '\n'; - output = out.str (); + output = out.str(); return 0; } diff --git a/src/commands/CmdCommands.h b/src/commands/CmdCommands.h index eb6e45000..59c936717 100644 --- a/src/commands/CmdCommands.h +++ b/src/commands/CmdCommands.h @@ -27,28 +27,26 @@ #ifndef INCLUDED_CMDCOMMANDS #define INCLUDED_CMDCOMMANDS -#include #include -class CmdCommands : public Command -{ -public: - CmdCommands (); - int execute (std::string&); +#include + +class CmdCommands : public Command { + public: + CmdCommands(); + int execute(std::string&); }; -class CmdCompletionCommands : public Command -{ -public: - CmdCompletionCommands (); - int execute (std::string&); +class CmdCompletionCommands : public Command { + public: + CmdCompletionCommands(); + int execute(std::string&); }; -class CmdZshCommands : public Command -{ -public: - CmdZshCommands (); - int execute (std::string&); +class CmdZshCommands : public Command { + public: + CmdZshCommands(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdConfig.cpp b/src/commands/CmdConfig.cpp index cc845948e..fab9b7631 100644 --- a/src/commands/CmdConfig.cpp +++ b/src/commands/CmdConfig.cpp @@ -28,63 +28,57 @@ // cmake.h include header must come first #include -#include -#include #include #include -#include #include +#include + +#include +#include //////////////////////////////////////////////////////////////////////////////// -CmdConfig::CmdConfig () -{ - _keyword = "config"; - _usage = "task config [name [value | '']]"; - _description = "Change settings in the task configuration"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdConfig::CmdConfig() { + _keyword = "config"; + _usage = "task config [name [value | '']]"; + _description = "Change settings in the task configuration"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::config; + _category = Command::Category::config; } //////////////////////////////////////////////////////////////////////////////// -bool CmdConfig::setConfigVariable ( - const std::string& name, - const std::string& value, - bool confirmation /* = false */) -{ +bool CmdConfig::setConfigVariable(const std::string& name, const std::string& value, + bool confirmation /* = false */) { // Read .taskrc (or equivalent) - std::vector contents; - File::read (Context::getContext ().config.file (), contents); + std::vector contents; + File::read(Context::getContext().config.file(), contents); auto found = false; auto change = false; - for (auto& line : contents) - { + for (auto& line : contents) { // Get l-trimmed version of the line - auto trimmed_line = trim (line, " "); + auto trimmed_line = trim(line, " "); // If there is a comment on the line, it must follow the pattern. - auto comment = line.find ('#'); - auto pos = trimmed_line.find (name + '='); + auto comment = line.find('#'); + auto pos = trimmed_line.find(name + '='); // TODO: Use std::regex here - if (pos == 0) - { + if (pos == 0) { found = true; if (!confirmation || - confirm (format ("Are you sure you want to change the value of '{1}' from '{2}' to '{3}'?", name, Context::getContext ().config.get (name), value))) - { - auto new_line = line.substr (0, pos + name.length () + 1) + json::encode (value); + confirm(format("Are you sure you want to change the value of '{1}' from '{2}' to '{3}'?", + name, Context::getContext().config.get(name), value))) { + auto new_line = line.substr(0, pos + name.length() + 1) + json::encode(value); // Preserve the comment - if (comment != std::string::npos) - new_line += " " + line.substr (comment); + if (comment != std::string::npos) new_line += " " + line.substr(comment); // Rewrite the line line = new_line; @@ -94,61 +88,52 @@ bool CmdConfig::setConfigVariable ( } // Not found, so append instead. - if (! found && - (! confirmation || - confirm (format ("Are you sure you want to add '{1}' with a value of '{2}'?", name, value)))) - { - contents.push_back (name + '=' + json::encode (value)); + if (!found && + (!confirmation || + confirm(format("Are you sure you want to add '{1}' with a value of '{2}'?", name, value)))) { + contents.push_back(name + '=' + json::encode(value)); change = true; } - if (change) - File::write (Context::getContext ().config.file (), contents); + if (change) File::write(Context::getContext().config.file(), contents); return change; } //////////////////////////////////////////////////////////////////////////////// -int CmdConfig::unsetConfigVariable (const std::string& name, bool confirmation /* = false */) -{ +int CmdConfig::unsetConfigVariable(const std::string& name, bool confirmation /* = false */) { // Read .taskrc (or equivalent) - std::vector contents; - File::read (Context::getContext ().config.file (), contents); + std::vector contents; + File::read(Context::getContext().config.file(), contents); auto found = false; auto change = false; - for (auto line = contents.begin (); line != contents.end (); ) - { + for (auto line = contents.begin(); line != contents.end();) { auto lineDeleted = false; // Get l-trimmed version of the line // If there is a comment on the line, it must follow the pattern. - auto pos = trim (*line, " ").find (name + '='); + auto pos = trim(*line, " ").find(name + '='); // TODO: Use std::regex here - if (pos == 0) - { + if (pos == 0) { found = true; // Remove name - if (!confirmation || - confirm (format ("Are you sure you want to remove '{1}'?", name))) - { + if (!confirmation || confirm(format("Are you sure you want to remove '{1}'?", name))) { // vector::erase method returns a valid iterator to the next object - line = contents.erase (line); + line = contents.erase(line); lineDeleted = true; change = true; } } - if (! lineDeleted) - line++; + if (!lineDeleted) line++; } - if (change) - File::write (Context::getContext ().config.file (), contents); + if (change) File::write(Context::getContext().config.file(), contents); if (change && found) return 0; @@ -159,107 +144,88 @@ int CmdConfig::unsetConfigVariable (const std::string& name, bool confirmation / } //////////////////////////////////////////////////////////////////////////////// -int CmdConfig::execute (std::string& output) -{ +int CmdConfig::execute(std::string& output) { auto rc = 0; std::stringstream out; // Get the non-attribute, non-fancy command line arguments. - std::vector words = Context::getContext ().cli2.getWords (); + std::vector words = Context::getContext().cli2.getWords(); // Support: // task config name value # set name to value // task config name "" # set name to blank // task config name # remove name - if (words.size ()) - { - auto confirmation = Context::getContext ().config.getBoolean ("confirmation"); + if (words.size()) { + auto confirmation = Context::getContext().config.getBoolean("confirmation"); auto found = false; auto name = words[0]; std::string value = ""; // Join the remaining words into config variable's value - if (words.size () > 1) - { - for (unsigned int i = 1; i < words.size (); ++i) - { - if (i > 1) - value += ' '; + if (words.size() > 1) { + for (unsigned int i = 1; i < words.size(); ++i) { + if (i > 1) value += ' '; value += words[i]; } } - if (name != "") - { + if (name != "") { auto change = false; // task config name value // task config name "" - if (words.size () > 1) - change = setConfigVariable(name, value, confirmation); + if (words.size() > 1) change = setConfigVariable(name, value, confirmation); // task config name - else - { + else { rc = unsetConfigVariable(name, confirmation); - if (rc == 0) - { + if (rc == 0) { change = true; found = true; - } - else if (rc == 1) + } else if (rc == 1) found = true; - if (! found) - throw format ("No entry named '{1}' found.", name); + if (!found) throw format("No entry named '{1}' found.", name); } // Show feedback depending on whether .taskrc has been rewritten - if (change) - { - out << format ("Config file {1} modified.", Context::getContext ().config.file ()) - << '\n'; - } - else + if (change) { + out << format("Config file {1} modified.", Context::getContext().config.file()) << '\n'; + } else out << "No changes made.\n"; - } - else - throw std::string ("Specify the name of a config variable to modify."); + } else + throw std::string("Specify the name of a config variable to modify."); - output = out.str (); - } - else - throw std::string ("Specify the name of a config variable to modify."); + output = out.str(); + } else + throw std::string("Specify the name of a config variable to modify."); return rc; } //////////////////////////////////////////////////////////////////////////////// -CmdCompletionConfig::CmdCompletionConfig () -{ - _keyword = "_config"; - _usage = "task _config"; - _description = "Lists all supported configuration variables, for completion purposes"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdCompletionConfig::CmdCompletionConfig() { + _keyword = "_config"; + _usage = "task _config"; + _description = "Lists all supported configuration variables, for completion purposes"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionConfig::execute (std::string& output) -{ - auto configs = Context::getContext ().config.all (); - std::sort (configs.begin (), configs.end ()); +int CmdCompletionConfig::execute(std::string& output) { + auto configs = Context::getContext().config.all(); + std::sort(configs.begin(), configs.end()); - for (const auto& config : configs) - output += config + '\n'; + for (const auto& config : configs) output += config + '\n'; return 0; } diff --git a/src/commands/CmdConfig.h b/src/commands/CmdConfig.h index 4cf6c9583..866c83385 100644 --- a/src/commands/CmdConfig.h +++ b/src/commands/CmdConfig.h @@ -27,23 +27,22 @@ #ifndef INCLUDED_CMDCONFIG #define INCLUDED_CMDCONFIG -#include #include -class CmdConfig : public Command -{ -public: - CmdConfig (); - static bool setConfigVariable (const std::string&, const std::string&, bool confirmation = false); - static int unsetConfigVariable (const std::string&, bool confirmation = false); - int execute (std::string&); +#include + +class CmdConfig : public Command { + public: + CmdConfig(); + static bool setConfigVariable(const std::string&, const std::string&, bool confirmation = false); + static int unsetConfigVariable(const std::string&, bool confirmation = false); + int execute(std::string&); }; -class CmdCompletionConfig : public Command -{ -public: - CmdCompletionConfig (); - int execute (std::string&); +class CmdCompletionConfig : public Command { + public: + CmdCompletionConfig(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index b010b62f9..adcff94c7 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -27,60 +27,62 @@ #include // cmake.h include header must come first -#include #include -#include +#include #include #include -#include -#include -#include +#include #include #include #include #include +#include +#include +#include + //////////////////////////////////////////////////////////////////////////////// -CmdContext::CmdContext () -{ - _keyword = "context"; - _usage = "task context [ | ]"; - _description = "Set and define contexts (default filters / modifications)"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdContext::CmdContext() { + _keyword = "context"; + _usage = "task context [ | ]"; + _description = "Set and define contexts (default filters / modifications)"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::context; + _category = Command::Category::context; } //////////////////////////////////////////////////////////////////////////////// -int CmdContext::execute (std::string& output) -{ +int CmdContext::execute(std::string& output) { std::stringstream out; // Get the non-attribute, non-fancy command line arguments. - auto words = Context::getContext ().cli2.getWords (); - if (words.size () > 0) - { + auto words = Context::getContext().cli2.getWords(); + if (words.size() > 0) { auto subcommand = words[0]; - if (subcommand == "define") defineContext (words, out); - else if (subcommand == "delete") deleteContext (words, out); - else if (subcommand == "list") listContexts (out); - else if (subcommand == "none") unsetContext (out); - else if (subcommand == "show") showContext (out); - else if (words.size ()) setContext (words, out); - } - else - { - listContexts (out); + if (subcommand == "define") + defineContext(words, out); + else if (subcommand == "delete") + deleteContext(words, out); + else if (subcommand == "list") + listContexts(out); + else if (subcommand == "none") + unsetContext(out); + else if (subcommand == "show") + showContext(out); + else if (words.size()) + setContext(words, out); + } else { + listContexts(out); out << "Use 'task context none' to unset the current context.\n"; } - output = out.str (); + output = out.str(); return 0; } @@ -90,17 +92,14 @@ int CmdContext::execute (std::string& output) // // If to is specified as 0 (default value), all the remaining words will be joined. // -std::string CmdContext::joinWords (const std::vector & words, unsigned int from, unsigned int to /* = 0 */) -{ +std::string CmdContext::joinWords(const std::vector& words, unsigned int from, + unsigned int to /* = 0 */) { std::string value = ""; - if (to == 0) - to = words.size(); + if (to == 0) to = words.size(); - for (unsigned int i = from; i < to; ++i) - { - if (i > from) - value += ' '; + for (unsigned int i = from; i < to; ++i) { + if (i > from) value += ' '; value += words[i]; } @@ -119,33 +118,28 @@ std::string CmdContext::joinWords (const std::vector & words, unsig // invalid due to a wrong modifier use, the modifier string will contain the // first invalid modifier. // -bool CmdContext::validateWriteContext (const std::vector & lexedArgs, std::string& reason) -{ - for (auto &arg: lexedArgs) { +bool CmdContext::validateWriteContext(const std::vector& lexedArgs, std::string& reason) { + for (auto& arg : lexedArgs) { if (arg._lextype == Lexer::Type::op) - if (arg.attribute ("raw") == "or") - { + if (arg.attribute("raw") == "or") { reason = "contains the 'OR' operator"; return false; } if (arg._lextype == Lexer::Type::pair) { - auto modifier = arg.attribute ("modifier"); - if (modifier != "" && modifier != "is" && modifier != "equals") - { - reason = format ("contains an attribute modifier '{1}'", arg.attribute ("raw")); + auto modifier = arg.attribute("modifier"); + if (modifier != "" && modifier != "is" && modifier != "equals") { + reason = format("contains an attribute modifier '{1}'", arg.attribute("raw")); return false; } } if (arg._lextype == Lexer::Type::tag) { - if (arg.attribute ("sign") == "-") - { - reason = format ("contains tag exclusion '{1}'", arg.attribute ("raw")); + if (arg.attribute("sign") == "-") { + reason = format("contains tag exclusion '{1}'", arg.attribute("raw")); return false; } } - } return true; @@ -154,22 +148,20 @@ bool CmdContext::validateWriteContext (const std::vector & lexedArgs, std::s //////////////////////////////////////////////////////////////////////////////// // Returns all user defined contexts. // -std::vector CmdContext::getContexts () -{ - std::set contexts; +std::vector CmdContext::getContexts() { + std::set contexts; - for (auto& name : Context::getContext ().config) - if (name.first.substr (0, 8) == "context.") - { - std::string suffix = name.first.substr (8); + for (auto& name : Context::getContext().config) + if (name.first.substr(0, 8) == "context.") { + std::string suffix = name.first.substr(8); - if (suffix.find (".") != std::string::npos) - contexts.insert (suffix.substr (0, suffix.find ("."))); + if (suffix.find(".") != std::string::npos) + contexts.insert(suffix.substr(0, suffix.find("."))); else - contexts.insert (suffix); + contexts.insert(suffix); } - return std::vector (contexts.begin (), contexts.end ()); + return std::vector(contexts.begin(), contexts.end()); } //////////////////////////////////////////////////////////////////////////////// @@ -182,85 +174,85 @@ std::vector CmdContext::getContexts () // Invoked with: task context define // Example: task context define home project:Home // -void CmdContext::defineContext (const std::vector & words, std::stringstream& out) -{ - auto config = Context::getContext ().config; - bool confirmation = config.getBoolean ("confirmation"); +void CmdContext::defineContext(const std::vector& words, std::stringstream& out) { + auto config = Context::getContext().config; + bool confirmation = config.getBoolean("confirmation"); - if (words.size () > 2) - { + if (words.size() > 2) { auto name = "context." + words[1]; - auto value = joinWords (words, 2); + auto value = joinWords(words, 2); // Make sure nobody creates a context with name 'list', 'none' or 'show' - if (words[1] == "none" or words[1] == "list" or words[1] == "show") - { - throw format ("The name '{1}' is reserved and not allowed to use as a context name.", words[1]); + if (words[1] == "none" or words[1] == "list" or words[1] == "show") { + throw format("The name '{1}' is reserved and not allowed to use as a context name.", + words[1]); } // Extract MISCELLANEOUS arguments (containing the filter definition) for later analysis - std::vector lexedArgs = Context::getContext ().cli2.getMiscellaneous(); + std::vector lexedArgs = Context::getContext().cli2.getMiscellaneous(); // Check if the value is a proper filter by filtering current pending.data Filter filter; - std::vector filtered; - auto pending = Context::getContext ().tdb2.pending_tasks (); + std::vector filtered; + auto pending = Context::getContext().tdb2.pending_tasks(); - try - { + try { // This result is not used, and is just to check validity. - Context::getContext ().cli2.addFilter (value); - filter.subset (pending, filtered); - } - catch (std::string exception) - { - throw format ("Filter validation failed: {1}", exception); + Context::getContext().cli2.addFilter(value); + filter.subset(pending, filtered); + } catch (std::string exception) { + throw format("Filter validation failed: {1}", exception); } // Make user explicitly confirm filters that are matching no pending tasks - if (filtered.size () == 0) + if (filtered.size() == 0) if (confirmation && - ! confirm (format ("The filter '{1}' matches 0 pending tasks. Do you wish to continue?", value))) - throw std::string ("Context definition aborted."); + !confirm( + format("The filter '{1}' matches 0 pending tasks. Do you wish to continue?", value))) + throw std::string("Context definition aborted."); std::string reason = ""; - bool valid_write_context = CmdContext::validateWriteContext (lexedArgs, reason); + bool valid_write_context = CmdContext::validateWriteContext(lexedArgs, reason); - if (! valid_write_context) - { + if (!valid_write_context) { std::stringstream warning; - warning << format ("The filter '{1}' is not a valid modification string, because it contains {2}.", value, reason) - << "\nAs such, value for the write context cannot be set (context will not apply on task add / task log).\n\n" - << format ("Please use 'task config context.{1}.write ' to set default attribute values for new tasks in this context manually.\n\n", words[1]); - out << colorizeFootnote (warning.str ()); + warning + << format("The filter '{1}' is not a valid modification string, because it contains {2}.", + value, reason) + << "\nAs such, value for the write context cannot be set (context will not apply on task " + "add / task log).\n\n" + << format( + "Please use 'task config context.{1}.write ' to set default " + "attribute values for new tasks in this context manually.\n\n", + words[1]); + out << colorizeFootnote(warning.str()); } // Set context definition config variable - bool read_success = CmdConfig::setConfigVariable (name + ".read", value, confirmation); + bool read_success = CmdConfig::setConfigVariable(name + ".read", value, confirmation); bool write_success = false; if (valid_write_context) - write_success = CmdConfig::setConfigVariable (name + ".write", value, confirmation); + write_success = CmdConfig::setConfigVariable(name + ".write", value, confirmation); // Remove old-school context name, if it exists, assuming the read context was defined if (read_success) - if (config.has (name)) { - CmdConfig::unsetConfigVariable (name, false); + if (config.has(name)) { + CmdConfig::unsetConfigVariable(name, false); } if (!read_success and !write_success) - throw format ("Context '{1}' not defined.", words[1]); + throw format("Context '{1}' not defined.", words[1]); else if (!read_success) - out << format ("Context '{1}' defined (write only).", words[1]); + out << format("Context '{1}' defined (write only).", words[1]); else if (!write_success) - out << format ("Context '{1}' defined (read only).", words[1]); + out << format("Context '{1}' defined (read only).", words[1]); else - out << format ("Context '{1}' defined (read, write).", words[1]); + out << format("Context '{1}' defined (read, write).", words[1]); - out << format (" Use 'task context {1}' to activate.\n", words[1]); - } - else - throw std::string ("Both context name and its definition must be provided."); + out << format(" Use 'task context {1}' to activate.\n", words[1]); + } else + throw std::string("Both context name and its definition must be provided."); } //////////////////////////////////////////////////////////////////////////////// @@ -273,36 +265,33 @@ void CmdContext::defineContext (const std::vector & words, std::str // Invoked with: task context delete // Example: task context delete home // -void CmdContext::deleteContext (const std::vector & words, std::stringstream& out) -{ - if (words.size () > 1) - { +void CmdContext::deleteContext(const std::vector& words, std::stringstream& out) { + if (words.size() > 1) { // Delete the specified context auto name = "context." + words[1]; - auto confirmation = Context::getContext ().config.getBoolean ("confirmation"); - if (confirmation && ! confirm (format ("Do you want to delete context '{1}'?", words[1]))) - throw format ("Context '{1}' not deleted.", words[1]); + auto confirmation = Context::getContext().config.getBoolean("confirmation"); + if (confirmation && !confirm(format("Do you want to delete context '{1}'?", words[1]))) + throw format("Context '{1}' not deleted.", words[1]); // Delete legacy format and .read / .write flavours auto rc = CmdConfig::unsetConfigVariable(name, false); - rc += CmdConfig::unsetConfigVariable(name + ".read", false); - rc += CmdConfig::unsetConfigVariable(name + ".write", false); + rc += CmdConfig::unsetConfigVariable(name + ".read", false); + rc += CmdConfig::unsetConfigVariable(name + ".write", false); // If the currently set context was deleted, unset it - if (Context::getContext ().config.get ("context") == words[1]) + if (Context::getContext().config.get("context") == words[1]) CmdConfig::unsetConfigVariable("context", false); // Output feedback, rc should be even because only 0 (found and removed) // and 2 (not found) are aceptable return values from unsetConfigVariable if (rc % 2 != 0) - throw format ("Context '{1}' not deleted.", words[1]); + throw format("Context '{1}' not deleted.", words[1]); else if (rc == 6) - throw format ("Context '{1}' not found.", words[1]); + throw format("Context '{1}' not found.", words[1]); - out << format ("Context '{1}' deleted.\n", words[1]); - } - else + out << format("Context '{1}' deleted.\n", words[1]); + } else throw std::string("Context name needs to be specified."); } @@ -314,48 +303,41 @@ void CmdContext::deleteContext (const std::vector & words, std::str // Invoked with: task context list // Example: task context list // -void CmdContext::listContexts (std::stringstream& out) -{ +void CmdContext::listContexts(std::stringstream& out) { auto contexts = getContexts(); - if (contexts.size ()) - { - std::sort (contexts.begin (), contexts.end ()); + if (contexts.size()) { + std::sort(contexts.begin(), contexts.end()); Table table; - table.width (Context::getContext ().getWidth ()); - table.add ("Name"); - table.add ("Type"); - table.add ("Definition"); - table.add ("Active"); - setHeaderUnderline (table); + table.width(Context::getContext().getWidth()); + table.add("Name"); + table.add("Type"); + table.add("Definition"); + table.add("Active"); + setHeaderUnderline(table); - std::string activeContext = Context::getContext ().config.get ("context"); + std::string activeContext = Context::getContext().config.get("context"); - for (auto& userContext : contexts) - { + for (auto& userContext : contexts) { std::string active = "no"; - if (userContext == activeContext) - active = "yes"; + if (userContext == activeContext) active = "yes"; - int row = table.addRow (); - table.set (row, 0, userContext); - table.set (row, 1, "read"); - table.set (row, 2, Context::getContext ().getTaskContext("read", userContext)); - table.set (row, 3, active); + int row = table.addRow(); + table.set(row, 0, userContext); + table.set(row, 1, "read"); + table.set(row, 2, Context::getContext().getTaskContext("read", userContext)); + table.set(row, 3, active); - row = table.addRow (); - table.set (row, 0, ""); - table.set (row, 1, "write"); - table.set (row, 2, Context::getContext ().getTaskContext("write", userContext)); - table.set (row, 3, active); + row = table.addRow(); + table.set(row, 0, ""); + table.set(row, 1, "write"); + table.set(row, 2, Context::getContext().getTaskContext("write", userContext)); + table.set(row, 3, active); } - out << optionalBlankLine () - << table.render () - << optionalBlankLine (); - } - else - throw std::string ("No contexts defined."); + out << optionalBlankLine() << table.render() << optionalBlankLine(); + } else + throw std::string("No contexts defined."); } //////////////////////////////////////////////////////////////////////////////// @@ -369,23 +351,21 @@ void CmdContext::listContexts (std::stringstream& out) // Invoked with: task context // Example: task context home // -void CmdContext::setContext (const std::vector & words, std::stringstream& out) -{ +void CmdContext::setContext(const std::vector& words, std::stringstream& out) { auto value = words[0]; - auto contexts = getContexts (); + auto contexts = getContexts(); // Check that the specified context is defined - if (std::find (contexts.begin (), contexts.end (), value) == contexts.end ()) - throw format ("Context '{1}' not found.", value); + if (std::find(contexts.begin(), contexts.end(), value) == contexts.end()) + throw format("Context '{1}' not found.", value); // Set the active context. // Should always succeed, as we do not require confirmation. - bool success = CmdConfig::setConfigVariable ("context", value, false); + bool success = CmdConfig::setConfigVariable("context", value, false); - if (! success) - throw format ("Context '{1}' not applied.", value); + if (!success) throw format("Context '{1}' not applied.", value); - out << format ("Context '{1}' set. Use 'task context none' to remove.\n", value); + out << format("Context '{1}' set. Use 'task context none' to remove.\n", value); } //////////////////////////////////////////////////////////////////////////////// @@ -396,20 +376,17 @@ void CmdContext::setContext (const std::vector & words, std::string // Invoked with: task context show // Example: task context show // -void CmdContext::showContext (std::stringstream& out) -{ - auto currentContext = Context::getContext ().config.get ("context"); +void CmdContext::showContext(std::stringstream& out) { + auto currentContext = Context::getContext().config.get("context"); if (currentContext == "") out << "No context is currently applied.\n"; - else - { - out << format ( - "Context '{1}' with \n\n* read filter: '{2}'\n* write filter: '{3}'\n\nis currently applied.\n", - currentContext, - Context::getContext ().getTaskContext("read", ""), - Context::getContext ().getTaskContext("write", "") - ); + else { + out << format( + "Context '{1}' with \n\n* read filter: '{2}'\n* write filter: '{3}'\n\nis currently " + "applied.\n", + currentContext, Context::getContext().getTaskContext("read", ""), + Context::getContext().getTaskContext("write", "")); } } @@ -423,35 +400,30 @@ void CmdContext::showContext (std::stringstream& out) // Invoked with: task context none // Example: task context none // -void CmdContext::unsetContext (std::stringstream& out) -{ - if (CmdConfig::unsetConfigVariable ("context", false)) - throw std::string ("Context not unset."); +void CmdContext::unsetContext(std::stringstream& out) { + if (CmdConfig::unsetConfigVariable("context", false)) throw std::string("Context not unset."); out << "Context unset.\n"; } //////////////////////////////////////////////////////////////////////////////// -CmdCompletionContext::CmdCompletionContext () -{ - _keyword = "_context"; - _usage = "task _context"; - _description = "Lists all supported contexts, for completion purposes"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdCompletionContext::CmdCompletionContext() { + _keyword = "_context"; + _usage = "task _context"; + _description = "Lists all supported contexts, for completion purposes"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionContext::execute (std::string& output) -{ - for (auto& context : CmdContext::getContexts ()) - output += context + '\n'; +int CmdCompletionContext::execute(std::string& output) { + for (auto& context : CmdContext::getContexts()) output += context + '\n'; return 0; } diff --git a/src/commands/CmdContext.h b/src/commands/CmdContext.h index c21e8a0b3..a14197ccb 100644 --- a/src/commands/CmdContext.h +++ b/src/commands/CmdContext.h @@ -27,31 +27,30 @@ #ifndef INCLUDED_CMDCONTEXT #define INCLUDED_CMDCONTEXT -#include -#include #include +#include -class CmdContext : public Command -{ -public: - CmdContext (); - int execute (std::string&); - std::string joinWords (const std::vector &, unsigned int, unsigned int = 0); - bool validateWriteContext (const std::vector &, std::string&); - static std::vector getContexts (); - void defineContext (const std::vector &, std::stringstream&); - void deleteContext (const std::vector &, std::stringstream&); - void listContexts (std::stringstream&); - void setContext (const std::vector &, std::stringstream&); - void showContext (std::stringstream&); - void unsetContext (std::stringstream&); +#include + +class CmdContext : public Command { + public: + CmdContext(); + int execute(std::string&); + std::string joinWords(const std::vector&, unsigned int, unsigned int = 0); + bool validateWriteContext(const std::vector&, std::string&); + static std::vector getContexts(); + void defineContext(const std::vector&, std::stringstream&); + void deleteContext(const std::vector&, std::stringstream&); + void listContexts(std::stringstream&); + void setContext(const std::vector&, std::stringstream&); + void showContext(std::stringstream&); + void unsetContext(std::stringstream&); }; -class CmdCompletionContext : public Command -{ -public: - CmdCompletionContext (); - int execute (std::string&); +class CmdCompletionContext : public Command { + public: + CmdCompletionContext(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdCount.cpp b/src/commands/CmdCount.cpp index f577136ba..0da28bc1b 100644 --- a/src/commands/CmdCount.cpp +++ b/src/commands/CmdCount.cpp @@ -29,38 +29,37 @@ #include #include -#include #include +#include //////////////////////////////////////////////////////////////////////////////// -CmdCount::CmdCount () -{ - _keyword = "count"; - _usage = "task count"; - _description = "Counts matching tasks"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = true; - _accepts_filter = true; +CmdCount::CmdCount() { + _keyword = "count"; + _usage = "task count"; + _description = "Counts matching tasks"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::metadata; + _category = Command::Category::metadata; } //////////////////////////////////////////////////////////////////////////////// -int CmdCount::execute (std::string& output) -{ +int CmdCount::execute(std::string& output) { // Apply filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); // Find number of matching tasks. Skip recurring parent tasks. - int count = std::count_if(filtered.begin(), filtered.end(), [](const auto& task){ return task.getStatus () != Task::recurring; }); - output = format (count) + '\n'; + int count = std::count_if(filtered.begin(), filtered.end(), + [](const auto& task) { return task.getStatus() != Task::recurring; }); + output = format(count) + '\n'; return 0; } diff --git a/src/commands/CmdCount.h b/src/commands/CmdCount.h index bea9f6851..a7d49cfb8 100644 --- a/src/commands/CmdCount.h +++ b/src/commands/CmdCount.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDCOUNT #define INCLUDED_CMDCOUNT -#include #include -class CmdCount : public Command -{ -public: - CmdCount (); - int execute (std::string&); +#include + +class CmdCount : public Command { + public: + CmdCount(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index 7266b2187..0537f265d 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -28,178 +28,157 @@ // cmake.h include header must come first #include -#include -#include -#include -#include -#include -#include #include #include #include #include #include #include -#include -#include #include +#include +#include +#include + +#include +#include +#include +#include +#include //////////////////////////////////////////////////////////////////////////////// -CmdCustom::CmdCustom ( - const std::string& keyword, - const std::string& usage, - const std::string& description) -{ - _keyword = keyword; - _usage = usage; - _description = description; - _read_only = true; - _displays_id = true; - _needs_gc = true; - _uses_context = true; - _accepts_filter = true; +CmdCustom::CmdCustom(const std::string& keyword, const std::string& usage, + const std::string& description) { + _keyword = keyword; + _usage = usage; + _description = description; + _read_only = true; + _displays_id = true; + _needs_gc = true; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Category::report; + _category = Category::report; } //////////////////////////////////////////////////////////////////////////////// // Whether a report uses context is defined by the report..context // configuration variable. // -bool CmdCustom::uses_context () const -{ - auto config = Context::getContext ().config; +bool CmdCustom::uses_context() const { + auto config = Context::getContext().config; auto key = "report." + _keyword + ".context"; - if (config.has (key)) - return config.getBoolean (key); + if (config.has(key)) + return config.getBoolean(key); else return _uses_context; } //////////////////////////////////////////////////////////////////////////////// -int CmdCustom::execute (std::string& output) -{ +int CmdCustom::execute(std::string& output) { auto rc = 0; // Load report configuration. - auto reportColumns = Context::getContext ().config.get ("report." + _keyword + ".columns"); - auto reportLabels = Context::getContext ().config.get ("report." + _keyword + ".labels"); - auto reportSort = Context::getContext ().config.get ("report." + _keyword + ".sort"); - auto reportFilter = Context::getContext ().config.get ("report." + _keyword + ".filter"); + auto reportColumns = Context::getContext().config.get("report." + _keyword + ".columns"); + auto reportLabels = Context::getContext().config.get("report." + _keyword + ".labels"); + auto reportSort = Context::getContext().config.get("report." + _keyword + ".sort"); + auto reportFilter = Context::getContext().config.get("report." + _keyword + ".filter"); - auto columns = split (reportColumns, ','); - validateReportColumns (columns); + auto columns = split(reportColumns, ','); + validateReportColumns(columns); - auto labels = split (reportLabels, ','); + auto labels = split(reportLabels, ','); - if (columns.size () != labels.size () && labels.size () != 0) - throw format ("There are different numbers of columns and labels for report '{1}'.", _keyword); + if (columns.size() != labels.size() && labels.size() != 0) + throw format("There are different numbers of columns and labels for report '{1}'.", _keyword); - auto sortOrder = split (reportSort, ','); - if (sortOrder.size () != 0 && - sortOrder[0] != "none") - validateSortColumns (sortOrder); + auto sortOrder = split(reportSort, ','); + if (sortOrder.size() != 0 && sortOrder[0] != "none") validateSortColumns(sortOrder); // Add the report filter to any existing filter. - if (reportFilter != "") - Context::getContext ().cli2.addFilter (reportFilter); + if (reportFilter != "") Context::getContext().cli2.addFilter(reportFilter); // Make sure reccurent tasks are generated. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); - std::vector sequence; - if (sortOrder.size () && - sortOrder[0] == "none") - { + std::vector sequence; + if (sortOrder.size() && sortOrder[0] == "none") { // Assemble a sequence vector that represents the tasks listed in // Context::getContext ().cli2._uuid_ranges, in the order in which they appear. This // equates to no sorting, just a specified order. - sortOrder.clear (); - for (auto& i : Context::getContext ().cli2._uuid_list) - for (unsigned int t = 0; t < filtered.size (); ++t) - if (filtered[t].get ("uuid") == i) - sequence.push_back (t); - } - else - { + sortOrder.clear(); + for (auto& i : Context::getContext().cli2._uuid_list) + for (unsigned int t = 0; t < filtered.size(); ++t) + if (filtered[t].get("uuid") == i) sequence.push_back(t); + } else { // There is a sortOrder, so sorting will take place, which means the initial // order of sequence is ascending. - for (unsigned int i = 0; i < filtered.size (); ++i) - sequence.push_back (i); + for (unsigned int i = 0; i < filtered.size(); ++i) sequence.push_back(i); // Sort the tasks. - if (sortOrder.size ()) - sort_tasks (filtered, sequence, reportSort); + if (sortOrder.size()) sort_tasks(filtered, sequence, reportSort); } // Configure the view. ViewTask view; - view.width (Context::getContext ().getWidth ()); - view.leftMargin (Context::getContext ().config.getInteger ("indent.report")); - view.extraPadding (Context::getContext ().config.getInteger ("row.padding")); - view.intraPadding (Context::getContext ().config.getInteger ("column.padding")); + view.width(Context::getContext().getWidth()); + view.leftMargin(Context::getContext().config.getInteger("indent.report")); + view.extraPadding(Context::getContext().config.getInteger("row.padding")); + view.intraPadding(Context::getContext().config.getInteger("column.padding")); - if (Context::getContext ().color ()) - { - Color label (Context::getContext ().config.get ("color.label")); - view.colorHeader (label); + if (Context::getContext().color()) { + Color label(Context::getContext().config.get("color.label")); + view.colorHeader(label); - Color label_sort (Context::getContext ().config.get ("color.label.sort")); - view.colorSortHeader (label_sort); + Color label_sort(Context::getContext().config.get("color.label.sort")); + view.colorSortHeader(label_sort); // If an alternating row color is specified, notify the table. - Color alternate (Context::getContext ().config.get ("color.alternate")); - if (alternate.nontrivial ()) - { - view.colorOdd (alternate); - view.intraColorOdd (alternate); + Color alternate(Context::getContext().config.get("color.alternate")); + if (alternate.nontrivial()) { + view.colorOdd(alternate); + view.intraColorOdd(alternate); } } // Capture columns that are sorted. - std::vector sortColumns; + std::vector sortColumns; // Add the break columns, if any. - for (const auto& so : sortOrder) - { + for (const auto& so : sortOrder) { std::string name; bool ascending; bool breakIndicator; - Context::getContext ().decomposeSortField (so, name, ascending, breakIndicator); + Context::getContext().decomposeSortField(so, name, ascending, breakIndicator); - if (breakIndicator) - view.addBreak (name); + if (breakIndicator) view.addBreak(name); - sortColumns.push_back (name); + sortColumns.push_back(name); } // Add the columns and labels. - for (unsigned int i = 0; i < columns.size (); ++i) - { - Column* c = Column::factory (columns[i], _keyword); - if (i < labels.size ()) - c->setLabel (labels[i]); + for (unsigned int i = 0; i < columns.size(); ++i) { + Column* c = Column::factory(columns[i], _keyword); + if (i < labels.size()) c->setLabel(labels[i]); - bool sort = std::find (sortColumns.begin (), sortColumns.end (), c->name ()) != sortColumns.end () - ? true - : false; + bool sort = std::find(sortColumns.begin(), sortColumns.end(), c->name()) != sortColumns.end() + ? true + : false; - view.add (c, sort); + view.add(c, sort); } // How many lines taken up by table header? int table_header = 0; - if (Context::getContext ().verbose ("label")) - { - if (Context::getContext ().color () && Context::getContext ().config.getBoolean ("fontunderline")) + if (Context::getContext().verbose("label")) { + if (Context::getContext().color() && Context::getContext().config.getBoolean("fontunderline")) table_header = 1; // Underlining doesn't use extra line. else table_header = 2; // Dashes use an extra line. @@ -208,92 +187,77 @@ int CmdCustom::execute (std::string& output) // Report output can be limited by rows or lines. auto maxrows = 0; auto maxlines = 0; - Context::getContext ().getLimits (maxrows, maxlines); + Context::getContext().getLimits(maxrows, maxlines); // Adjust for fluff in the output. if (maxlines) - maxlines -= table_header - + (Context::getContext ().verbose ("blank") ? 1 : 0) - + (Context::getContext ().verbose ("footnote") ? Context::getContext ().footnotes.size () : 0) - + (Context::getContext ().verbose ("affected") ? 1 : 0) - + Context::getContext ().config.getInteger ("reserved.lines"); // For prompt, etc. + maxlines -= + table_header + (Context::getContext().verbose("blank") ? 1 : 0) + + (Context::getContext().verbose("footnote") ? Context::getContext().footnotes.size() : 0) + + (Context::getContext().verbose("affected") ? 1 : 0) + + Context::getContext().config.getInteger("reserved.lines"); // For prompt, etc. // Render. std::stringstream out; - if (filtered.size ()) - { - view.truncateRows (maxrows); - view.truncateLines (maxlines); + if (filtered.size()) { + view.truncateRows(maxrows); + view.truncateLines(maxlines); - out << optionalBlankLine () - << view.render (filtered, sequence) - << optionalBlankLine (); + out << optionalBlankLine() << view.render(filtered, sequence) << optionalBlankLine(); // Print the number of rendered tasks - if (Context::getContext ().verbose ("affected")) - { - out << (filtered.size () == 1 - ? "1 task" - : format ("{1} tasks", filtered.size ())); + if (Context::getContext().verbose("affected")) { + out << (filtered.size() == 1 ? "1 task" : format("{1} tasks", filtered.size())); - if (maxrows && maxrows < (int)filtered.size ()) - out << ", " << format ("{1} shown", maxrows); + if (maxrows && maxrows < (int)filtered.size()) out << ", " << format("{1} shown", maxrows); - if (maxlines && maxlines < (int)filtered.size ()) - out << ", " - << format ("truncated to {1} lines", maxlines - table_header); + if (maxlines && maxlines < (int)filtered.size()) + out << ", " << format("truncated to {1} lines", maxlines - table_header); out << '\n'; } - } - else - { - Context::getContext ().footnote ("No matches."); + } else { + Context::getContext().footnote("No matches."); rc = 1; } // Inform user about the new release highlights if not presented yet - Version news_version(Context::getContext ().config.get ("news.version")); + Version news_version(Context::getContext().config.get("news.version")); Version current_version = Version::Current(); - auto should_nag = news_version != current_version && Context::getContext ().verbose ("news"); - if (should_nag) - { + auto should_nag = news_version != current_version && Context::getContext().verbose("news"); + if (should_nag) { std::ostringstream notice; - notice << "Recently upgraded to " << current_version << ". " - "Please run 'task news' to read highlights about the new release."; - if (Context::getContext ().verbose ("footnote")) - Context::getContext ().footnote (notice.str()); - else if (Context::getContext ().verbose ("header")) - Context::getContext ().header (notice.str()); + notice << "Recently upgraded to " << current_version + << ". " + "Please run 'task news' to read highlights about the new release."; + if (Context::getContext().verbose("footnote")) + Context::getContext().footnote(notice.str()); + else if (Context::getContext().verbose("header")) + Context::getContext().header(notice.str()); } - std::string location = (Context::getContext ().data_dir); - File pending_data = File (location + "/pending.data"); + std::string location = (Context::getContext().data_dir); + File pending_data = File(location + "/pending.data"); if (pending_data.exists()) { - Color warning = Color (Context::getContext ().config.get ("color.warning")); - std::cerr << warning.colorize ( - format ("Found existing '*.data' files in {1}", location)) << "\n"; + Color warning = Color(Context::getContext().config.get("color.warning")); + std::cerr << warning.colorize(format("Found existing '*.data' files in {1}", location)) << "\n"; std::cerr << " Taskwarrior's storage format changed in 3.0, requiring a manual migration.\n"; std::cerr << " See https://github.com/GothenburgBitFactory/taskwarrior/releases.\n"; } - feedback_backlog (); - output = out.str (); + feedback_backlog(); + output = out.str(); return rc; } //////////////////////////////////////////////////////////////////////////////// -void CmdCustom::validateReportColumns (std::vector & columns) -{ - for (auto& col : columns) - legacyColumnMap (col); +void CmdCustom::validateReportColumns(std::vector& columns) { + for (auto& col : columns) legacyColumnMap(col); } //////////////////////////////////////////////////////////////////////////////// -void CmdCustom::validateSortColumns (std::vector & columns) -{ - for (auto& col : columns) - legacySortColumnMap (col); +void CmdCustom::validateSortColumns(std::vector& columns) { + for (auto& col : columns) legacySortColumnMap(col); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdCustom.h b/src/commands/CmdCustom.h index 521876d65..eeefe6302 100644 --- a/src/commands/CmdCustom.h +++ b/src/commands/CmdCustom.h @@ -27,19 +27,19 @@ #ifndef INCLUDED_CMDCUSTOM #define INCLUDED_CMDCUSTOM -#include #include -class CmdCustom : public Command -{ -public: - CmdCustom (const std::string&, const std::string&, const std::string&); - bool uses_context () const override; - int execute (std::string&) override; +#include -private: - void validateReportColumns (std::vector &); - void validateSortColumns (std::vector &); +class CmdCustom : public Command { + public: + CmdCustom(const std::string&, const std::string&, const std::string&); + bool uses_context() const override; + int execute(std::string&) override; + + private: + void validateReportColumns(std::vector&); + void validateSortColumns(std::vector&); }; #endif diff --git a/src/commands/CmdDelete.cpp b/src/commands/CmdDelete.cpp index b27690c81..3026bd05d 100644 --- a/src/commands/CmdDelete.cpp +++ b/src/commands/CmdDelete.cpp @@ -28,165 +28,142 @@ // cmake.h include header must come first #include -#include #include #include -#include #include #include +#include -#define STRING_CMD_DELETE_TASK_R "Deleting recurring task {1} '{2}'." -#define STRING_CMD_DELETE_CONFIRM_R "This is a recurring task. Do you want to delete all pending recurrences of this same task?" +#include + +#define STRING_CMD_DELETE_TASK_R "Deleting recurring task {1} '{2}'." +#define STRING_CMD_DELETE_CONFIRM_R \ + "This is a recurring task. Do you want to delete all pending recurrences of this same task?" //////////////////////////////////////////////////////////////////////////////// -CmdDelete::CmdDelete () -{ - _keyword = "delete"; - _usage = "task delete "; - _description = "Deletes the specified task"; - _read_only = false; - _displays_id = false; - _needs_confirm = true; - _needs_gc = false; - _uses_context = true; - _accepts_filter = true; +CmdDelete::CmdDelete() { + _keyword = "delete"; + _usage = "task delete "; + _description = "Deletes the specified task"; + _read_only = false; + _displays_id = false; + _needs_confirm = true; + _needs_gc = false; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdDelete::execute (std::string&) -{ +int CmdDelete::execute(std::string&) { auto rc = 0; auto count = 0; // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + std::vector filtered; + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // Accumulated project change notifications. - std::map projectChanges; + std::map projectChanges; - if(filtered.size() > 1) { + if (filtered.size() > 1) { feedback_affected("This command will alter {1} tasks.", filtered.size()); } - for (auto& task : filtered) - { - Task before (task); + for (auto& task : filtered) { + Task before(task); - if (task.getStatus () != Task::deleted) - { + if (task.getStatus() != Task::deleted) { // Delete the specified task. std::string question; - question = format ("Delete task {1} '{2}'?", - task.identifier (true), - task.get ("description")); + question = format("Delete task {1} '{2}'?", task.identifier(true), task.get("description")); - task.modify (Task::modAnnotate); - task.setStatus (Task::deleted); - if (! task.has ("end")) - task.setAsNow ("end"); + task.modify(Task::modAnnotate); + task.setStatus(Task::deleted); + if (!task.has("end")) task.setAsNow("end"); - if (permission (question, filtered.size ())) - { - updateRecurrenceMask (task); + if (permission(question, filtered.size())) { + updateRecurrenceMask(task); ++count; - Context::getContext ().tdb2.modify (task); - feedback_affected ("Deleting task {1} '{2}'.", task); - feedback_unblocked (task); - dependencyChainOnComplete (task); - if (Context::getContext ().verbose ("project")) - projectChanges[task.get ("project")] = onProjectChange (task); + Context::getContext().tdb2.modify(task); + feedback_affected("Deleting task {1} '{2}'.", task); + feedback_unblocked(task); + dependencyChainOnComplete(task); + if (Context::getContext().verbose("project")) + projectChanges[task.get("project")] = onProjectChange(task); // Delete siblings. - if (task.has ("parent")) - { - if ((Context::getContext ().config.get ("recurrence.confirmation") == "prompt" - && confirm (STRING_CMD_DELETE_CONFIRM_R)) || - Context::getContext ().config.getBoolean ("recurrence.confirmation")) - { - std::vector siblings = Context::getContext ().tdb2.siblings (task); - for (auto& sibling : siblings) - { - sibling.modify (Task::modAnnotate); - sibling.setStatus (Task::deleted); - if (! sibling.has ("end")) - sibling.setAsNow ("end"); + if (task.has("parent")) { + if ((Context::getContext().config.get("recurrence.confirmation") == "prompt" && + confirm(STRING_CMD_DELETE_CONFIRM_R)) || + Context::getContext().config.getBoolean("recurrence.confirmation")) { + std::vector siblings = Context::getContext().tdb2.siblings(task); + for (auto& sibling : siblings) { + sibling.modify(Task::modAnnotate); + sibling.setStatus(Task::deleted); + if (!sibling.has("end")) sibling.setAsNow("end"); - updateRecurrenceMask (sibling); - Context::getContext ().tdb2.modify (sibling); - feedback_affected (STRING_CMD_DELETE_TASK_R, sibling); - feedback_unblocked (sibling); + updateRecurrenceMask(sibling); + Context::getContext().tdb2.modify(sibling); + feedback_affected(STRING_CMD_DELETE_TASK_R, sibling); + feedback_unblocked(sibling); ++count; } // Delete the parent Task parent; - Context::getContext ().tdb2.get (task.get ("parent"), parent); - parent.setStatus (Task::deleted); - if (! parent.has ("end")) - parent.setAsNow ("end"); + Context::getContext().tdb2.get(task.get("parent"), parent); + parent.setStatus(Task::deleted); + if (!parent.has("end")) parent.setAsNow("end"); - Context::getContext ().tdb2.modify (parent); + Context::getContext().tdb2.modify(parent); } } // Task potentially has child tasks - optionally delete them. - else - { - std::vector children = Context::getContext ().tdb2.children (task); + else { + std::vector children = Context::getContext().tdb2.children(task); if (children.size() && - ((Context::getContext ().config.get ("recurrence.confirmation") == "prompt" - && confirm (STRING_CMD_DELETE_CONFIRM_R)) || - Context::getContext ().config.getBoolean ("recurrence.confirmation"))) - { - for (auto& child : children) - { - child.modify (Task::modAnnotate); - child.setStatus (Task::deleted); - if (! child.has ("end")) - child.setAsNow ("end"); + ((Context::getContext().config.get("recurrence.confirmation") == "prompt" && + confirm(STRING_CMD_DELETE_CONFIRM_R)) || + Context::getContext().config.getBoolean("recurrence.confirmation"))) { + for (auto& child : children) { + child.modify(Task::modAnnotate); + child.setStatus(Task::deleted); + if (!child.has("end")) child.setAsNow("end"); - updateRecurrenceMask (child); - Context::getContext ().tdb2.modify (child); - feedback_affected (STRING_CMD_DELETE_TASK_R, child); - feedback_unblocked (child); + updateRecurrenceMask(child); + Context::getContext().tdb2.modify(child); + feedback_affected(STRING_CMD_DELETE_TASK_R, child); + feedback_unblocked(child); ++count; } } } - } - else - { + } else { std::cout << "Task not deleted.\n"; rc = 1; - if (_permission_quit) - break; + if (_permission_quit) break; } - } - else - { - std::cout << format ("Task {1} '{2}' is not deletable.", - task.identifier (true), - task.get ("description")) - << '\n'; + } else { + std::cout << format("Task {1} '{2}' is not deletable.", task.identifier(true), + task.get("description")) + << '\n'; rc = 1; } } // Now list the project changes. for (const auto& change : projectChanges) - if (change.first != "") - Context::getContext ().footnote (change.second); + if (change.first != "") Context::getContext().footnote(change.second); - feedback_affected (count == 1 ? "Deleted {1} task." : "Deleted {1} tasks.", count); + feedback_affected(count == 1 ? "Deleted {1} task." : "Deleted {1} tasks.", count); return rc; } diff --git a/src/commands/CmdDelete.h b/src/commands/CmdDelete.h index 94345e622..2cc604b02 100644 --- a/src/commands/CmdDelete.h +++ b/src/commands/CmdDelete.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDDELETE #define INCLUDED_CMDDELETE -#include #include -class CmdDelete : public Command -{ -public: - CmdDelete (); - int execute (std::string&); +#include + +class CmdDelete : public Command { + public: + CmdDelete(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdDenotate.cpp b/src/commands/CmdDenotate.cpp index 1b3a56916..363834843 100644 --- a/src/commands/CmdDenotate.cpp +++ b/src/commands/CmdDenotate.cpp @@ -28,143 +28,124 @@ // cmake.h include header must come first #include -#include #include #include -#include #include -#include #include +#include +#include -#define STRING_CMD_DENO_NO "Task not denotated." -#define STRING_CMD_DENO_1 "Denotated {1} task." -#define STRING_CMD_DENO_N "Denotated {1} tasks." +#include + +#define STRING_CMD_DENO_NO "Task not denotated." +#define STRING_CMD_DENO_1 "Denotated {1} task." +#define STRING_CMD_DENO_N "Denotated {1} tasks." //////////////////////////////////////////////////////////////////////////////// -CmdDenotate::CmdDenotate () -{ - _keyword = "denotate"; - _usage = "task denotate "; - _description = "Deletes an annotation"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = true; - _accepts_filter = true; +CmdDenotate::CmdDenotate() { + _keyword = "denotate"; + _usage = "task denotate "; + _description = "Deletes an annotation"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdDenotate::execute (std::string&) -{ +int CmdDenotate::execute(std::string&) { auto rc = 0; auto count = 0; - auto sensitive = Context::getContext ().config.getBoolean ("search.case.sensitive"); + auto sensitive = Context::getContext().config.getBoolean("search.case.sensitive"); // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + std::vector filtered; + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // Extract all the ORIGINAL MODIFICATION args as simple text patterns. std::string pattern = ""; - for (auto& a : Context::getContext ().cli2._args) - { - if (a.hasTag ("MISCELLANEOUS")) - { - if (pattern != "") - pattern += ' '; + for (auto& a : Context::getContext().cli2._args) { + if (a.hasTag("MISCELLANEOUS")) { + if (pattern != "") pattern += ' '; - pattern += a.attribute ("raw"); + pattern += a.attribute("raw"); } } // Accumulated project change notifications. - std::map projectChanges; + std::map projectChanges; - if(filtered.size() > 1) { + if (filtered.size() > 1) { feedback_affected("This command will alter {1} tasks.", filtered.size()); } - for (auto& task : filtered) - { - Task before (task); + for (auto& task : filtered) { + Task before(task); - auto annotations = task.getAnnotations (); + auto annotations = task.getAnnotations(); - if (annotations.size () == 0) - throw std::string ("The specified task has no annotations that can be deleted."); + if (annotations.size() == 0) + throw std::string("The specified task has no annotations that can be deleted."); std::string anno; auto match = false; - for (auto i = annotations.begin (); i != annotations.end (); ++i) - { - if (i->second == pattern) - { + for (auto i = annotations.begin(); i != annotations.end(); ++i) { + if (i->second == pattern) { match = true; anno = i->second; - annotations.erase (i); - task.setAnnotations (annotations); + annotations.erase(i); + task.setAnnotations(annotations); break; } } - if (! match) - { - for (auto i = annotations.begin (); i != annotations.end (); ++i) - { - auto loc = find (i->second, pattern, sensitive); - if (loc != std::string::npos) - { + if (!match) { + for (auto i = annotations.begin(); i != annotations.end(); ++i) { + auto loc = find(i->second, pattern, sensitive); + if (loc != std::string::npos) { anno = i->second; - annotations.erase (i); - task.setAnnotations (annotations); + annotations.erase(i); + task.setAnnotations(annotations); break; } } } - if (before.getAnnotations () != task.getAnnotations ()) - { - auto question = format ("Denotate task {1} '{2}'?", - task.identifier (true), - task.get ("description")); + if (before.getAnnotations() != task.getAnnotations()) { + auto question = + format("Denotate task {1} '{2}'?", task.identifier(true), task.get("description")); - if (permission (before.diff (task) + question, filtered.size ())) - { + if (permission(before.diff(task) + question, filtered.size())) { ++count; - Context::getContext ().tdb2.modify (task); - feedback_affected (format ("Found annotation '{1}' and deleted it.", anno)); - if (Context::getContext ().verbose ("project")) - projectChanges[task.get ("project")] = onProjectChange (task, false); - } - else - { + Context::getContext().tdb2.modify(task); + feedback_affected(format("Found annotation '{1}' and deleted it.", anno)); + if (Context::getContext().verbose("project")) + projectChanges[task.get("project")] = onProjectChange(task, false); + } else { std::cout << STRING_CMD_DENO_NO << '\n'; rc = 1; - if (_permission_quit) - break; + if (_permission_quit) break; } - } - else - { - std::cout << format ("Did not find any matching annotation to be deleted for '{1}'.\n", pattern); + } else { + std::cout << format("Did not find any matching annotation to be deleted for '{1}'.\n", + pattern); rc = 1; } } // Now list the project changes. for (const auto& change : projectChanges) - if (change.first != "") - Context::getContext ().footnote (change.second); + if (change.first != "") Context::getContext().footnote(change.second); - feedback_affected (count == 1 ? STRING_CMD_DENO_1 : STRING_CMD_DENO_N, count); + feedback_affected(count == 1 ? STRING_CMD_DENO_1 : STRING_CMD_DENO_N, count); return rc; } diff --git a/src/commands/CmdDenotate.h b/src/commands/CmdDenotate.h index 87ffd0f17..e45c9e2b1 100644 --- a/src/commands/CmdDenotate.h +++ b/src/commands/CmdDenotate.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDDENOTATE #define INCLUDED_CMDDENOTATE -#include #include -class CmdDenotate : public Command -{ -public: - CmdDenotate (); - int execute (std::string&); +#include + +class CmdDenotate : public Command { + public: + CmdDenotate(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdDiagnostics.cpp b/src/commands/CmdDiagnostics.cpp index 7901524cf..e4ac10bae 100644 --- a/src/commands/CmdDiagnostics.cpp +++ b/src/commands/CmdDiagnostics.cpp @@ -28,33 +28,33 @@ // cmake.h include header must come first #include -#include -#include -#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include + +#include +#include +#include #ifdef HAVE_COMMIT #include #endif //////////////////////////////////////////////////////////////////////////////// -CmdDiagnostics::CmdDiagnostics () -{ - _keyword = "diagnostics"; - _usage = "task diagnostics"; - _description = "Platform, build and environment details"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdDiagnostics::CmdDiagnostics() { + _keyword = "diagnostics"; + _usage = "task diagnostics"; + _description = "Platform, build and environment details"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::misc; + _category = Command::Category::misc; } //////////////////////////////////////////////////////////////////////////////// @@ -62,26 +62,19 @@ CmdDiagnostics::CmdDiagnostics () // // Although this will change over time, initially this command will answer the // kind of questions we always have to ask whenever something is wrong. -int CmdDiagnostics::execute (std::string& output) -{ +int CmdDiagnostics::execute(std::string& output) { Color bold; - if (Context::getContext ().color ()) - bold = Color ("bold"); + if (Context::getContext().color()) bold = Color("bold"); std::stringstream out; - out << '\n' - << bold.colorize (PACKAGE_STRING) - << '\n'; + out << '\n' << bold.colorize(PACKAGE_STRING) << '\n'; - out << " Platform: " << osName () - << "\n\n"; + out << " Platform: " << osName() << "\n\n"; // Compiler. - out << bold.colorize ("Compiler") - << '\n' + out << bold.colorize("Compiler") << '\n' #ifdef __VERSION__ - << " Version: " - << __VERSION__ << '\n' + << " Version: " << __VERSION__ << '\n' #endif << " Caps:" #ifdef __STDC__ @@ -105,20 +98,13 @@ int CmdDiagnostics::execute (std::string& output) #ifdef _LP64 << " +LP64" #endif - << " +c" << 8 * sizeof (char) - << " +i" << 8 * sizeof (int) - << " +l" << 8 * sizeof (long) - << " +vp" << 8 * sizeof (void*) - << " +time_t" << 8 * sizeof (time_t) - << '\n'; + << " +c" << 8 * sizeof(char) << " +i" << 8 * sizeof(int) << " +l" << 8 * sizeof(long) + << " +vp" << 8 * sizeof(void*) << " +time_t" << 8 * sizeof(time_t) << '\n'; // Compiler compliance level. - out << " Compliance: " - << cppCompliance () - << "\n\n"; + out << " Compliance: " << cppCompliance() << "\n\n"; - out << bold.colorize ("Build Features") - << '\n' + out << bold.colorize("Build Features") << '\n' #ifdef HAVE_COMMIT << " Commit: " << COMMIT << '\n' @@ -142,205 +128,146 @@ int CmdDiagnostics::execute (std::string& output) << "\n\n"; // Config: .taskrc found, readable, writable - File rcFile (Context::getContext ().config.file ()); - out << bold.colorize ("Configuration") - << '\n' - << " File: " << rcFile._data << ' ' - << (rcFile.exists () - ? "(found)" - : "(missing)") - << ", " << rcFile.size () << ' ' << "bytes" - << ", mode " - << std::setbase (8) - << rcFile.mode () - << '\n'; + File rcFile(Context::getContext().config.file()); + out << bold.colorize("Configuration") << '\n' + << " File: " << rcFile._data << ' ' << (rcFile.exists() ? "(found)" : "(missing)") + << ", " << rcFile.size() << ' ' << "bytes" + << ", mode " << std::setbase(8) << rcFile.mode() << '\n'; // Config: data.location found, readable, writable - File location (Context::getContext ().config.get ("data.location")); - out << " Data: " << location._data << ' ' - << (location.exists () - ? "(found)" - : "(missing)") - << ", " << (location.is_directory () ? "dir" : "?") - << ", mode " - << std::setbase (8) - << location.mode () - << '\n'; + File location(Context::getContext().config.get("data.location")); + out << " Data: " << location._data << ' ' << (location.exists() ? "(found)" : "(missing)") + << ", " << (location.is_directory() ? "dir" : "?") << ", mode " << std::setbase(8) + << location.mode() << '\n'; - char* env = getenv ("TASKRC"); - if (env) - out << " TASKRC: " - << env - << '\n'; + char* env = getenv("TASKRC"); + if (env) out << " TASKRC: " << env << '\n'; - env = getenv ("TASKDATA"); - if (env) - out << " TASKDATA: " - << env - << '\n'; + env = getenv("TASKDATA"); + if (env) out << " TASKDATA: " << env << '\n'; - out << " GC: " - << (Context::getContext ().config.getBoolean ("gc") - ? "Enabled" - : "Disabled") + out << " GC: " << (Context::getContext().config.getBoolean("gc") ? "Enabled" : "Disabled") << '\n'; // Determine rc.editor/$EDITOR/$VISUAL. char* peditor; - if (Context::getContext ().config.get ("editor") != "") - out << " rc.editor: " << Context::getContext ().config.get ("editor") << '\n'; - else if ((peditor = getenv ("VISUAL")) != nullptr) + if (Context::getContext().config.get("editor") != "") + out << " rc.editor: " << Context::getContext().config.get("editor") << '\n'; + else if ((peditor = getenv("VISUAL")) != nullptr) out << " $VISUAL: " << peditor << '\n'; - else if ((peditor = getenv ("EDITOR")) != nullptr) + else if ((peditor = getenv("EDITOR")) != nullptr) out << " $EDITOR: " << peditor << '\n'; // Display hook status. Path hookLocation; - if (Context::getContext ().config.has ("hooks.location")) - { - hookLocation = Path (Context::getContext ().config.get ("hooks.location")); - } - else - { - hookLocation = Path (Context::getContext ().config.get ("data.location")); + if (Context::getContext().config.has("hooks.location")) { + hookLocation = Path(Context::getContext().config.get("hooks.location")); + } else { + hookLocation = Path(Context::getContext().config.get("data.location")); hookLocation += "hooks"; } - out << bold.colorize ("Hooks") - << '\n' + out << bold.colorize("Hooks") << '\n' << " System: " - << (Context::getContext ().config.getBoolean ("hooks") ? "Enabled" : "Disabled") - << '\n' - << " Location: " - << static_cast (hookLocation) - << '\n'; + << (Context::getContext().config.getBoolean("hooks") ? "Enabled" : "Disabled") << '\n' + << " Location: " << static_cast(hookLocation) << '\n'; - auto hooks = Context::getContext ().hooks.list (); - if (hooks.size ()) - { + auto hooks = Context::getContext().hooks.list(); + if (hooks.size()) { unsigned int longest = 0; for (auto& hook : hooks) - if (hook.length () > longest) - longest = hook.length (); - longest -= hookLocation._data.length () + 1; + if (hook.length() > longest) longest = hook.length(); + longest -= hookLocation._data.length() + 1; out << " Active: "; int count = 0; - for (auto& hook : hooks) - { - Path p (hook); - if (! p.is_directory ()) - { - auto name = p.name (); + for (auto& hook : hooks) { + Path p(hook); + if (!p.is_directory()) { + auto name = p.name(); - if (p.executable () && - (name.substr (0, 6) == "on-add" || - name.substr (0, 9) == "on-modify" || - name.substr (0, 9) == "on-launch" || - name.substr (0, 7) == "on-exit")) - { + if (p.executable() && + (name.substr(0, 6) == "on-add" || name.substr(0, 9) == "on-modify" || + name.substr(0, 9) == "on-launch" || name.substr(0, 7) == "on-exit")) { out << (count++ ? " " : ""); - out.width (longest); - out << std::left << name - << " (executable)" - << (p.is_link () ? " (symlink)" : "") - << '\n'; + out.width(longest); + out << std::left << name << " (executable)" << (p.is_link() ? " (symlink)" : "") << '\n'; } } } - if (! count) - out << '\n'; + if (!count) out << '\n'; out << " Inactive: "; count = 0; - for (auto& hook : hooks) - { - Path p (hook); - if (! p.is_directory ()) - { - auto name = p.name (); + for (auto& hook : hooks) { + Path p(hook); + if (!p.is_directory()) { + auto name = p.name(); - if (! p.executable () || - (name.substr (0, 6) != "on-add" && - name.substr (0, 9) != "on-modify" && - name.substr (0, 9) != "on-launch" && - name.substr (0, 7) != "on-exit")) - { + if (!p.executable() || + (name.substr(0, 6) != "on-add" && name.substr(0, 9) != "on-modify" && + name.substr(0, 9) != "on-launch" && name.substr(0, 7) != "on-exit")) { out << (count++ ? " " : ""); - out.width (longest); - out << std::left << name - << (p.executable () ? " (executable)" : " (not executable)") - << (p.is_link () ? " (symlink)" : "") - << ((name.substr (0, 6) == "on-add" || - name.substr (0, 9) == "on-modify" || - name.substr (0, 9) == "on-launch" || - name.substr (0, 7) == "on-exit") ? "" : "unrecognized hook name") + out.width(longest); + out << std::left << name << (p.executable() ? " (executable)" : " (not executable)") + << (p.is_link() ? " (symlink)" : "") + << ((name.substr(0, 6) == "on-add" || name.substr(0, 9) == "on-modify" || + name.substr(0, 9) == "on-launch" || name.substr(0, 7) == "on-exit") + ? "" + : "unrecognized hook name") << '\n'; } } } - if (! count) - out << '\n'; - } - else + if (!count) out << '\n'; + } else out << " (-none-)\n"; out << '\n'; // Verify UUIDs are all unique. - out << bold.colorize ("Tests") - << '\n'; + out << bold.colorize("Tests") << '\n'; // Report terminal dimensions. - out << " Terminal: " - << Context::getContext ().getWidth () - << 'x' - << Context::getContext ().getHeight () - << '\n'; + out << " Terminal: " << Context::getContext().getWidth() << 'x' + << Context::getContext().getHeight() << '\n'; // Check all the UUID references - auto all = Context::getContext ().tdb2.all_tasks (); + auto all = Context::getContext().tdb2.all_tasks(); bool noBrokenRefs = true; - out << " Broken ref: " - << format ("Scanned {1} tasks for broken references:", all.size ()) - << '\n'; + out << " Broken ref: " << format("Scanned {1} tasks for broken references:", all.size()) << '\n'; - for (auto& task : all) - { + for (auto& task : all) { // Check dependencies - for (auto& uuid : task.getDependencyUUIDs ()) - { - if (! Context::getContext ().tdb2.has (uuid)) - { + for (auto& uuid : task.getDependencyUUIDs()) { + if (!Context::getContext().tdb2.has(uuid)) { out << " " - << format ("Task {1} depends on nonexistent task: {2}", task.get ("uuid"), uuid) - << '\n'; + << format("Task {1} depends on nonexistent task: {2}", task.get("uuid"), uuid) << '\n'; noBrokenRefs = false; } } // Check recurrence parent - auto parentUUID = task.get ("parent"); + auto parentUUID = task.get("parent"); - if (parentUUID != "" && ! Context::getContext ().tdb2.has (parentUUID)) - { + if (parentUUID != "" && !Context::getContext().tdb2.has(parentUUID)) { out << " " - << format ("Task {1} has nonexistent recurrence template {2}", task.get ("uuid"), parentUUID) + << format("Task {1} has nonexistent recurrence template {2}", task.get("uuid"), + parentUUID) << '\n'; noBrokenRefs = false; } } - if (noBrokenRefs) - out << " No broken references found\n"; + if (noBrokenRefs) out << " No broken references found\n"; out << '\n'; - output = out.str (); + output = out.str(); return 0; } diff --git a/src/commands/CmdDiagnostics.h b/src/commands/CmdDiagnostics.h index 0cb399e84..0d4adf62b 100644 --- a/src/commands/CmdDiagnostics.h +++ b/src/commands/CmdDiagnostics.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDDIAGNOSTICS #define INCLUDED_CMDDIAGNOSTICS -#include #include -class CmdDiagnostics : public Command -{ -public: - CmdDiagnostics (); - int execute (std::string&); +#include + +class CmdDiagnostics : public Command { + public: + CmdDiagnostics(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdDone.cpp b/src/commands/CmdDone.cpp index e6b8af922..1e3638d33 100644 --- a/src/commands/CmdDone.cpp +++ b/src/commands/CmdDone.cpp @@ -28,118 +28,102 @@ // cmake.h include header must come first #include -#include #include #include -#include #include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -CmdDone::CmdDone () -{ - _keyword = "done"; - _usage = "task done "; - _description = "Marks the specified task as completed"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = true; - _accepts_filter = true; +CmdDone::CmdDone() { + _keyword = "done"; + _usage = "task done "; + _description = "Marks the specified task as completed"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdDone::execute (std::string&) -{ +int CmdDone::execute(std::string&) { auto rc = 0; auto count = 0; // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + std::vector filtered; + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // Accumulated project change notifications. - std::map projectChanges; + std::map projectChanges; - if(filtered.size() > 1) { + if (filtered.size() > 1) { feedback_affected("This command will alter {1} tasks.", filtered.size()); } - std::vector modified; - for (auto& task : filtered) - { - Task before (task); + std::vector modified; + for (auto& task : filtered) { + Task before(task); - if (task.getStatus () == Task::pending || - task.getStatus () == Task::waiting) - { + if (task.getStatus() == Task::pending || task.getStatus() == Task::waiting) { // Complete the specified task. - std::string question = format ("Complete task {1} '{2}'?", - task.identifier (true), - task.get ("description")); + std::string question = + format("Complete task {1} '{2}'?", task.identifier(true), task.get("description")); - task.modify (Task::modAnnotate); - task.setStatus (Task::completed); - if (! task.has ("end")) - task.setAsNow ("end"); + task.modify(Task::modAnnotate); + task.setStatus(Task::completed); + if (!task.has("end")) task.setAsNow("end"); // Stop the task, if started. - if (task.has ("start")) - { - task.remove ("start"); - if (Context::getContext ().config.getBoolean ("journal.time")) - task.addAnnotation (Context::getContext ().config.get ("journal.time.stop.annotation")); + if (task.has("start")) { + task.remove("start"); + if (Context::getContext().config.getBoolean("journal.time")) + task.addAnnotation(Context::getContext().config.get("journal.time.stop.annotation")); } - if (permission (before.diff (task) + question, filtered.size ())) - { - updateRecurrenceMask (task); - Context::getContext ().tdb2.modify (task); + if (permission(before.diff(task) + question, filtered.size())) { + updateRecurrenceMask(task); + Context::getContext().tdb2.modify(task); ++count; - feedback_affected ("Completed task {1} '{2}'.", task); - feedback_unblocked (task); - dependencyChainOnComplete (task); - if (Context::getContext ().verbose ("project")) - projectChanges[task.get ("project")] = onProjectChange (task); + feedback_affected("Completed task {1} '{2}'.", task); + feedback_unblocked(task); + dependencyChainOnComplete(task); + if (Context::getContext().verbose("project")) + projectChanges[task.get("project")] = onProjectChange(task); // Save unmodified task for potential nagging later modified.push_back(before); - } - else - { + } else { std::cout << "Task not completed.\n"; rc = 1; - if (_permission_quit) - break; + if (_permission_quit) break; } - } - else - { - std::cout << format ("Task {1} '{2}' is neither pending nor waiting.", - task.identifier (true), - task.get ("description")) + } else { + std::cout << format("Task {1} '{2}' is neither pending nor waiting.", task.identifier(true), + task.get("description")) << '\n'; rc = 1; } } - nag (modified); + nag(modified); // Now list the project changes. for (const auto& change : projectChanges) - if (change.first != "") - Context::getContext ().footnote (change.second); + if (change.first != "") Context::getContext().footnote(change.second); - feedback_affected (count == 1 ? "Completed {1} task." : "Completed {1} tasks.", count); + feedback_affected(count == 1 ? "Completed {1} task." : "Completed {1} tasks.", count); return rc; } diff --git a/src/commands/CmdDone.h b/src/commands/CmdDone.h index 422a0e5e4..3aa4ce8b8 100644 --- a/src/commands/CmdDone.h +++ b/src/commands/CmdDone.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDDONE #define INCLUDED_CMDDONE -#include #include -class CmdDone : public Command -{ -public: - CmdDone (); - int execute (std::string&); +#include + +class CmdDone : public Command { + public: + CmdDone(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdDuplicate.cpp b/src/commands/CmdDuplicate.cpp index 386a301e7..381d6c368 100644 --- a/src/commands/CmdDuplicate.cpp +++ b/src/commands/CmdDuplicate.cpp @@ -28,119 +28,109 @@ // cmake.h include header must come first #include -#include #include #include #include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -CmdDuplicate::CmdDuplicate () -{ - _keyword = "duplicate"; - _usage = "task duplicate "; - _description = "Duplicates the specified tasks"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = true; - _accepts_filter = true; +CmdDuplicate::CmdDuplicate() { + _keyword = "duplicate"; + _usage = "task duplicate "; + _description = "Duplicates the specified tasks"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdDuplicate::execute (std::string&) -{ +int CmdDuplicate::execute(std::string&) { auto rc = 0; auto count = 0; // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + std::vector filtered; + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // Accumulated project change notifications. - std::map projectChanges; + std::map projectChanges; - for (auto& task : filtered) - { + for (auto& task : filtered) { // Duplicate the specified task. - Task dup (task); - dup.id = 0; // Reset, and TDB2::add will set. - dup.set ("uuid", uuid ()); // Needs a new UUID. - dup.remove ("start"); // Does not inherit start date. - dup.remove ("end"); // Does not inherit end date. - dup.remove ("entry"); // Does not inherit entry date. + Task dup(task); + dup.id = 0; // Reset, and TDB2::add will set. + dup.set("uuid", uuid()); // Needs a new UUID. + dup.remove("start"); // Does not inherit start date. + dup.remove("end"); // Does not inherit end date. + dup.remove("entry"); // Does not inherit entry date. // When duplicating a child task, downgrade it to a plain task. - if (dup.has ("parent")) - { - dup.remove ("parent"); - dup.remove ("recur"); - dup.remove ("until"); - dup.remove ("imask"); - std::cout << format ("Note: task {1} was a recurring task. The duplicated task is not.", task.identifier ()) - << '\n'; + if (dup.has("parent")) { + dup.remove("parent"); + dup.remove("recur"); + dup.remove("until"); + dup.remove("imask"); + std::cout << format("Note: task {1} was a recurring task. The duplicated task is not.", + task.identifier()) + << '\n'; } // When duplicating a parent task, create a new parent task. - else if (dup.getStatus () == Task::recurring) - { - dup.remove ("mask"); - std::cout << format ("Note: task {1} was a parent recurring task. The duplicated task is too.", task.identifier ()) - << '\n'; + else if (dup.getStatus() == Task::recurring) { + dup.remove("mask"); + std::cout << format( + "Note: task {1} was a parent recurring task. The duplicated task is too.", + task.identifier()) + << '\n'; } - dup.setStatus (Task::pending); // Does not inherit status. + dup.setStatus(Task::pending); // Does not inherit status. // Must occur after Task::recurring check. - dup.modify (Task::modAnnotate); + dup.modify(Task::modAnnotate); - if (permission (format ("Duplicate task {1} '{2}'?", - task.identifier (true), - task.get ("description")), - filtered.size ())) - { - Context::getContext ().tdb2.add (dup); + if (permission( + format("Duplicate task {1} '{2}'?", task.identifier(true), task.get("description")), + filtered.size())) { + Context::getContext().tdb2.add(dup); ++count; - feedback_affected ("Duplicated task {1} '{2}'.", task); + feedback_affected("Duplicated task {1} '{2}'.", task); - auto status = dup.getStatus (); - if (Context::getContext ().verbose ("new-id") && - (status == Task::pending || - status == Task::waiting)) - std::cout << format ("Created task {1}.\n", dup.id); + auto status = dup.getStatus(); + if (Context::getContext().verbose("new-id") && + (status == Task::pending || status == Task::waiting)) + std::cout << format("Created task {1}.\n", dup.id); - else if (Context::getContext ().verbose ("new-uuid") && - status != Task::recurring) - std::cout << format ("Created task {1}.\n", dup.get ("uuid")); + else if (Context::getContext().verbose("new-uuid") && status != Task::recurring) + std::cout << format("Created task {1}.\n", dup.get("uuid")); - if (Context::getContext ().verbose ("project")) - projectChanges[task.get ("project")] = onProjectChange (task); - } - else - { + if (Context::getContext().verbose("project")) + projectChanges[task.get("project")] = onProjectChange(task); + } else { std::cout << "Task not duplicated.\n"; rc = 1; - if (_permission_quit) - break; + if (_permission_quit) break; } } // Now list the project changes. for (const auto& change : projectChanges) - if (change.first != "") - Context::getContext ().footnote (change.second); + if (change.first != "") Context::getContext().footnote(change.second); - feedback_affected (count == 1 ? "Duplicated {1} task." : "Duplicated {1} tasks.", count); + feedback_affected(count == 1 ? "Duplicated {1} task." : "Duplicated {1} tasks.", count); return rc; } diff --git a/src/commands/CmdDuplicate.h b/src/commands/CmdDuplicate.h index 7360faccc..b05a53ab1 100644 --- a/src/commands/CmdDuplicate.h +++ b/src/commands/CmdDuplicate.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDDUPLICATE #define INCLUDED_CMDDUPLICATE -#include #include -class CmdDuplicate : public Command -{ -public: - CmdDuplicate (); - int execute (std::string&); +#include + +class CmdDuplicate : public Command { + public: + CmdDuplicate(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdEdit.cpp b/src/commands/CmdEdit.cpp index 7e8c93023..acdd1ce4c 100644 --- a/src/commands/CmdEdit.cpp +++ b/src/commands/CmdEdit.cpp @@ -28,105 +28,94 @@ // cmake.h include header must come first #include -#include -#include -#include -#include -#include -#include -#include +#include #include #include -#include -#include #include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include -#define STRING_EDIT_START_MOD "Start date modified." -#define STRING_EDIT_SCHED_MOD "Scheduled date modified." -#define STRING_EDIT_DUE_MOD "Due date modified." -#define STRING_EDIT_UNTIL_MOD "Until date modified." -#define STRING_EDIT_WAIT_MOD "Wait date modified." +#include +#include +#include +#include +#include +#include + +#define STRING_EDIT_START_MOD "Start date modified." +#define STRING_EDIT_SCHED_MOD "Scheduled date modified." +#define STRING_EDIT_DUE_MOD "Due date modified." +#define STRING_EDIT_UNTIL_MOD "Until date modified." +#define STRING_EDIT_WAIT_MOD "Wait date modified." const std::string CmdEdit::ANNOTATION_EDIT_MARKER = "\n "; //////////////////////////////////////////////////////////////////////////////// -CmdEdit::CmdEdit () -{ - _keyword = "edit"; - _usage = "task edit"; - _description = "Launches an editor to modify a task directly"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = true; - _accepts_filter = true; +CmdEdit::CmdEdit() { + _keyword = "edit"; + _usage = "task edit"; + _description = "Launches an editor to modify a task directly"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// // Introducing the Silver Bullet. This feature is the catch-all fixative for // various other ills. This is like opening up the hood and going in with a // wrench. To be used sparingly. -int CmdEdit::execute (std::string&) -{ +int CmdEdit::execute(std::string&) { // Filter the tasks. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); - if (! filtered.size ()) - { - Context::getContext ().footnote ("No matches."); + if (!filtered.size()) { + Context::getContext().footnote("No matches."); return 1; } - unsigned int bulk = Context::getContext ().config.getInteger ("bulk"); + unsigned int bulk = Context::getContext().config.getInteger("bulk"); // If we are editing more than "bulk" tasks, ask for confirmation. // Bulk = 0 denotes infinite bulk. - if ((filtered.size () > bulk) && (bulk != 0)) - if (! confirm (format ("Do you wish to manually edit {1} tasks?", filtered.size ()))) - return 2; + if ((filtered.size() > bulk) && (bulk != 0)) + if (!confirm(format("Do you wish to manually edit {1} tasks?", filtered.size()))) return 2; // Find number of matching tasks. - for (auto& task : filtered) - { - auto result = editFile (task); + for (auto& task : filtered) { + auto result = editFile(task); if (result == CmdEdit::editResult::error) break; else if (result == CmdEdit::editResult::changes) - Context::getContext ().tdb2.modify (task); + Context::getContext().tdb2.modify(task); } return 0; } //////////////////////////////////////////////////////////////////////////////// -std::string CmdEdit::findValue ( - const std::string& text, - const std::string& name) -{ - auto found = text.find (name); - if (found != std::string::npos) - { - auto eol = text.find ('\n', found + 1); - if (eol != std::string::npos) - { - std::string value = text.substr ( - found + name.length (), - eol - (found + name.length ())); +std::string CmdEdit::findValue(const std::string& text, const std::string& name) { + auto found = text.find(name); + if (found != std::string::npos) { + auto eol = text.find('\n', found + 1); + if (eol != std::string::npos) { + std::string value = text.substr(found + name.length(), eol - (found + name.length())); - return Lexer::trim (value, "\t "); + return Lexer::trim(value, "\t "); } } @@ -134,47 +123,34 @@ std::string CmdEdit::findValue ( } //////////////////////////////////////////////////////////////////////////////// -std::string CmdEdit::findMultilineValue ( - const std::string& text, - const std::string& startMarker, - const std::string& endMarker) -{ - auto start = text.find (startMarker); - if (start != std::string::npos) - { - auto end = text.find (endMarker, start); - if (end != std::string::npos) - { - std::string value = text.substr (start + startMarker.length (), - end - (start + startMarker.length ())); - return Lexer::trim (value, "\\\t "); +std::string CmdEdit::findMultilineValue(const std::string& text, const std::string& startMarker, + const std::string& endMarker) { + auto start = text.find(startMarker); + if (start != std::string::npos) { + auto end = text.find(endMarker, start); + if (end != std::string::npos) { + std::string value = + text.substr(start + startMarker.length(), end - (start + startMarker.length())); + return Lexer::trim(value, "\\\t "); } } return ""; } //////////////////////////////////////////////////////////////////////////////// -std::vector CmdEdit::findValues ( - const std::string& text, - const std::string& name) -{ - std::vector results; +std::vector CmdEdit::findValues(const std::string& text, const std::string& name) { + std::vector results; std::string::size_type found = 0; - while (found != std::string::npos) - { - found = text.find (name, found + 1); - if (found != std::string::npos) - { - auto eol = text.find ('\n', found + 1); - if (eol != std::string::npos) - { - auto value = text.substr ( - found + name.length (), - eol - (found + name.length ())); + while (found != std::string::npos) { + found = text.find(name, found + 1); + if (found != std::string::npos) { + auto eol = text.find('\n', found + 1); + if (eol != std::string::npos) { + auto value = text.substr(found + name.length(), eol - (found + name.length())); found = eol - 1; - results.push_back (Lexer::trim (value, "\t ")); + results.push_back(Lexer::trim(value, "\t ")); } } } @@ -183,35 +159,26 @@ std::vector CmdEdit::findValues ( } //////////////////////////////////////////////////////////////////////////////// -std::string CmdEdit::formatDate ( - Task& task, - const std::string& attribute, - const std::string& dateformat) -{ - auto value = task.get (attribute); - if (value.length ()) - value = Datetime (value).toString (dateformat); +std::string CmdEdit::formatDate(Task& task, const std::string& attribute, + const std::string& dateformat) { + auto value = task.get(attribute); + if (value.length()) value = Datetime(value).toString(dateformat); return value; } //////////////////////////////////////////////////////////////////////////////// -std::string CmdEdit::formatDuration ( - Task& task, - const std::string& attribute) -{ - auto value = task.get (attribute); - if (value.length ()) - value = Duration (value).formatISO (); +std::string CmdEdit::formatDuration(Task& task, const std::string& attribute) { + auto value = task.get(attribute); + if (value.length()) value = Duration(value).formatISO(); return value; } //////////////////////////////////////////////////////////////////////////////// -std::string CmdEdit::formatTask (Task task, const std::string& dateformat) -{ +std::string CmdEdit::formatTask(Task task, const std::string& dateformat) { std::stringstream before; - auto verbose = Context::getContext ().verbose ("edit"); + auto verbose = Context::getContext().verbose("edit"); if (verbose) before << "# The 'task edit' command allows you to modify all aspects of a task\n" @@ -232,408 +199,321 @@ std::string CmdEdit::formatTask (Task task, const std::string& dateformat) before << "# Name Editable details\n" << "# ----------------- ----------------------------------------------------\n" - << "# ID: " << task.id << '\n' - << "# UUID: " << task.get ("uuid") << '\n' - << "# Status: " << Lexer::ucFirst (Task::statusToText (task.getStatus ())) << '\n' - << "# Mask: " << task.get ("mask") << '\n' - << "# iMask: " << task.get ("imask") << '\n' - << " Project: " << task.get ("project") << '\n'; + << "# ID: " << task.id << '\n' + << "# UUID: " << task.get("uuid") << '\n' + << "# Status: " << Lexer::ucFirst(Task::statusToText(task.getStatus())) << '\n' + << "# Mask: " << task.get("mask") << '\n' + << "# iMask: " << task.get("imask") << '\n' + << " Project: " << task.get("project") << '\n'; + + if (verbose) before << "# Separate the tags with spaces, like this: tag1 tag2\n"; + + before << " Tags: " << join(" ", task.getTags()) << '\n' + << " Description: " << task.get("description") << '\n' + << " Created: " << formatDate(task, "entry", dateformat) << '\n' + << " Started: " << formatDate(task, "start", dateformat) << '\n' + << " Ended: " << formatDate(task, "end", dateformat) << '\n' + << " Scheduled: " << formatDate(task, "scheduled", dateformat) << '\n' + << " Due: " << formatDate(task, "due", dateformat) << '\n' + << " Until: " << formatDate(task, "until", dateformat) << '\n' + << " Recur: " << task.get("recur") << '\n' + << " Wait until: " << formatDate(task, "wait", dateformat) << '\n' + << "# Modified: " << formatDate(task, "modified", dateformat) << '\n' + << " Parent: " << task.get("parent") << '\n'; if (verbose) - before << "# Separate the tags with spaces, like this: tag1 tag2\n"; + before + << "# Annotations look like this: -- and there can be any number of them.\n" + "# The ' -- ' separator between the date and text field should not be removed.\n" + "# Multiline annotations need to be indented up to (" + << ANNOTATION_EDIT_MARKER.length() - 1 + << " spaces).\n" + "# A \"blank slot\" for adding an annotation follows for your convenience.\n"; - before << " Tags: " << join (" ", task.getTags ()) << '\n' - << " Description: " << task.get ("description") << '\n' - << " Created: " << formatDate (task, "entry", dateformat) << '\n' - << " Started: " << formatDate (task, "start", dateformat) << '\n' - << " Ended: " << formatDate (task, "end", dateformat) << '\n' - << " Scheduled: " << formatDate (task, "scheduled", dateformat) << '\n' - << " Due: " << formatDate (task, "due", dateformat) << '\n' - << " Until: " << formatDate (task, "until", dateformat) << '\n' - << " Recur: " << task.get ("recur") << '\n' - << " Wait until: " << formatDate (task, "wait", dateformat) << '\n' - << "# Modified: " << formatDate (task, "modified", dateformat) << '\n' - << " Parent: " << task.get ("parent") << '\n'; - - if (verbose) - before << "# Annotations look like this: -- and there can be any number of them.\n" - "# The ' -- ' separator between the date and text field should not be removed.\n" - "# Multiline annotations need to be indented up to (" << ANNOTATION_EDIT_MARKER.length () - 1 << " spaces).\n" - "# A \"blank slot\" for adding an annotation follows for your convenience.\n"; - - for (auto& anno : task.getAnnotations ()) - { - Datetime dt (strtoll (anno.first.substr (11).c_str (), nullptr, 10)); - before << " Annotation: " << dt.toString (dateformat) - << " -- " << str_replace (anno.second, "\n", ANNOTATION_EDIT_MARKER) << '\n'; + for (auto& anno : task.getAnnotations()) { + Datetime dt(strtoll(anno.first.substr(11).c_str(), nullptr, 10)); + before << " Annotation: " << dt.toString(dateformat) << " -- " + << str_replace(anno.second, "\n", ANNOTATION_EDIT_MARKER) << '\n'; } Datetime now; - before << " Annotation: " << now.toString (dateformat) << " -- \n"; + before << " Annotation: " << now.toString(dateformat) << " -- \n"; // Add dependencies here. - auto dependencies = task.getDependencyUUIDs (); + auto dependencies = task.getDependencyUUIDs(); std::stringstream allDeps; - for (unsigned int i = 0; i < dependencies.size (); ++i) - { - if (i) - allDeps << ","; + for (unsigned int i = 0; i < dependencies.size(); ++i) { + if (i) allDeps << ","; Task t; - Context::getContext ().tdb2.get (dependencies[i], t); - if (t.getStatus () == Task::pending || - t.getStatus () == Task::waiting) + Context::getContext().tdb2.get(dependencies[i], t); + if (t.getStatus() == Task::pending || t.getStatus() == Task::waiting) allDeps << t.id; else allDeps << dependencies[i]; } if (verbose) - before << "# Dependencies should be a comma-separated list of task IDs/UUIDs or ID ranges, with no spaces.\n"; + before << "# Dependencies should be a comma-separated list of task IDs/UUIDs or ID ranges, " + "with no spaces.\n"; - before << " Dependencies: " << allDeps.str () << '\n'; + before << " Dependencies: " << allDeps.str() << '\n'; // UDAs - std::vector udas; - for (auto& col : Context::getContext ().columns) - if (Context::getContext ().config.get ("uda." + col.first + ".type") != "") - udas.push_back (col.first); + std::vector udas; + for (auto& col : Context::getContext().columns) + if (Context::getContext().config.get("uda." + col.first + ".type") != "") + udas.push_back(col.first); - if (udas.size ()) - { + if (udas.size()) { before << "# User Defined Attributes\n"; - std::sort (udas.begin (), udas.end ()); - for (auto& uda : udas) - { - int pad = 13 - uda.length (); + std::sort(udas.begin(), udas.end()); + for (auto& uda : udas) { + int pad = 13 - uda.length(); std::string padding = ""; - if (pad > 0) - padding = std::string (pad, ' '); + if (pad > 0) padding = std::string(pad, ' '); - std::string type = Context::getContext ().config.get ("uda." + uda + ".type"); - if (type == "string" || type == "numeric") - { - auto value = task.get (uda); - if (type == "string") - value = json::encode (value); + std::string type = Context::getContext().config.get("uda." + uda + ".type"); + if (type == "string" || type == "numeric") { + auto value = task.get(uda); + if (type == "string") value = json::encode(value); before << " UDA " << uda << ": " << padding << value << '\n'; - } - else if (type == "date") - before << " UDA " << uda << ": " << padding << formatDate (task, uda, dateformat) << '\n'; + } else if (type == "date") + before << " UDA " << uda << ": " << padding << formatDate(task, uda, dateformat) << '\n'; else if (type == "duration") - before << " UDA " << uda << ": " << padding << formatDuration (task, uda) << '\n'; + before << " UDA " << uda << ": " << padding << formatDuration(task, uda) << '\n'; } } // UDA orphans - auto orphans = task.getUDAOrphans (); - if (orphans.size ()) - { + auto orphans = task.getUDAOrphans(); + if (orphans.size()) { before << "# User Defined Attribute Orphans\n"; - std::sort (orphans.begin (), orphans.end ()); - for (auto& orphan : orphans) - { - int pad = 6 - orphan.length (); + std::sort(orphans.begin(), orphans.end()); + for (auto& orphan : orphans) { + int pad = 6 - orphan.length(); std::string padding = ""; - if (pad > 0) - padding = std::string (pad, ' '); + if (pad > 0) padding = std::string(pad, ' '); - before << " UDA Orphan " << orphan << ": " << padding << task.get (orphan) << '\n'; + before << " UDA Orphan " << orphan << ": " << padding << task.get(orphan) << '\n'; } } before << "# End\n"; - return before.str (); + return before.str(); } //////////////////////////////////////////////////////////////////////////////// -void CmdEdit::parseTask (Task& task, const std::string& after, const std::string& dateformat) -{ +void CmdEdit::parseTask(Task& task, const std::string& after, const std::string& dateformat) { // project - auto value = findValue (after, "\n Project:"); - if (task.get ("project") != value) - { - if (value != "") - { - Context::getContext ().footnote ("Project modified."); - task.set ("project", value); - } - else - { - Context::getContext ().footnote ("Project deleted."); - task.remove ("project"); + auto value = findValue(after, "\n Project:"); + if (task.get("project") != value) { + if (value != "") { + Context::getContext().footnote("Project modified."); + task.set("project", value); + } else { + Context::getContext().footnote("Project deleted."); + task.remove("project"); } } // tags - value = findValue (after, "\n Tags:"); - task.remove ("tags"); - task.setTags (split (value, ' ')); + value = findValue(after, "\n Tags:"); + task.remove("tags"); + task.setTags(split(value, ' ')); // description. - value = findMultilineValue (after, "\n Description:", "\n Created:"); - if (task.get ("description") != value) - { - if (value != "") - { - Context::getContext ().footnote ("Description modified."); - task.set ("description", value); - } - else - throw std::string ("Cannot remove description."); + value = findMultilineValue(after, "\n Description:", "\n Created:"); + if (task.get("description") != value) { + if (value != "") { + Context::getContext().footnote("Description modified."); + task.set("description", value); + } else + throw std::string("Cannot remove description."); } // entry - value = findValue (after, "\n Created:"); - if (value != "") - { - if (value != formatDate (task, "entry", dateformat)) - { - Context::getContext ().footnote ("Creation date modified."); - task.set ("entry", Datetime (value, dateformat).toEpochString ()); + value = findValue(after, "\n Created:"); + if (value != "") { + if (value != formatDate(task, "entry", dateformat)) { + Context::getContext().footnote("Creation date modified."); + task.set("entry", Datetime(value, dateformat).toEpochString()); } - } - else - throw std::string ("Cannot remove creation date."); + } else + throw std::string("Cannot remove creation date."); // start - value = findValue (after, "\n Started:"); - if (value != "") - { - if (task.get ("start") != "") - { - if (value != formatDate (task, "start", dateformat)) - { - Context::getContext ().footnote (STRING_EDIT_START_MOD); - task.set ("start", Datetime (value, dateformat).toEpochString ()); + value = findValue(after, "\n Started:"); + if (value != "") { + if (task.get("start") != "") { + if (value != formatDate(task, "start", dateformat)) { + Context::getContext().footnote(STRING_EDIT_START_MOD); + task.set("start", Datetime(value, dateformat).toEpochString()); } + } else { + Context::getContext().footnote(STRING_EDIT_START_MOD); + task.set("start", Datetime(value, dateformat).toEpochString()); } - else - { - Context::getContext ().footnote (STRING_EDIT_START_MOD); - task.set ("start", Datetime (value, dateformat).toEpochString ()); - } - } - else - { - if (task.get ("start") != "") - { - Context::getContext ().footnote ("Start date removed."); - task.remove ("start"); + } else { + if (task.get("start") != "") { + Context::getContext().footnote("Start date removed."); + task.remove("start"); } } // end - value = findValue (after, "\n Ended:"); - if (value != "") - { - if (task.get ("end") != "") - { - if (value != formatDate (task, "end", dateformat)) - { - Context::getContext ().footnote ("End date modified."); - task.set ("end", Datetime (value, dateformat).toEpochString ()); + value = findValue(after, "\n Ended:"); + if (value != "") { + if (task.get("end") != "") { + if (value != formatDate(task, "end", dateformat)) { + Context::getContext().footnote("End date modified."); + task.set("end", Datetime(value, dateformat).toEpochString()); } - } - else if (task.getStatus () != Task::deleted) - throw std::string ("Cannot set a done date on a pending task."); - } - else - { - if (task.get ("end") != "") - { - Context::getContext ().footnote ("End date removed."); - task.setStatus (Task::pending); - task.remove ("end"); + } else if (task.getStatus() != Task::deleted) + throw std::string("Cannot set a done date on a pending task."); + } else { + if (task.get("end") != "") { + Context::getContext().footnote("End date removed."); + task.setStatus(Task::pending); + task.remove("end"); } } // scheduled - value = findValue (after, "\n Scheduled:"); - if (value != "") - { - if (task.get ("scheduled") != "") - { - if (value != formatDate (task, "scheduled", dateformat)) - { - Context::getContext ().footnote (STRING_EDIT_SCHED_MOD); - task.set ("scheduled", Datetime (value, dateformat).toEpochString ()); + value = findValue(after, "\n Scheduled:"); + if (value != "") { + if (task.get("scheduled") != "") { + if (value != formatDate(task, "scheduled", dateformat)) { + Context::getContext().footnote(STRING_EDIT_SCHED_MOD); + task.set("scheduled", Datetime(value, dateformat).toEpochString()); } + } else { + Context::getContext().footnote(STRING_EDIT_SCHED_MOD); + task.set("scheduled", Datetime(value, dateformat).toEpochString()); } - else - { - Context::getContext ().footnote (STRING_EDIT_SCHED_MOD); - task.set ("scheduled", Datetime (value, dateformat).toEpochString ()); - } - } - else - { - if (task.get ("scheduled") != "") - { - Context::getContext ().footnote ("Scheduled date removed."); - task.remove ("scheduled"); + } else { + if (task.get("scheduled") != "") { + Context::getContext().footnote("Scheduled date removed."); + task.remove("scheduled"); } } // due - value = findValue (after, "\n Due:"); - if (value != "") - { - if (task.get ("due") != "") - { - if (value != formatDate (task, "due", dateformat)) - { - Context::getContext ().footnote (STRING_EDIT_DUE_MOD); - task.set ("due", Datetime (value, dateformat).toEpochString ()); + value = findValue(after, "\n Due:"); + if (value != "") { + if (task.get("due") != "") { + if (value != formatDate(task, "due", dateformat)) { + Context::getContext().footnote(STRING_EDIT_DUE_MOD); + task.set("due", Datetime(value, dateformat).toEpochString()); } + } else { + Context::getContext().footnote(STRING_EDIT_DUE_MOD); + task.set("due", Datetime(value, dateformat).toEpochString()); } - else - { - Context::getContext ().footnote (STRING_EDIT_DUE_MOD); - task.set ("due", Datetime (value, dateformat).toEpochString ()); - } - } - else - { - if (task.get ("due") != "") - { - if (task.getStatus () == Task::recurring || - task.get ("parent") != "") - { - Context::getContext ().footnote ("Cannot remove a due date from a recurring task."); - } - else - { - Context::getContext ().footnote ("Due date removed."); - task.remove ("due"); + } else { + if (task.get("due") != "") { + if (task.getStatus() == Task::recurring || task.get("parent") != "") { + Context::getContext().footnote("Cannot remove a due date from a recurring task."); + } else { + Context::getContext().footnote("Due date removed."); + task.remove("due"); } } } // until - value = findValue (after, "\n Until:"); - if (value != "") - { - if (task.get ("until") != "") - { - if (value != formatDate (task, "until", dateformat)) - { - Context::getContext ().footnote (STRING_EDIT_UNTIL_MOD); - task.set ("until", Datetime (value, dateformat).toEpochString ()); + value = findValue(after, "\n Until:"); + if (value != "") { + if (task.get("until") != "") { + if (value != formatDate(task, "until", dateformat)) { + Context::getContext().footnote(STRING_EDIT_UNTIL_MOD); + task.set("until", Datetime(value, dateformat).toEpochString()); } + } else { + Context::getContext().footnote(STRING_EDIT_UNTIL_MOD); + task.set("until", Datetime(value, dateformat).toEpochString()); } - else - { - Context::getContext ().footnote (STRING_EDIT_UNTIL_MOD); - task.set ("until", Datetime (value, dateformat).toEpochString ()); - } - } - else - { - if (task.get ("until") != "") - { - Context::getContext ().footnote ("Until date removed."); - task.remove ("until"); + } else { + if (task.get("until") != "") { + Context::getContext().footnote("Until date removed."); + task.remove("until"); } } // recur - value = findValue (after, "\n Recur:"); - if (value != task.get ("recur")) - { - if (value != "") - { + value = findValue(after, "\n Recur:"); + if (value != task.get("recur")) { + if (value != "") { Duration p; std::string::size_type idx = 0; - if (p.parse (value, idx)) - { - Context::getContext ().footnote ("Recurrence modified."); - if (task.get ("due") != "") - { - task.set ("recur", value); - task.setStatus (Task::recurring); - } - else - throw std::string ("A recurring task must have a due date."); - } - else - throw std::string ("Not a valid recurrence duration."); - } - else - { - Context::getContext ().footnote ("Recurrence removed."); - task.setStatus (Task::pending); - task.remove ("recur"); - task.remove ("until"); - task.remove ("mask"); - task.remove ("imask"); + if (p.parse(value, idx)) { + Context::getContext().footnote("Recurrence modified."); + if (task.get("due") != "") { + task.set("recur", value); + task.setStatus(Task::recurring); + } else + throw std::string("A recurring task must have a due date."); + } else + throw std::string("Not a valid recurrence duration."); + } else { + Context::getContext().footnote("Recurrence removed."); + task.setStatus(Task::pending); + task.remove("recur"); + task.remove("until"); + task.remove("mask"); + task.remove("imask"); } } // wait - value = findValue (after, "\n Wait until:"); - if (value != "") - { - if (task.get ("wait") != "") - { - if (value != formatDate (task, "wait", dateformat)) - { - Context::getContext ().footnote (STRING_EDIT_WAIT_MOD); - task.set ("wait", Datetime (value, dateformat).toEpochString ()); - task.setStatus (Task::waiting); + value = findValue(after, "\n Wait until:"); + if (value != "") { + if (task.get("wait") != "") { + if (value != formatDate(task, "wait", dateformat)) { + Context::getContext().footnote(STRING_EDIT_WAIT_MOD); + task.set("wait", Datetime(value, dateformat).toEpochString()); + task.setStatus(Task::waiting); } + } else { + Context::getContext().footnote(STRING_EDIT_WAIT_MOD); + task.set("wait", Datetime(value, dateformat).toEpochString()); + task.setStatus(Task::waiting); } - else - { - Context::getContext ().footnote (STRING_EDIT_WAIT_MOD); - task.set ("wait", Datetime (value, dateformat).toEpochString ()); - task.setStatus (Task::waiting); - } - } - else - { - if (task.get ("wait") != "") - { - Context::getContext ().footnote ("Wait date removed."); - task.remove ("wait"); - task.setStatus (Task::pending); + } else { + if (task.get("wait") != "") { + Context::getContext().footnote("Wait date removed."); + task.remove("wait"); + task.setStatus(Task::pending); } } // parent - value = findValue (after, "\n Parent:"); - if (value != task.get ("parent")) - { - if (value != "") - { - Context::getContext ().footnote ("Parent UUID modified."); - task.set ("parent", value); - } - else - { - Context::getContext ().footnote ("Parent UUID removed."); - task.remove ("parent"); + value = findValue(after, "\n Parent:"); + if (value != task.get("parent")) { + if (value != "") { + Context::getContext().footnote("Parent UUID modified."); + task.set("parent", value); + } else { + Context::getContext().footnote("Parent UUID removed."); + task.remove("parent"); } } // Annotations - std::map annotations; + std::map annotations; std::string::size_type found = 0; - while ((found = after.find ("\n Annotation:", found)) != std::string::npos) - { + while ((found = after.find("\n Annotation:", found)) != std::string::npos) { found += 14; // Length of "\n Annotation:". auto eol = found; - while ((eol = after.find ('\n', ++eol)) != std::string::npos) - if (after.substr (eol, ANNOTATION_EDIT_MARKER.length ()) != ANNOTATION_EDIT_MARKER) - break; + while ((eol = after.find('\n', ++eol)) != std::string::npos) + if (after.substr(eol, ANNOTATION_EDIT_MARKER.length()) != ANNOTATION_EDIT_MARKER) break; - if (eol != std::string::npos) - { - auto value = Lexer::trim (str_replace (after.substr (found, eol - found), ANNOTATION_EDIT_MARKER, "\n"), "\t "); - auto gap = value.find (" -- "); - if (gap != std::string::npos) - { + if (eol != std::string::npos) { + auto value = Lexer::trim( + str_replace(after.substr(found, eol - found), ANNOTATION_EDIT_MARKER, "\n"), "\t "); + auto gap = value.find(" -- "); + if (gap != std::string::npos) { // TODO keeping the initial dates even if dateformat approximates them // is complex as finding the correspondence between each original line // and edited line may be impossible (bug #705). It would be simpler if @@ -641,228 +521,191 @@ void CmdEdit::parseTask (Task& task, const std::string& after, const std::string // for each line: if the annotation is the same, then it is copied; if // the annotation is modified, then its original date may be kept; and // if there is no corresponding id, then a new unique date is created). - Datetime when (value.substr (0, gap), dateformat); + Datetime when(value.substr(0, gap), dateformat); // If the map already contains an annotation for a given timestamp // we need to increment until we find an unused key - int timestamp = (int) when.toEpoch (); + int timestamp = (int)when.toEpoch(); std::stringstream name; - do - { - name.str (""); // Clear + do { + name.str(""); // Clear name << "annotation_" << timestamp; timestamp++; - } - while (annotations.find (name.str ()) != annotations.end ()); + } while (annotations.find(name.str()) != annotations.end()); - auto text = Lexer::trim (value.substr (gap + 4), "\t "); - annotations.emplace (name.str (), text); + auto text = Lexer::trim(value.substr(gap + 4), "\t "); + annotations.emplace(name.str(), text); } } } - task.setAnnotations (annotations); + task.setAnnotations(annotations); // Dependencies - value = findValue (after, "\n Dependencies:"); - auto dependencies = split (value, ','); + value = findValue(after, "\n Dependencies:"); + auto dependencies = split(value, ','); - for (auto& dep : task.getDependencyUUIDs ()) - task.removeDependency (dep); - for (auto& dep : dependencies) - { - if (dep.length () >= 7) - task.addDependency (dep); + for (auto& dep : task.getDependencyUUIDs()) task.removeDependency(dep); + for (auto& dep : dependencies) { + if (dep.length() >= 7) + task.addDependency(dep); else - task.addDependency ((int) strtol (dep.c_str (), nullptr, 10)); + task.addDependency((int)strtol(dep.c_str(), nullptr, 10)); } // UDAs - for (auto& col : Context::getContext ().columns) - { - auto type = Context::getContext ().config.get ("uda." + col.first + ".type"); - if (type != "") - { - auto value = findValue (after, "\n UDA " + col.first + ":"); - if (type == "string") - value = json::decode (value); - if ((task.get (col.first) != value) && (type != "date" || - (task.get (col.first) != Datetime (value, dateformat).toEpochString ())) && - (type != "duration" || - (task.get (col.first) != Duration (value).toString ()))) - { - if (value != "") - { - Context::getContext ().footnote (format ("UDA {1} modified.", col.first)); + for (auto& col : Context::getContext().columns) { + auto type = Context::getContext().config.get("uda." + col.first + ".type"); + if (type != "") { + auto value = findValue(after, "\n UDA " + col.first + ":"); + if (type == "string") value = json::decode(value); + if ((task.get(col.first) != value) && + (type != "date" || + (task.get(col.first) != Datetime(value, dateformat).toEpochString())) && + (type != "duration" || (task.get(col.first) != Duration(value).toString()))) { + if (value != "") { + Context::getContext().footnote(format("UDA {1} modified.", col.first)); - if (type == "string") - { - task.set (col.first, value); - } - else if (type == "numeric") - { - Pig pig (value); + if (type == "string") { + task.set(col.first, value); + } else if (type == "numeric") { + Pig pig(value); double d; - if (pig.getNumber (d) && - pig.eos ()) - task.set (col.first, value); + if (pig.getNumber(d) && pig.eos()) + task.set(col.first, value); else - throw format ("The value '{1}' is not a valid numeric value.", value); + throw format("The value '{1}' is not a valid numeric value.", value); + } else if (type == "date") { + task.set(col.first, Datetime(value, dateformat).toEpochString()); + } else if (type == "duration") { + task.set(col.first, Duration(value).toTime_t()); } - else if (type == "date") - { - task.set (col.first, Datetime (value, dateformat).toEpochString ()); - } - else if (type == "duration") - { - task.set (col.first, Duration (value).toTime_t ()); - } - } - else - { - Context::getContext ().footnote (format ("UDA {1} deleted.", col.first)); - task.remove (col.first); + } else { + Context::getContext().footnote(format("UDA {1} deleted.", col.first)); + task.remove(col.first); } } } } // UDA orphans - for (auto& orphan : findValues (after, "\n UDA Orphan ")) - { - auto colon = orphan.find (':'); - if (colon != std::string::npos) - { - std::string name = Lexer::trim (orphan.substr (0, colon), "\t "); - std::string value = Lexer::trim (orphan.substr (colon + 1), "\t "); + for (auto& orphan : findValues(after, "\n UDA Orphan ")) { + auto colon = orphan.find(':'); + if (colon != std::string::npos) { + std::string name = Lexer::trim(orphan.substr(0, colon), "\t "); + std::string value = Lexer::trim(orphan.substr(colon + 1), "\t "); if (value != "") - task.set (name, value); + task.set(name, value); else - task.remove (name); + task.remove(name); } } } //////////////////////////////////////////////////////////////////////////////// -CmdEdit::editResult CmdEdit::editFile (Task& task) -{ +CmdEdit::editResult CmdEdit::editFile(Task& task) { // Check for file permissions. - Directory location (Context::getContext ().config.get ("data.location")); - if (! location.writable ()) - throw std::string ("Your data.location directory is not writable."); + Directory location(Context::getContext().config.get("data.location")); + if (!location.writable()) throw std::string("Your data.location directory is not writable."); // Create a temp file name in data.location. std::stringstream file; - file << "task." << task.get ("uuid").substr (0, 8) << ".task"; + file << "task." << task.get("uuid").substr(0, 8) << ".task"; // Determine the output date format, which uses a hierarchy of definitions. // rc.dateformat.edit // rc.dateformat - auto dateformat = Context::getContext ().config.get ("dateformat.edit"); - if (dateformat == "") - dateformat = Context::getContext ().config.get ("dateformat"); + auto dateformat = Context::getContext().config.get("dateformat.edit"); + if (dateformat == "") dateformat = Context::getContext().config.get("dateformat"); // Change directory for the editor - auto current_dir = Directory::cwd (); - int ignored = chdir (location._data.c_str ()); - ++ignored; // Keep compiler quiet. + auto current_dir = Directory::cwd(); + int ignored = chdir(location._data.c_str()); + ++ignored; // Keep compiler quiet. // Check if the file already exists, if so, bail out - Path filepath = Path (file.str ()); - if (filepath.exists ()) - throw std::string ("Task is already being edited."); + Path filepath = Path(file.str()); + if (filepath.exists()) throw std::string("Task is already being edited."); // Format the contents, T -> text, write to a file. - auto before = formatTask (task, dateformat); + auto before = formatTask(task, dateformat); auto before_orig = before; - File::write (file.str (), before); + File::write(file.str(), before); // Determine correct editor: .taskrc:editor > $VISUAL > $EDITOR > vi - auto editor = Context::getContext ().config.get ("editor"); - char* peditor = getenv ("VISUAL"); - if (editor == "" && peditor) editor = std::string (peditor); - peditor = getenv ("EDITOR"); - if (editor == "" && peditor) editor = std::string (peditor); + auto editor = Context::getContext().config.get("editor"); + char* peditor = getenv("VISUAL"); + if (editor == "" && peditor) editor = std::string(peditor); + peditor = getenv("EDITOR"); + if (editor == "" && peditor) editor = std::string(peditor); if (editor == "") editor = "vi"; // Complete the command line. editor += ' '; - editor += '"' + file.str () + '"'; + editor += '"' + file.str() + '"'; ARE_THESE_REALLY_HARMFUL: - bool changes = false; // No changes made. + bool changes = false; // No changes made. // Launch the editor. - std::cout << format ("Launching '{1}' now...\n", editor); - int exitcode = system (editor.c_str ()); + std::cout << format("Launching '{1}' now...\n", editor); + int exitcode = system(editor.c_str()); auto captured_errno = errno; if (0 == exitcode) std::cout << "Editing complete.\n"; - else - { - std::cout << format ("Editing failed with exit code {1}.\n", exitcode); - if (-1 == exitcode) - std::cout << std::strerror (captured_errno) << '\n'; - File::remove (file.str ()); + else { + std::cout << format("Editing failed with exit code {1}.\n", exitcode); + if (-1 == exitcode) std::cout << std::strerror(captured_errno) << '\n'; + File::remove(file.str()); return CmdEdit::editResult::error; } // Slurp file. std::string after; - File::read (file.str (), after); + File::read(file.str(), after); // Update task based on what can be parsed back out of the file, but only // if changes were made. - if (before_orig != after) - { + if (before_orig != after) { std::cout << "Edits were detected.\n"; std::string problem = ""; auto oops = false; - try - { - parseTask (task, after, dateformat); + try { + parseTask(task, after, dateformat); } - catch (const std::string& e) - { + catch (const std::string& e) { problem = e; oops = true; } - if (oops) - { + if (oops) { std::cerr << "Error: " << problem << '\n'; - File::remove (file.str()); + File::remove(file.str()); - if (confirm ("Taskwarrior couldn't handle your edits. Would you like to try again?")) - { + if (confirm("Taskwarrior couldn't handle your edits. Would you like to try again?")) { // Preserve the edits. before = after; - File::write (file.str (), before); + File::write(file.str(), before); goto ARE_THESE_REALLY_HARMFUL; } - } - else + } else changes = true; - } - else - { + } else { std::cout << "No edits were detected.\n"; changes = false; } // Cleanup. - File::remove (file.str ()); - ignored = chdir (current_dir.c_str ()); - return changes - ? CmdEdit::editResult::changes - : CmdEdit::editResult::nochanges; + File::remove(file.str()); + ignored = chdir(current_dir.c_str()); + return changes ? CmdEdit::editResult::changes : CmdEdit::editResult::nochanges; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdEdit.h b/src/commands/CmdEdit.h index 9d2272594..2d5fbd3d7 100644 --- a/src/commands/CmdEdit.h +++ b/src/commands/CmdEdit.h @@ -27,26 +27,26 @@ #ifndef INCLUDED_CMDEDIT #define INCLUDED_CMDEDIT -#include #include #include -class CmdEdit : public Command -{ -public: - CmdEdit (); - int execute (std::string&); +#include -private: - std::string findValue (const std::string&, const std::string&); - std::string findMultilineValue (const std::string&, const std::string&, const std::string&); - std::vector findValues (const std::string&, const std::string&); - std::string formatDate (Task&, const std::string&, const std::string&); - std::string formatDuration (Task&, const std::string&); - std::string formatTask (Task, const std::string&); - void parseTask (Task&, const std::string&, const std::string&); +class CmdEdit : public Command { + public: + CmdEdit(); + int execute(std::string&); + + private: + std::string findValue(const std::string&, const std::string&); + std::string findMultilineValue(const std::string&, const std::string&, const std::string&); + std::vector findValues(const std::string&, const std::string&); + std::string formatDate(Task&, const std::string&, const std::string&); + std::string formatDuration(Task&, const std::string&); + std::string formatTask(Task, const std::string&); + void parseTask(Task&, const std::string&, const std::string&); enum class editResult { error, changes, nochanges }; - editResult editFile (Task&); + editResult editFile(Task&); static const std::string ANNOTATION_EDIT_MARKER; }; diff --git a/src/commands/CmdExec.cpp b/src/commands/CmdExec.cpp index 3c200d6ba..3391f0447 100644 --- a/src/commands/CmdExec.cpp +++ b/src/commands/CmdExec.cpp @@ -28,38 +28,34 @@ // cmake.h include header must come first #include -#include #include #include +#include //////////////////////////////////////////////////////////////////////////////// -CmdExec::CmdExec () -{ - _keyword = "execute"; - _usage = "task execute "; - _description = "Executes external commands and scripts"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdExec::CmdExec() { + _keyword = "execute"; + _usage = "task execute "; + _description = "Executes external commands and scripts"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::misc; + _category = Command::Category::misc; } //////////////////////////////////////////////////////////////////////////////// -int CmdExec::execute (std::string&) -{ - std::string command = join (" ", Context::getContext ().cli2.getWords ()); +int CmdExec::execute(std::string&) { + std::string command = join(" ", Context::getContext().cli2.getWords()); - if (command.empty()) - { - Context::getContext ().error ("Cannot execute an empty command."); + if (command.empty()) { + Context::getContext().error("Cannot execute an empty command."); return 1; - } - else - return system (command.c_str ()); + } else + return system(command.c_str()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdExec.h b/src/commands/CmdExec.h index 82e0d16ec..4847ebff7 100644 --- a/src/commands/CmdExec.h +++ b/src/commands/CmdExec.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDEXEC #define INCLUDED_CMDEXEC -#include #include -class CmdExec : public Command -{ -public: - CmdExec (); - int execute (std::string&); +#include + +class CmdExec : public Command { + public: + CmdExec(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdExport.cpp b/src/commands/CmdExport.cpp index dcbd26206..a51f6428e 100644 --- a/src/commands/CmdExport.cpp +++ b/src/commands/CmdExport.cpp @@ -31,100 +31,86 @@ #include #include #include -#include -#include #include +#include //////////////////////////////////////////////////////////////////////////////// -CmdExport::CmdExport () -{ - _keyword = "export"; - _usage = "task export []"; - _description = "Exports tasks in JSON format"; - _read_only = true; - _displays_id = true; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdExport::CmdExport() { + _keyword = "export"; + _usage = "task export []"; + _description = "Exports tasks in JSON format"; + _read_only = true; + _displays_id = true; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::migration; + _category = Command::Category::migration; } //////////////////////////////////////////////////////////////////////////////// -int CmdExport::execute (std::string& output) -{ +int CmdExport::execute(std::string& output) { int rc = 0; - auto words = Context::getContext ().cli2.getWords (); + auto words = Context::getContext().cli2.getWords(); std::string selectedReport = ""; - if (words.size () == 1) - { + if (words.size() == 1) { // Find the report matching the prompt - for (auto& command : Context::getContext ().commands) - { - if (command.second->category () == Command::Category::report && - closeEnough(command.second->keyword (), words[0])) - { - selectedReport = command.second->keyword (); + for (auto& command : Context::getContext().commands) { + if (command.second->category() == Command::Category::report && + closeEnough(command.second->keyword(), words[0])) { + selectedReport = command.second->keyword(); break; } } - if (selectedReport.empty ()) { - throw format("Unable to find report that matches '{1}'.", words[0]); + if (selectedReport.empty()) { + throw format("Unable to find report that matches '{1}'.", words[0]); } } - auto reportSort = Context::getContext ().config.get ("report." + selectedReport + ".sort"); - auto reportFilter = Context::getContext ().config.get ("report." + selectedReport + ".filter"); + auto reportSort = Context::getContext().config.get("report." + selectedReport + ".sort"); + auto reportFilter = Context::getContext().config.get("report." + selectedReport + ".filter"); - auto sortOrder = split (reportSort, ','); - if (sortOrder.size () != 0 && - sortOrder[0] != "none") { - validateSortColumns (sortOrder); - } + auto sortOrder = split(reportSort, ','); + if (sortOrder.size() != 0 && sortOrder[0] != "none") { + validateSortColumns(sortOrder); + } // Add the report filter to any existing filter. - if (reportFilter != "") - Context::getContext ().cli2.addFilter (reportFilter); + if (reportFilter != "") Context::getContext().cli2.addFilter(reportFilter); // Make sure reccurent tasks are generated. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); - std::vector sequence; - if (sortOrder.size () && - sortOrder[0] == "none") - { + std::vector sequence; + if (sortOrder.size() && sortOrder[0] == "none") { // Assemble a sequence vector that represents the tasks listed in // Context::getContext ().cli2._uuid_ranges, in the order in which they appear. This // equates to no sorting, just a specified order. - sortOrder.clear (); - for (auto& i : Context::getContext ().cli2._uuid_list) - for (unsigned int t = 0; t < filtered.size (); ++t) - if (filtered[t].get ("uuid") == i) - sequence.push_back (t); - } - else - { + sortOrder.clear(); + for (auto& i : Context::getContext().cli2._uuid_list) + for (unsigned int t = 0; t < filtered.size(); ++t) + if (filtered[t].get("uuid") == i) sequence.push_back(t); + } else { // sort_tasks requires the order array initially be identity - for (unsigned int i = 0; i < filtered.size (); ++i) - sequence.push_back (i); + for (unsigned int i = 0; i < filtered.size(); ++i) sequence.push_back(i); // if no sort order, sort by id - if (!sortOrder.size ()) { + if (!sortOrder.size()) { reportSort = "id,uuid"; } // Sort the tasks. - sort_tasks (filtered, sequence, reportSort); + sort_tasks(filtered, sequence, reportSort); } // Export == render. @@ -133,48 +119,39 @@ int CmdExport::execute (std::string& output) // Obey 'limit:N'. int rows = 0; int lines = 0; - Context::getContext ().getLimits (rows, lines); + Context::getContext().getLimits(rows, lines); int limit = (rows > lines ? rows : lines); // Is output contained within a JSON array? - bool json_array = Context::getContext ().config.getBoolean ("json.array"); + bool json_array = Context::getContext().config.getBoolean("json.array"); // Compose output. - if (json_array) - output += "[\n"; + if (json_array) output += "[\n"; int counter = 0; - for (auto& t : sequence) - { + for (auto& t : sequence) { auto task = filtered[t]; - if (counter) - { - if (json_array) - output += ','; + if (counter) { + if (json_array) output += ','; output += '\n'; } - output += task.composeJSON (true); + output += task.composeJSON(true); ++counter; - if (limit && counter >= limit) - break; + if (limit && counter >= limit) break; } - if (filtered.size ()) - output += '\n'; + if (filtered.size()) output += '\n'; - if (json_array) - output += "]\n"; + if (json_array) output += "]\n"; - Context::getContext ().time_render_us += timer.total_us (); + Context::getContext().time_render_us += timer.total_us(); return rc; } //////////////////////////////////////////////////////////////////////////////// -void CmdExport::validateSortColumns (std::vector & columns) -{ - for (auto& col : columns) - legacySortColumnMap (col); +void CmdExport::validateSortColumns(std::vector& columns) { + for (auto& col : columns) legacySortColumnMap(col); } diff --git a/src/commands/CmdExport.h b/src/commands/CmdExport.h index 24eb252c3..db3faee90 100644 --- a/src/commands/CmdExport.h +++ b/src/commands/CmdExport.h @@ -27,16 +27,17 @@ #ifndef INCLUDED_CMDEXPORT #define INCLUDED_CMDEXPORT -#include #include -class CmdExport : public Command -{ -public: - CmdExport (); - int execute (std::string&); -private: - void validateSortColumns (std::vector &); +#include + +class CmdExport : public Command { + public: + CmdExport(); + int execute(std::string&); + + private: + void validateSortColumns(std::vector&); }; #endif diff --git a/src/commands/CmdGet.cpp b/src/commands/CmdGet.cpp index 0c2eb7173..c6114cc28 100644 --- a/src/commands/CmdGet.cpp +++ b/src/commands/CmdGet.cpp @@ -28,27 +28,26 @@ // cmake.h include header must come first #include -#include #include #include +#include +#include #include #include -#include //////////////////////////////////////////////////////////////////////////////// -CmdGet::CmdGet () -{ - _keyword = "_get"; - _usage = "task _get [ ...]"; - _description = "DOM Accessor"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdGet::CmdGet() { + _keyword = "_get"; + _usage = "task _get [ ...]"; + _description = "DOM Accessor"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// @@ -57,39 +56,32 @@ CmdGet::CmdGet () // // It is an error to specify no DOM references. // It is not an error for a DOM reference to resolve to a blank value. -int CmdGet::execute (std::string& output) -{ - std::vector results; - for (auto& arg : Context::getContext ().cli2._args) - { - switch (arg._lextype) - { - case Lexer::Type::dom: - { +int CmdGet::execute(std::string& output) { + std::vector results; + for (auto& arg : Context::getContext().cli2._args) { + switch (arg._lextype) { + case Lexer::Type::dom: { Variant result; - if (getDOM (arg.attribute ("raw"), NULL, result)) - results.emplace_back (result); + if (getDOM(arg.attribute("raw"), NULL, result)) + results.emplace_back(result); else - results.emplace_back (""); - } - break; + results.emplace_back(""); + } break; - // Look for non-refs to complain about. - case Lexer::Type::word: - case Lexer::Type::identifier: - if (! arg.hasTag ("BINARY") && - ! arg.hasTag ("CMD")) - throw format ("'{1}' is not a DOM reference.", arg.attribute ("raw")); + // Look for non-refs to complain about. + case Lexer::Type::word: + case Lexer::Type::identifier: + if (!arg.hasTag("BINARY") && !arg.hasTag("CMD")) + throw format("'{1}' is not a DOM reference.", arg.attribute("raw")); - default: - break; + default: + break; } } - if (results.size () == 0) - throw std::string ("No DOM reference specified."); + if (results.size() == 0) throw std::string("No DOM reference specified."); - output = join (" ", results); + output = join(" ", results); output += '\n'; return 0; } diff --git a/src/commands/CmdGet.h b/src/commands/CmdGet.h index 68fe81f84..18f651a0c 100644 --- a/src/commands/CmdGet.h +++ b/src/commands/CmdGet.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDGET #define INCLUDED_CMDGET -#include #include -class CmdGet : public Command -{ -public: - CmdGet (); - int execute (std::string&); +#include + +class CmdGet : public Command { + public: + CmdGet(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdHelp.cpp b/src/commands/CmdHelp.cpp index 72c2ea51a..033b12789 100644 --- a/src/commands/CmdHelp.cpp +++ b/src/commands/CmdHelp.cpp @@ -28,217 +28,205 @@ // cmake.h include header must come first #include -#include -#include #include -#include +#include #include +#include #include +#include + //////////////////////////////////////////////////////////////////////////////// -CmdHelp::CmdHelp () -{ - _keyword = "help"; - _usage = "task help ['usage']"; - _description = "Displays this usage help text"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdHelp::CmdHelp() { + _keyword = "help"; + _usage = "task help ['usage']"; + _description = "Displays this usage help text"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::misc; + _category = Command::Category::misc; } //////////////////////////////////////////////////////////////////////////////// -int CmdHelp::execute (std::string& output) -{ - auto words = Context::getContext ().cli2.getWords (); - if (words.size () == 1 && closeEnough ("usage", words[0])) - output = '\n' - + composeUsage () - + '\n'; +int CmdHelp::execute(std::string& output) { + auto words = Context::getContext().cli2.getWords(); + if (words.size() == 1 && closeEnough("usage", words[0])) + output = '\n' + composeUsage() + '\n'; else - output = '\n' - + composeUsage () - + '\n' - + "Documentation for Taskwarrior can be found using 'man task', 'man taskrc', 'man " - "task-color', 'man task-sync' or at https://taskwarrior.org\n" - "\n" - "The general form of commands is:\n" - " task [] []\n" - "\n" - "The consists of zero or more restrictions on which tasks to select, " - "such as:\n" - " task \n" - " task 28 \n" - " task +weekend \n" - " task project:Home due.before:today \n" - " task ebeeab00-ccf8-464b-8b58-f7f2d606edfb \n" - "\n" - "By default, filter elements are combined with an implicit 'and' operator, but " - "'or' and 'xor' may also be used, provided parentheses are included:\n" - " task '(/[Cc]at|[Dd]og/ or /[0-9]+/)' \n" - "\n" - "A filter may target specific tasks using ID or UUID numbers. To specify " - "multiple tasks use one of these forms:\n" - " task 1,2,3 delete\n" - " task 1-3 info\n" - " task 1,2-5,19 modify pri:H\n" - " task 4-7 ebeeab00-ccf8-464b-8b58-f7f2d606edfb info\n" - "\n" - "The consist of zero or more changes to apply to the selected tasks, " - "such as:\n" - " task project:Home\n" - " task +weekend +garden due:tomorrow\n" - " task Description/annotation text\n" - " task /from/to/ <- replace first match\n" - " task /from/to/g <- replace all matches\n" - "\n" - "Tags are arbitrary words, any quantity:\n" - " +tag The + means add the tag\n" - " -tag The - means remove the tag\n" - "\n" - "Built-in attributes are:\n" - " description: Task description text\n" - " status: Status of task - pending, completed, deleted, waiting\n" - " project: Project name\n" - " priority: Priority\n" - " due: Due date\n" - " recur: Recurrence frequency\n" - " until: Expiration date of a task\n" - " limit: Desired number of rows in report, or 'page'\n" - " wait: Date until task becomes pending\n" - " entry: Date task was created\n" - " end: Date task was completed/deleted\n" - " start: Date task was started\n" - " scheduled: Date task is scheduled to start\n" - " modified: Date task was last modified\n" - " depends: Other tasks that this task depends upon\n" - "\n" - "Attribute modifiers make filters more precise. Supported modifiers are:\n" - "\n" - " Modifiers Example Equivalent Meaning\n" - " ---------------- ----------------- ------------------- -------------------------\n" - " due:today due = today Fuzzy match\n" - " not due.not:today due != today Fuzzy non-match\n" - " before, below due.before:today due < today Exact date comparison\n" - " after, above due.after:today due >= tomorrow Exact date comparison\n" - " none project.none: project == '' Empty\n" - " any project.any: project !== '' Not empty\n" - " is, equals project.is:x project == x Exact match\n" - " isnt project.isnt:x project !== x Exact non-match\n" - " has, contains desc.has:Hello desc ~ Hello Pattern match\n" - " hasnt, desc.hasnt:Hello desc !~ Hello Pattern non-match\n" - " startswith, left desc.left:Hel desc ~ '^Hel' Beginning match\n" - " endswith, right desc.right:llo desc ~ 'llo$' End match\n" - " word desc.word:Hello desc ~ '\\bHello\\b' Boundaried word match\n" - " noword desc.noword:Hello desc !~ '\\bHello\\b' Boundaried word non-match\n" - "\n" - "Alternately algebraic expressions support:\n" - " and or xor Logical operators\n" - " < <= = != >= > Relational operators\n" - " ( ) Precedence\n" - "\n" - " task due.before:eom priority.not:L list\n" - " task '(due < eom and priority != L)' list\n" - "\n" - "The default .taskrc file can be overridden with:\n" - " task ... rc: ...\n" - " task ... rc:~/.alt_taskrc ...\n" - "\n" - "The values in .taskrc (or alternate) can be overridden with:\n" - " task ... rc.= ...\n" - " task rc.color=off list\n" - "\n" - "Any command or attribute name may be abbreviated if still unique:\n" - " task list project:Home\n" - " task li pro:Home\n" - "\n" - "Some task descriptions need to be escaped because of the shell:\n" - " task add \"quoted ' quote\"\n" - " task add escaped \\' quote\n" - "\n" - "The argument -- tells Taskwarrior to treat all other args as description, even " - "if they would otherwise be attributes or tags:\n" - " task add -- project:Home needs scheduling\n" - "\n" - "Many characters have special meaning to the shell, including:\n" - " $ ! ' \" ( ) ; \\ ` * ? { } [ ] < > | & % # ~\n" - "\n"; + output = + '\n' + composeUsage() + '\n' + + "Documentation for Taskwarrior can be found using 'man task', 'man taskrc', 'man " + "task-color', 'man task-sync' or at https://taskwarrior.org\n" + "\n" + "The general form of commands is:\n" + " task [] []\n" + "\n" + "The consists of zero or more restrictions on which tasks to select, " + "such as:\n" + " task \n" + " task 28 \n" + " task +weekend \n" + " task project:Home due.before:today \n" + " task ebeeab00-ccf8-464b-8b58-f7f2d606edfb \n" + "\n" + "By default, filter elements are combined with an implicit 'and' operator, but " + "'or' and 'xor' may also be used, provided parentheses are included:\n" + " task '(/[Cc]at|[Dd]og/ or /[0-9]+/)' \n" + "\n" + "A filter may target specific tasks using ID or UUID numbers. To specify " + "multiple tasks use one of these forms:\n" + " task 1,2,3 delete\n" + " task 1-3 info\n" + " task 1,2-5,19 modify pri:H\n" + " task 4-7 ebeeab00-ccf8-464b-8b58-f7f2d606edfb info\n" + "\n" + "The consist of zero or more changes to apply to the selected tasks, " + "such as:\n" + " task project:Home\n" + " task +weekend +garden due:tomorrow\n" + " task Description/annotation text\n" + " task /from/to/ <- replace first match\n" + " task /from/to/g <- replace all matches\n" + "\n" + "Tags are arbitrary words, any quantity:\n" + " +tag The + means add the tag\n" + " -tag The - means remove the tag\n" + "\n" + "Built-in attributes are:\n" + " description: Task description text\n" + " status: Status of task - pending, completed, deleted, waiting\n" + " project: Project name\n" + " priority: Priority\n" + " due: Due date\n" + " recur: Recurrence frequency\n" + " until: Expiration date of a task\n" + " limit: Desired number of rows in report, or 'page'\n" + " wait: Date until task becomes pending\n" + " entry: Date task was created\n" + " end: Date task was completed/deleted\n" + " start: Date task was started\n" + " scheduled: Date task is scheduled to start\n" + " modified: Date task was last modified\n" + " depends: Other tasks that this task depends upon\n" + "\n" + "Attribute modifiers make filters more precise. Supported modifiers are:\n" + "\n" + " Modifiers Example Equivalent Meaning\n" + " ---------------- ----------------- ------------------- -------------------------\n" + " due:today due = today Fuzzy match\n" + " not due.not:today due != today Fuzzy non-match\n" + " before, below due.before:today due < today Exact date comparison\n" + " after, above due.after:today due >= tomorrow Exact date comparison\n" + " none project.none: project == '' Empty\n" + " any project.any: project !== '' Not empty\n" + " is, equals project.is:x project == x Exact match\n" + " isnt project.isnt:x project !== x Exact non-match\n" + " has, contains desc.has:Hello desc ~ Hello Pattern match\n" + " hasnt, desc.hasnt:Hello desc !~ Hello Pattern non-match\n" + " startswith, left desc.left:Hel desc ~ '^Hel' Beginning match\n" + " endswith, right desc.right:llo desc ~ 'llo$' End match\n" + " word desc.word:Hello desc ~ '\\bHello\\b' Boundaried word match\n" + " noword desc.noword:Hello desc !~ '\\bHello\\b' Boundaried word non-match\n" + "\n" + "Alternately algebraic expressions support:\n" + " and or xor Logical operators\n" + " < <= = != >= > Relational operators\n" + " ( ) Precedence\n" + "\n" + " task due.before:eom priority.not:L list\n" + " task '(due < eom and priority != L)' list\n" + "\n" + "The default .taskrc file can be overridden with:\n" + " task ... rc: ...\n" + " task ... rc:~/.alt_taskrc ...\n" + "\n" + "The values in .taskrc (or alternate) can be overridden with:\n" + " task ... rc.= ...\n" + " task rc.color=off list\n" + "\n" + "Any command or attribute name may be abbreviated if still unique:\n" + " task list project:Home\n" + " task li pro:Home\n" + "\n" + "Some task descriptions need to be escaped because of the shell:\n" + " task add \"quoted ' quote\"\n" + " task add escaped \\' quote\n" + "\n" + "The argument -- tells Taskwarrior to treat all other args as description, even " + "if they would otherwise be attributes or tags:\n" + " task add -- project:Home needs scheduling\n" + "\n" + "Many characters have special meaning to the shell, including:\n" + " $ ! ' \" ( ) ; \\ ` * ? { } [ ] < > | & % # ~\n" + "\n"; - /* - TODO To be included later, before the 'precedence' line. + /* + TODO To be included later, before the 'precedence' line. - " + - Addition, Subtraktion\n" \ - " ! Negation\n" \ - " ~ !~ Treffer, kein Treffer\n" \ - */ + " + - Addition, Subtraktion\n" \ + " ! Negation\n" \ + " ~ !~ Treffer, kein Treffer\n" \ + */ return 0; } //////////////////////////////////////////////////////////////////////////////// -std::string CmdHelp::composeUsage () const -{ +std::string CmdHelp::composeUsage() const { Table view; - view.width (Context::getContext ().getWidth ()); - view.add (""); - view.add (""); - view.add (""); + view.width(Context::getContext().getWidth()); + view.add(""); + view.add(""); + view.add(""); // Static first row. - auto row = view.addRow (); - view.set (row, 0, "Usage:"); - view.set (row, 1, "task"); - view.set (row, 2, "Runs rc.default.command, if specified."); + auto row = view.addRow(); + view.set(row, 0, "Usage:"); + view.set(row, 1, "task"); + view.set(row, 2, "Runs rc.default.command, if specified."); // Obsolete method of getting a list of all commands. - std::vector all; - for (auto& cmd : Context::getContext ().commands) - all.push_back (cmd.first); + std::vector all; + for (auto& cmd : Context::getContext().commands) all.push_back(cmd.first); // Sort alphabetically by usage. - std::sort (all.begin (), all.end ()); + std::sort(all.begin(), all.end()); // Add the regular commands. - for (auto& name : all) - { - if (name[0] != '_') - { - row = view.addRow (); - view.set (row, 1, Context::getContext ().commands[name]->usage ()); - view.set (row, 2, Context::getContext ().commands[name]->description ()); + for (auto& name : all) { + if (name[0] != '_') { + row = view.addRow(); + view.set(row, 1, Context::getContext().commands[name]->usage()); + view.set(row, 2, Context::getContext().commands[name]->description()); } } // Add the helper commands. - for (auto& name : all) - { - if (name[0] == '_') - { - row = view.addRow (); - view.set (row, 1, Context::getContext ().commands[name]->usage ()); - view.set (row, 2, Context::getContext ().commands[name]->description ()); + for (auto& name : all) { + if (name[0] == '_') { + row = view.addRow(); + view.set(row, 1, Context::getContext().commands[name]->usage()); + view.set(row, 2, Context::getContext().commands[name]->description()); } } // Add the aliases commands. - row = view.addRow (); - view.set (row, 1, " "); + row = view.addRow(); + view.set(row, 1, " "); - for (auto& alias : Context::getContext ().config) - { - if (alias.first.substr (0, 6) == "alias.") - { - row = view.addRow (); - view.set (row, 1, alias.first.substr (6)); - view.set (row, 2, format ("Aliased to '{1}'", alias.second)); + for (auto& alias : Context::getContext().config) { + if (alias.first.substr(0, 6) == "alias.") { + row = view.addRow(); + view.set(row, 1, alias.first.substr(6)); + view.set(row, 2, format("Aliased to '{1}'", alias.second)); } } - return view.render (); + return view.render(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdHelp.h b/src/commands/CmdHelp.h index 7b793f6a6..af7e2a84a 100644 --- a/src/commands/CmdHelp.h +++ b/src/commands/CmdHelp.h @@ -27,17 +27,17 @@ #ifndef INCLUDED_CMDHELP #define INCLUDED_CMDHELP -#include #include -class CmdHelp : public Command -{ -public: - CmdHelp (); - int execute (std::string&); +#include -private: - std::string composeUsage () const; +class CmdHelp : public Command { + public: + CmdHelp(); + int execute(std::string&); + + private: + std::string composeUsage() const; }; #endif diff --git a/src/commands/CmdHistory.cpp b/src/commands/CmdHistory.cpp index 5f895b8e8..4449aea13 100644 --- a/src/commands/CmdHistory.cpp +++ b/src/commands/CmdHistory.cpp @@ -28,343 +28,299 @@ // cmake.h include header must come first #include -#include #include +#include #include #include -#include #include +#include #include -#include -#define STRING_CMD_HISTORY_YEAR "Year" -#define STRING_CMD_HISTORY_MONTH "Month" -#define STRING_CMD_HISTORY_DAY "Day" -#define STRING_CMD_HISTORY_ADDED "Added" -#define STRING_CMD_HISTORY_COMP "Completed" -#define STRING_CMD_HISTORY_DEL "Deleted" +#include + +#define STRING_CMD_HISTORY_YEAR "Year" +#define STRING_CMD_HISTORY_MONTH "Month" +#define STRING_CMD_HISTORY_DAY "Day" +#define STRING_CMD_HISTORY_ADDED "Added" +#define STRING_CMD_HISTORY_COMP "Completed" +#define STRING_CMD_HISTORY_DEL "Deleted" //////////////////////////////////////////////////////////////////////////////// -template -CmdHistoryBase::CmdHistoryBase () -{ - _keyword = HistoryStrategy::keyword; - _usage = HistoryStrategy::usage; - _description = HistoryStrategy::description; +template +CmdHistoryBase::CmdHistoryBase() { + _keyword = HistoryStrategy::keyword; + _usage = HistoryStrategy::usage; + _description = HistoryStrategy::description; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = true; - _accepts_filter = true; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::graphs; + _category = Command::Category::graphs; } //////////////////////////////////////////////////////////////////////////////// -template -void CmdHistoryBase::outputGraphical (std::string& output) -{ - auto widthOfBar = Context::getContext ().getWidth () - HistoryStrategy::labelWidth; +template +void CmdHistoryBase::outputGraphical(std::string& output) { + auto widthOfBar = Context::getContext().getWidth() - HistoryStrategy::labelWidth; // Now build the view. Table view; - setHeaderUnderline (view); - view.width (Context::getContext ().getWidth ()); + setHeaderUnderline(view); + view.width(Context::getContext().getWidth()); - HistoryStrategy::setupTableDates (view); + HistoryStrategy::setupTableDates(view); - view.add ("Number Added/Completed/Deleted", true, false); // Fixed. + view.add("Number Added/Completed/Deleted", true, false); // Fixed. - Color color_add (Context::getContext ().config.get ("color.history.add")); - Color color_done (Context::getContext ().config.get ("color.history.done")); - Color color_delete (Context::getContext ().config.get ("color.history.delete")); - Color label (Context::getContext ().config.get ("color.label")); + Color color_add(Context::getContext().config.get("color.history.add")); + Color color_done(Context::getContext().config.get("color.history.done")); + Color color_delete(Context::getContext().config.get("color.history.delete")); + Color label(Context::getContext().config.get("color.label")); // Determine the longest line, and the longest "added" line. auto maxAddedLine = 0; auto maxRemovedLine = 0; - for (auto& i : groups) - { + for (auto& i : groups) { if (completedGroup[i.first] + deletedGroup[i.first] > maxRemovedLine) maxRemovedLine = completedGroup[i.first] + deletedGroup[i.first]; - if (addedGroup[i.first] > maxAddedLine) - maxAddedLine = addedGroup[i.first]; + if (addedGroup[i.first] > maxAddedLine) maxAddedLine = addedGroup[i.first]; } auto maxLine = maxAddedLine + maxRemovedLine; - if (maxLine > 0) - { + if (maxLine > 0) { unsigned int leftOffset = (widthOfBar * maxAddedLine) / maxLine; time_t priorTime = 0; auto row = 0; - for (auto& i : groups) - { - row = view.addRow (); + for (auto& i : groups) { + row = view.addRow(); - HistoryStrategy::insertRowDate (view, row, i.first, priorTime); + HistoryStrategy::insertRowDate(view, row, i.first, priorTime); priorTime = i.first; - unsigned int addedBar = (widthOfBar * addedGroup[i.first]) / maxLine; + unsigned int addedBar = (widthOfBar * addedGroup[i.first]) / maxLine; unsigned int completedBar = (widthOfBar * completedGroup[i.first]) / maxLine; - unsigned int deletedBar = (widthOfBar * deletedGroup[i.first]) / maxLine; + unsigned int deletedBar = (widthOfBar * deletedGroup[i.first]) / maxLine; std::string bar; - if (Context::getContext ().color ()) - { + if (Context::getContext().color()) { std::string aBar; - if (addedGroup[i.first]) - { - aBar = format (addedGroup[i.first]); - while (aBar.length () < addedBar) - aBar = ' ' + aBar; + if (addedGroup[i.first]) { + aBar = format(addedGroup[i.first]); + while (aBar.length() < addedBar) aBar = ' ' + aBar; } std::string cBar; - if (completedGroup[i.first]) - { - cBar = format (completedGroup[i.first]); - while (cBar.length () < completedBar) - cBar = ' ' + cBar; + if (completedGroup[i.first]) { + cBar = format(completedGroup[i.first]); + while (cBar.length() < completedBar) cBar = ' ' + cBar; } std::string dBar; - if (deletedGroup[i.first]) - { - dBar = format (deletedGroup[i.first]); - while (dBar.length () < deletedBar) - dBar = ' ' + dBar; + if (deletedGroup[i.first]) { + dBar = format(deletedGroup[i.first]); + while (dBar.length() < deletedBar) dBar = ' ' + dBar; } - bar += std::string (leftOffset - aBar.length (), ' '); - bar += color_add.colorize (aBar); - bar += color_done.colorize (cBar); - bar += color_delete.colorize (dBar); - } - else - { - std::string aBar; while (aBar.length () < addedBar) aBar += '+'; - std::string cBar; while (cBar.length () < completedBar) cBar += 'X'; - std::string dBar; while (dBar.length () < deletedBar) dBar += '-'; + bar += std::string(leftOffset - aBar.length(), ' '); + bar += color_add.colorize(aBar); + bar += color_done.colorize(cBar); + bar += color_delete.colorize(dBar); + } else { + std::string aBar; + while (aBar.length() < addedBar) aBar += '+'; + std::string cBar; + while (cBar.length() < completedBar) cBar += 'X'; + std::string dBar; + while (dBar.length() < deletedBar) dBar += '-'; - bar += std::string (leftOffset - aBar.length (), ' '); + bar += std::string(leftOffset - aBar.length(), ' '); bar += aBar + cBar + dBar; } - view.set (row, HistoryStrategy::dateFieldCount + 0, bar); + view.set(row, HistoryStrategy::dateFieldCount + 0, bar); } } std::stringstream out; - if (view.rows ()) - { - out << optionalBlankLine () - << view.render () - << '\n'; + if (view.rows()) { + out << optionalBlankLine() << view.render() << '\n'; - if (Context::getContext ().color ()) - out << format ("Legend: {1}, {2}, {3}", - color_add.colorize (STRING_CMD_HISTORY_ADDED), - color_done.colorize (STRING_CMD_HISTORY_COMP), - color_delete.colorize (STRING_CMD_HISTORY_DEL)) - << optionalBlankLine () - << '\n'; + if (Context::getContext().color()) + out << format("Legend: {1}, {2}, {3}", color_add.colorize(STRING_CMD_HISTORY_ADDED), + color_done.colorize(STRING_CMD_HISTORY_COMP), + color_delete.colorize(STRING_CMD_HISTORY_DEL)) + << optionalBlankLine() << '\n'; else out << "Legend: + Added, X Completed, - Deleted\n"; - } - else - { - Context::getContext ().footnote ("No tasks."); + } else { + Context::getContext().footnote("No tasks."); rc = 1; } - output = out.str (); + output = out.str(); } //////////////////////////////////////////////////////////////////////////////// -template -void CmdHistoryBase::outputTabular (std::string& output) -{ +template +void CmdHistoryBase::outputTabular(std::string& output) { Table view; - setHeaderUnderline (view); - view.width (Context::getContext ().getWidth ()); + setHeaderUnderline(view); + view.width(Context::getContext().getWidth()); - HistoryStrategy::setupTableDates (view); + HistoryStrategy::setupTableDates(view); - view.add (STRING_CMD_HISTORY_ADDED, false); - view.add (STRING_CMD_HISTORY_COMP, false); - view.add (STRING_CMD_HISTORY_DEL, false); - view.add ("Net", false); + view.add(STRING_CMD_HISTORY_ADDED, false); + view.add(STRING_CMD_HISTORY_COMP, false); + view.add(STRING_CMD_HISTORY_DEL, false); + view.add("Net", false); - auto totalAdded = 0; + auto totalAdded = 0; auto totalCompleted = 0; - auto totalDeleted = 0; + auto totalDeleted = 0; auto row = 0; time_t lastTime = 0; - for (auto& i : groups) - { - row = view.addRow (); + for (auto& i : groups) { + row = view.addRow(); - totalAdded += addedGroup [i.first]; - totalCompleted += completedGroup [i.first]; - totalDeleted += deletedGroup [i.first]; + totalAdded += addedGroup[i.first]; + totalCompleted += completedGroup[i.first]; + totalDeleted += deletedGroup[i.first]; - HistoryStrategy::insertRowDate (view, row, i.first, lastTime); + HistoryStrategy::insertRowDate(view, row, i.first, lastTime); lastTime = i.first; auto net = 0; - if (addedGroup.find (i.first) != addedGroup.end ()) - { - view.set (row, HistoryStrategy::dateFieldCount + 0, addedGroup[i.first]); - net +=addedGroup[i.first]; + if (addedGroup.find(i.first) != addedGroup.end()) { + view.set(row, HistoryStrategy::dateFieldCount + 0, addedGroup[i.first]); + net += addedGroup[i.first]; } - if (completedGroup.find (i.first) != completedGroup.end ()) - { - view.set (row, HistoryStrategy::dateFieldCount + 1, completedGroup[i.first]); + if (completedGroup.find(i.first) != completedGroup.end()) { + view.set(row, HistoryStrategy::dateFieldCount + 1, completedGroup[i.first]); net -= completedGroup[i.first]; } - if (deletedGroup.find (i.first) != deletedGroup.end ()) - { - view.set (row, HistoryStrategy::dateFieldCount + 2, deletedGroup[i.first]); + if (deletedGroup.find(i.first) != deletedGroup.end()) { + view.set(row, HistoryStrategy::dateFieldCount + 2, deletedGroup[i.first]); net -= deletedGroup[i.first]; } Color net_color; - if (Context::getContext ().color () && net) - net_color = net > 0 - ? Color (Color::red) - : Color (Color::green); + if (Context::getContext().color() && net) + net_color = net > 0 ? Color(Color::red) : Color(Color::green); - view.set (row, HistoryStrategy::dateFieldCount + 3, net, net_color); + view.set(row, HistoryStrategy::dateFieldCount + 3, net, net_color); } - if (view.rows ()) - { + if (view.rows()) { + row = view.addRow(); + view.set(row, 1, " "); row = view.addRow(); - view.set (row, 1, " "); - row = view.addRow (); Color row_color; - if (Context::getContext ().color ()) - row_color = Color (Color::nocolor, Color::nocolor, false, true, false); + if (Context::getContext().color()) + row_color = Color(Color::nocolor, Color::nocolor, false, true, false); - view.set (row, HistoryStrategy::dateFieldCount - 1, "Average", row_color); - view.set (row, HistoryStrategy::dateFieldCount + 0, totalAdded / (view.rows () - 2), row_color); - view.set (row, HistoryStrategy::dateFieldCount + 1, totalCompleted / (view.rows () - 2), row_color); - view.set (row, HistoryStrategy::dateFieldCount + 2, totalDeleted / (view.rows () - 2), row_color); - view.set (row, HistoryStrategy::dateFieldCount + 3, (totalAdded - totalCompleted - totalDeleted) / (view.rows () - 2), row_color); + view.set(row, HistoryStrategy::dateFieldCount - 1, "Average", row_color); + view.set(row, HistoryStrategy::dateFieldCount + 0, totalAdded / (view.rows() - 2), row_color); + view.set(row, HistoryStrategy::dateFieldCount + 1, totalCompleted / (view.rows() - 2), + row_color); + view.set(row, HistoryStrategy::dateFieldCount + 2, totalDeleted / (view.rows() - 2), row_color); + view.set(row, HistoryStrategy::dateFieldCount + 3, + (totalAdded - totalCompleted - totalDeleted) / (view.rows() - 2), row_color); } std::stringstream out; - if (view.rows ()) - out << optionalBlankLine () - << view.render () - << '\n'; - else - { - Context::getContext ().footnote ("No tasks."); + if (view.rows()) + out << optionalBlankLine() << view.render() << '\n'; + else { + Context::getContext().footnote("No tasks."); rc = 1; } - output = out.str (); + output = out.str(); } ////////////////////////////////////////////////////////////////////////////i -class MonthlyHistoryStrategy -{ -public: - static Datetime getRelevantDate (const Datetime& dt) - { - return dt.startOfMonth (); +class MonthlyHistoryStrategy { + public: + static Datetime getRelevantDate(const Datetime& dt) { return dt.startOfMonth(); } + + static void setupTableDates(Table& view) { + view.add(STRING_CMD_HISTORY_YEAR, true); + view.add(STRING_CMD_HISTORY_MONTH, true); } - static void setupTableDates (Table& view) - { - view.add (STRING_CMD_HISTORY_YEAR, true); - view.add (STRING_CMD_HISTORY_MONTH, true); - } - - static void insertRowDate ( - Table& view, - int row, - time_t rowTime, - time_t lastTime) - { - Datetime dt (rowTime); + static void insertRowDate(Table& view, int row, time_t rowTime, time_t lastTime) { + Datetime dt(rowTime); int m, d, y; - dt.toYMD (y, m, d); + dt.toYMD(y, m, d); - Datetime last_dt (lastTime); + Datetime last_dt(lastTime); int last_m, last_d, last_y; - last_dt.toYMD (last_y, last_m, last_d); + last_dt.toYMD(last_y, last_m, last_d); - if (y != last_y) - view.set (row, 0, y); + if (y != last_y) view.set(row, 0, y); - view.set (row, 1, Datetime::monthName (m)); + view.set(row, 1, Datetime::monthName(m)); } - static constexpr const char* keyword = "history.monthly"; - static constexpr const char* usage = "task history.monthly"; - static constexpr const char* description = "Shows a report of task history, by month"; + static constexpr const char* keyword = "history.monthly"; + static constexpr const char* usage = "task history.monthly"; + static constexpr const char* description = "Shows a report of task history, by month"; static constexpr unsigned int dateFieldCount = 2; - static constexpr bool graphical = false; - static constexpr unsigned int labelWidth = 0; // unused. + static constexpr bool graphical = false; + static constexpr unsigned int labelWidth = 0; // unused. }; //////////////////////////////////////////////////////////////////////////////// -template -int CmdHistoryBase::execute (std::string& output) -{ +template +int CmdHistoryBase::execute(std::string& output) { rc = 0; // TODO is this necessary? - groups.clear (); - addedGroup.clear (); - deletedGroup.clear (); - completedGroup.clear (); + groups.clear(); + addedGroup.clear(); + deletedGroup.clear(); + completedGroup.clear(); // Apply filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); - for (auto& task : filtered) - { - Datetime entry (task.get_date ("entry")); + for (auto& task : filtered) { + Datetime entry(task.get_date("entry")); Datetime end; - if (task.has ("end")) - end = Datetime (task.get_date ("end")); + if (task.has("end")) end = Datetime(task.get_date("end")); - auto epoch = HistoryStrategy::getRelevantDate (entry).toEpoch (); + auto epoch = HistoryStrategy::getRelevantDate(entry).toEpoch(); groups[epoch] = 0; // Every task has an entry date, but exclude templates. - if (task.getStatus () != Task::recurring) - ++addedGroup[epoch]; + if (task.getStatus() != Task::recurring) ++addedGroup[epoch]; // All deleted tasks have an end date. - if (task.getStatus () == Task::deleted) - { - epoch = HistoryStrategy::getRelevantDate (end).toEpoch (); + if (task.getStatus() == Task::deleted) { + epoch = HistoryStrategy::getRelevantDate(end).toEpoch(); groups[epoch] = 0; ++deletedGroup[epoch]; } // All completed tasks have an end date. - else if (task.getStatus () == Task::completed) - { - epoch = HistoryStrategy::getRelevantDate (end).toEpoch (); + else if (task.getStatus() == Task::completed) { + epoch = HistoryStrategy::getRelevantDate(end).toEpoch(); groups[epoch] = 0; ++completedGroup[epoch]; } @@ -372,338 +328,251 @@ int CmdHistoryBase::execute (std::string& output) // Now build the view. if (HistoryStrategy::graphical) - this->outputGraphical (output); + this->outputGraphical(output); else - this->outputTabular (output); + this->outputTabular(output); return rc; } ////////////////////////////////////////////////////////////////////////////i -class MonthlyGHistoryStrategy -{ -public: - static Datetime getRelevantDate (const Datetime& dt) - { - return dt.startOfMonth (); +class MonthlyGHistoryStrategy { + public: + static Datetime getRelevantDate(const Datetime& dt) { return dt.startOfMonth(); } + + static void setupTableDates(Table& view) { + view.add(STRING_CMD_HISTORY_YEAR, true); + view.add(STRING_CMD_HISTORY_MONTH, true); } - static void setupTableDates (Table& view) - { - view.add (STRING_CMD_HISTORY_YEAR, true); - view.add (STRING_CMD_HISTORY_MONTH, true); - } - - static void insertRowDate ( - Table& view, - int row, - time_t rowTime, - time_t lastTime) - { - Datetime dt (rowTime); + static void insertRowDate(Table& view, int row, time_t rowTime, time_t lastTime) { + Datetime dt(rowTime); int m, d, y; - dt.toYMD (y, m, d); + dt.toYMD(y, m, d); - Datetime last_dt (lastTime); + Datetime last_dt(lastTime); int last_m, last_d, last_y; - last_dt.toYMD (last_y, last_m, last_d); + last_dt.toYMD(last_y, last_m, last_d); - if (y != last_y) - view.set (row, 0, y); + if (y != last_y) view.set(row, 0, y); - view.set (row, 1, Datetime::monthName (m)); + view.set(row, 1, Datetime::monthName(m)); } - static constexpr const char* keyword = "ghistory.monthly"; - static constexpr const char* usage = "task ghistory.monthly"; - static constexpr const char* description = "Shows a graphical report of task history, by month"; + static constexpr const char* keyword = "ghistory.monthly"; + static constexpr const char* usage = "task ghistory.monthly"; + static constexpr const char* description = "Shows a graphical report of task history, by month"; static constexpr unsigned int dateFieldCount = 2; - static constexpr bool graphical = true; - static constexpr unsigned int labelWidth = 15; // length '2017 September ' = 15 + static constexpr bool graphical = true; + static constexpr unsigned int labelWidth = 15; // length '2017 September ' = 15 }; ////////////////////////////////////////////////////////////////////////////i -class AnnualGHistoryStrategy -{ -public: - static Datetime getRelevantDate (const Datetime& dt) - { - return dt.startOfYear (); - } +class AnnualGHistoryStrategy { + public: + static Datetime getRelevantDate(const Datetime& dt) { return dt.startOfYear(); } - static void setupTableDates (Table& view) - { - view.add (STRING_CMD_HISTORY_YEAR, true); - } + static void setupTableDates(Table& view) { view.add(STRING_CMD_HISTORY_YEAR, true); } - static void insertRowDate ( - Table& view, - int row, - time_t rowTime, - time_t lastTime) - { - Datetime dt (rowTime); + static void insertRowDate(Table& view, int row, time_t rowTime, time_t lastTime) { + Datetime dt(rowTime); int m, d, y; - dt.toYMD (y, m, d); + dt.toYMD(y, m, d); - Datetime last_dt (lastTime); + Datetime last_dt(lastTime); int last_m, last_d, last_y; - last_dt.toYMD (last_y, last_m, last_d); + last_dt.toYMD(last_y, last_m, last_d); - if (y != last_y) - view.set (row, 0, y); + if (y != last_y) view.set(row, 0, y); } - static constexpr const char* keyword = "ghistory.annual"; - static constexpr const char* usage = "task ghistory.annual"; - static constexpr const char* description = "Shows a graphical report of task history, by year"; + static constexpr const char* keyword = "ghistory.annual"; + static constexpr const char* usage = "task ghistory.annual"; + static constexpr const char* description = "Shows a graphical report of task history, by year"; static constexpr unsigned int dateFieldCount = 1; - static constexpr bool graphical = true; - static constexpr unsigned int labelWidth = 5; // length '2017 ' = 5 + static constexpr bool graphical = true; + static constexpr unsigned int labelWidth = 5; // length '2017 ' = 5 }; ////////////////////////////////////////////////////////////////////////////i -class AnnualHistoryStrategy -{ -public: - static Datetime getRelevantDate (const Datetime& dt) - { - return dt.startOfYear (); - } +class AnnualHistoryStrategy { + public: + static Datetime getRelevantDate(const Datetime& dt) { return dt.startOfYear(); } - static void setupTableDates (Table& view) - { - view.add (STRING_CMD_HISTORY_YEAR, true); - } + static void setupTableDates(Table& view) { view.add(STRING_CMD_HISTORY_YEAR, true); } - static void insertRowDate ( - Table& view, - int row, - time_t rowTime, - time_t lastTime) - { - Datetime dt (rowTime); + static void insertRowDate(Table& view, int row, time_t rowTime, time_t lastTime) { + Datetime dt(rowTime); int m, d, y; - dt.toYMD (y, m, d); + dt.toYMD(y, m, d); - Datetime last_dt (lastTime); + Datetime last_dt(lastTime); int last_m, last_d, last_y; - last_dt.toYMD (last_y, last_m, last_d); + last_dt.toYMD(last_y, last_m, last_d); - if (y != last_y) - view.set (row, 0, y); + if (y != last_y) view.set(row, 0, y); } - static constexpr const char* keyword = "history.annual"; - static constexpr const char* usage = "task history.annual"; - static constexpr const char* description = "Shows a report of task history, by year"; + static constexpr const char* keyword = "history.annual"; + static constexpr const char* usage = "task history.annual"; + static constexpr const char* description = "Shows a report of task history, by year"; static constexpr unsigned int dateFieldCount = 1; - static constexpr bool graphical = false; - static constexpr unsigned int labelWidth = 0; // unused. + static constexpr bool graphical = false; + static constexpr unsigned int labelWidth = 0; // unused. }; - ////////////////////////////////////////////////////////////////////////////i -class DailyHistoryStrategy -{ -public: - static Datetime getRelevantDate (const Datetime& dt) - { - return dt.startOfDay (); +class DailyHistoryStrategy { + public: + static Datetime getRelevantDate(const Datetime& dt) { return dt.startOfDay(); } + + static void setupTableDates(Table& view) { + view.add(STRING_CMD_HISTORY_YEAR, true); + view.add(STRING_CMD_HISTORY_MONTH, true); + view.add(STRING_CMD_HISTORY_DAY, false); } - static void setupTableDates (Table& view) - { - view.add (STRING_CMD_HISTORY_YEAR, true); - view.add (STRING_CMD_HISTORY_MONTH, true); - view.add (STRING_CMD_HISTORY_DAY, false); - } - - static void insertRowDate ( - Table& view, - int row, - time_t rowTime, - time_t lastTime) - { - Datetime dt (rowTime); + static void insertRowDate(Table& view, int row, time_t rowTime, time_t lastTime) { + Datetime dt(rowTime); int m, d, y; - dt.toYMD (y, m, d); + dt.toYMD(y, m, d); - Datetime last_dt (lastTime); + Datetime last_dt(lastTime); int last_m, last_d, last_y; - last_dt.toYMD (last_y, last_m, last_d); + last_dt.toYMD(last_y, last_m, last_d); bool y_changed = (y != last_y) || (lastTime == 0); bool m_changed = (m != last_m) || (lastTime == 0); - if (y_changed) - view.set (row, 0, y); + if (y_changed) view.set(row, 0, y); - if (y_changed || m_changed) - view.set (row, 1, Datetime::monthName (m)); + if (y_changed || m_changed) view.set(row, 1, Datetime::monthName(m)); - view.set (row, 2, d); + view.set(row, 2, d); } - static constexpr const char* keyword = "history.daily"; - static constexpr const char* usage = "task history.daily"; - static constexpr const char* description = "Shows a report of task history, by day"; + static constexpr const char* keyword = "history.daily"; + static constexpr const char* usage = "task history.daily"; + static constexpr const char* description = "Shows a report of task history, by day"; static constexpr unsigned int dateFieldCount = 3; - static constexpr bool graphical = false; - static constexpr unsigned int labelWidth = 0; // unused. + static constexpr bool graphical = false; + static constexpr unsigned int labelWidth = 0; // unused. }; ////////////////////////////////////////////////////////////////////////////i -class DailyGHistoryStrategy -{ -public: - static Datetime getRelevantDate (const Datetime& dt) - { - return dt.startOfDay (); +class DailyGHistoryStrategy { + public: + static Datetime getRelevantDate(const Datetime& dt) { return dt.startOfDay(); } + + static void setupTableDates(Table& view) { + view.add(STRING_CMD_HISTORY_YEAR, true); + view.add(STRING_CMD_HISTORY_MONTH, true); + view.add(STRING_CMD_HISTORY_DAY, false); } - static void setupTableDates (Table& view) - { - view.add (STRING_CMD_HISTORY_YEAR, true); - view.add (STRING_CMD_HISTORY_MONTH, true); - view.add (STRING_CMD_HISTORY_DAY, false); - } - - static void insertRowDate ( - Table& view, - int row, - time_t rowTime, - time_t lastTime) - { - Datetime dt (rowTime); + static void insertRowDate(Table& view, int row, time_t rowTime, time_t lastTime) { + Datetime dt(rowTime); int m, d, y; - dt.toYMD (y, m, d); + dt.toYMD(y, m, d); - Datetime last_dt (lastTime); + Datetime last_dt(lastTime); int last_m, last_d, last_y; - last_dt.toYMD (last_y, last_m, last_d); + last_dt.toYMD(last_y, last_m, last_d); bool y_changed = (y != last_y) || (lastTime == 0); bool m_changed = (m != last_m) || (lastTime == 0); - if (y_changed) - view.set (row, 0, y); + if (y_changed) view.set(row, 0, y); - if (y_changed || m_changed) - view.set (row, 1, Datetime::monthName (m)); + if (y_changed || m_changed) view.set(row, 1, Datetime::monthName(m)); - view.set (row, 2, d); + view.set(row, 2, d); } - static constexpr const char* keyword = "ghistory.daily"; - static constexpr const char* usage = "task ghistory.daily"; - static constexpr const char* description = "Shows a graphical report of task history, by day"; + static constexpr const char* keyword = "ghistory.daily"; + static constexpr const char* usage = "task ghistory.daily"; + static constexpr const char* description = "Shows a graphical report of task history, by day"; static constexpr unsigned int dateFieldCount = 3; - static constexpr bool graphical = true; - static constexpr unsigned int labelWidth = 19; // length '2017 September Day ' = 19 + static constexpr bool graphical = true; + static constexpr unsigned int labelWidth = 19; // length '2017 September Day ' = 19 }; ////////////////////////////////////////////////////////////////////////////i -class WeeklyHistoryStrategy -{ -public: - static Datetime getRelevantDate (const Datetime& dt) - { - return dt.startOfWeek (); +class WeeklyHistoryStrategy { + public: + static Datetime getRelevantDate(const Datetime& dt) { return dt.startOfWeek(); } + + static void setupTableDates(Table& view) { + view.add(STRING_CMD_HISTORY_YEAR, true); + view.add(STRING_CMD_HISTORY_MONTH, true); + view.add(STRING_CMD_HISTORY_DAY, false); } - static void setupTableDates (Table& view) - { - view.add (STRING_CMD_HISTORY_YEAR, true); - view.add (STRING_CMD_HISTORY_MONTH, true); - view.add (STRING_CMD_HISTORY_DAY, false); - } - - static void insertRowDate ( - Table& view, - int row, - time_t rowTime, - time_t lastTime) - { - Datetime dt (rowTime); + static void insertRowDate(Table& view, int row, time_t rowTime, time_t lastTime) { + Datetime dt(rowTime); int m, d, y; - dt.toYMD (y, m, d); + dt.toYMD(y, m, d); - Datetime last_dt (lastTime); + Datetime last_dt(lastTime); int last_m, last_d, last_y; - last_dt.toYMD (last_y, last_m, last_d); + last_dt.toYMD(last_y, last_m, last_d); bool y_changed = (y != last_y) || (lastTime == 0); bool m_changed = (m != last_m) || (lastTime == 0); - if (y_changed) - view.set (row, 0, y); + if (y_changed) view.set(row, 0, y); - if (y_changed || m_changed) - view.set (row, 1, Datetime::monthName (m)); + if (y_changed || m_changed) view.set(row, 1, Datetime::monthName(m)); - view.set (row, 2, d); + view.set(row, 2, d); } - static constexpr const char* keyword = "history.weekly"; - static constexpr const char* usage = "task history.weekly"; - static constexpr const char* description = "Shows a report of task history, by week"; + static constexpr const char* keyword = "history.weekly"; + static constexpr const char* usage = "task history.weekly"; + static constexpr const char* description = "Shows a report of task history, by week"; static constexpr unsigned int dateFieldCount = 3; - static constexpr bool graphical = false; - static constexpr unsigned int labelWidth = 0; // unused. + static constexpr bool graphical = false; + static constexpr unsigned int labelWidth = 0; // unused. }; ////////////////////////////////////////////////////////////////////////////i -class WeeklyGHistoryStrategy -{ -public: - static Datetime getRelevantDate (const Datetime& dt) - { - return dt.startOfWeek (); +class WeeklyGHistoryStrategy { + public: + static Datetime getRelevantDate(const Datetime& dt) { return dt.startOfWeek(); } + + static void setupTableDates(Table& view) { + view.add(STRING_CMD_HISTORY_YEAR, true); + view.add(STRING_CMD_HISTORY_MONTH, true); + view.add(STRING_CMD_HISTORY_DAY, false); } - static void setupTableDates (Table& view) - { - view.add (STRING_CMD_HISTORY_YEAR, true); - view.add (STRING_CMD_HISTORY_MONTH, true); - view.add (STRING_CMD_HISTORY_DAY, false); - } - - static void insertRowDate ( - Table& view, - int row, - time_t rowTime, - time_t lastTime) - { - Datetime dt (rowTime); + static void insertRowDate(Table& view, int row, time_t rowTime, time_t lastTime) { + Datetime dt(rowTime); int m, d, y; - dt.toYMD (y, m, d); + dt.toYMD(y, m, d); - Datetime last_dt (lastTime); + Datetime last_dt(lastTime); int last_m, last_d, last_y; - last_dt.toYMD (last_y, last_m, last_d); + last_dt.toYMD(last_y, last_m, last_d); bool y_changed = (y != last_y) || (lastTime == 0); bool m_changed = (m != last_m) || (lastTime == 0); - if (y_changed) - view.set (row, 0, y); + if (y_changed) view.set(row, 0, y); - if (y_changed || m_changed) - view.set (row, 1, Datetime::monthName (m)); + if (y_changed || m_changed) view.set(row, 1, Datetime::monthName(m)); - view.set (row, 2, d); + view.set(row, 2, d); } - static constexpr const char* keyword = "ghistory.weekly"; - static constexpr const char* usage = "task ghistory.weekly"; - static constexpr const char* description = "Shows a graphical report of task history, by week"; + static constexpr const char* keyword = "ghistory.weekly"; + static constexpr const char* usage = "task ghistory.weekly"; + static constexpr const char* description = "Shows a graphical report of task history, by week"; static constexpr unsigned int dateFieldCount = 3; - static constexpr bool graphical = true; - static constexpr unsigned int labelWidth = 19; // length '2017 September Day ' = 19 + static constexpr bool graphical = true; + static constexpr unsigned int labelWidth = 19; // length '2017 September Day ' = 19 }; - // Explicit instantiations, avoiding cpp-inclusion or implementation in header template class CmdHistoryBase; template class CmdHistoryBase; diff --git a/src/commands/CmdHistory.h b/src/commands/CmdHistory.h index 311620cf7..f76683442 100644 --- a/src/commands/CmdHistory.h +++ b/src/commands/CmdHistory.h @@ -27,27 +27,27 @@ #ifndef INCLUDED_CMDHISTORY #define INCLUDED_CMDHISTORY -#include #include -#include #include +#include -template -class CmdHistoryBase : public Command -{ -public: - CmdHistoryBase (); - int execute (std::string&); +#include -private: - std::map groups; // Represents any timeinterval with data - std::map addedGroup; // Additions by timeinterval - std::map completedGroup; // Completions by timeinterval - std::map deletedGroup; // Deletions by timeinterval +template +class CmdHistoryBase : public Command { + public: + CmdHistoryBase(); + int execute(std::string&); + + private: + std::map groups; // Represents any timeinterval with data + std::map addedGroup; // Additions by timeinterval + std::map completedGroup; // Completions by timeinterval + std::map deletedGroup; // Deletions by timeinterval int rc; - void outputTabular (std::string&); - void outputGraphical (std::string&); + void outputTabular(std::string&); + void outputGraphical(std::string&); }; // Forward-declare strategies implemented in CmdHistory.cpp @@ -61,13 +61,13 @@ class AnnualHistoryStrategy; class AnnualGHistoryStrategy; // typedef the templates to nice names to be used outside this class -typedef CmdHistoryBase CmdHistoryDaily; -typedef CmdHistoryBase CmdGHistoryDaily; -typedef CmdHistoryBase CmdHistoryWeekly; -typedef CmdHistoryBase CmdGHistoryWeekly; -typedef CmdHistoryBase CmdHistoryMonthly; +typedef CmdHistoryBase CmdHistoryDaily; +typedef CmdHistoryBase CmdGHistoryDaily; +typedef CmdHistoryBase CmdHistoryWeekly; +typedef CmdHistoryBase CmdGHistoryWeekly; +typedef CmdHistoryBase CmdHistoryMonthly; typedef CmdHistoryBase CmdGHistoryMonthly; -typedef CmdHistoryBase CmdHistoryAnnual; -typedef CmdHistoryBase CmdGHistoryAnnual; +typedef CmdHistoryBase CmdHistoryAnnual; +typedef CmdHistoryBase CmdGHistoryAnnual; #endif diff --git a/src/commands/CmdIDs.cpp b/src/commands/CmdIDs.cpp index 3e52aa42b..36024163c 100644 --- a/src/commands/CmdIDs.cpp +++ b/src/commands/CmdIDs.cpp @@ -28,51 +28,49 @@ // cmake.h include header must come first #include -#include -#include #include #include #include #include +#include +#include + std::string zshColonReplacement = ","; //////////////////////////////////////////////////////////////////////////////// -CmdIDs::CmdIDs () -{ - _keyword = "ids"; - _usage = "task ids"; - _description = "Shows the IDs of matching tasks, as a range"; - _read_only = true; - _displays_id = true; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdIDs::CmdIDs() { + _keyword = "ids"; + _usage = "task ids"; + _description = "Shows the IDs of matching tasks, as a range"; + _read_only = true; + _displays_id = true; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::metadata; + _category = Command::Category::metadata; } //////////////////////////////////////////////////////////////////////////////// -int CmdIDs::execute (std::string& output) -{ +int CmdIDs::execute(std::string& output) { // Apply filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); // Find number of matching tasks. - std::vector ids; + std::vector ids; for (auto& task : filtered) - if (task.id) - ids.push_back (task.id); + if (task.id) ids.push_back(task.id); - std::sort (ids.begin (), ids.end ()); - output = compressIds (ids) + '\n'; + std::sort(ids.begin(), ids.end()); + output = compressIds(ids) + '\n'; - Context::getContext ().headers.clear (); + Context::getContext().headers.clear(); return 0; } @@ -88,35 +86,25 @@ int CmdIDs::execute (std::string& output) // // 1,3-4,6-9,11 // -std::string CmdIDs::compressIds (const std::vector & ids) -{ +std::string CmdIDs::compressIds(const std::vector& ids) { std::stringstream result; auto range_start = 0; auto range_end = 0; - for (unsigned int i = 0; i < ids.size (); ++i) - { - if (i + 1 == ids.size ()) - { - if (result.str ().length ()) - result << ' '; + for (unsigned int i = 0; i < ids.size(); ++i) { + if (i + 1 == ids.size()) { + if (result.str().length()) result << ' '; if (range_start < range_end) result << ids[range_start] << '-' << ids[range_end]; else result << ids[range_start]; - } - else - { - if (ids[range_end] + 1 == ids[i + 1]) - { + } else { + if (ids[range_end] + 1 == ids[i + 1]) { ++range_end; - } - else - { - if (result.str ().length ()) - result << ' '; + } else { + if (result.str().length()) result << ' '; if (range_start < range_end) result << ids[range_start] << '-' << ids[range_end]; @@ -128,201 +116,183 @@ std::string CmdIDs::compressIds (const std::vector & ids) } } - return result.str (); + return result.str(); } //////////////////////////////////////////////////////////////////////////////// -CmdCompletionIds::CmdCompletionIds () -{ - _keyword = "_ids"; - _usage = "task _ids"; - _description = "Shows the IDs of matching tasks, in the form of a list"; - _read_only = true; - _displays_id = true; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdCompletionIds::CmdCompletionIds() { + _keyword = "_ids"; + _usage = "task _ids"; + _description = "Shows the IDs of matching tasks, in the form of a list"; + _read_only = true; + _displays_id = true; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionIds::execute (std::string& output) -{ +int CmdCompletionIds::execute(std::string& output) { // Apply filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); - std::vector ids; + std::vector ids; for (auto& task : filtered) - if (task.getStatus () != Task::deleted && - task.getStatus () != Task::completed) - ids.push_back (task.id); + if (task.getStatus() != Task::deleted && task.getStatus() != Task::completed) + ids.push_back(task.id); - std::sort (ids.begin (), ids.end ()); - output = join ("\n", ids) + '\n'; + std::sort(ids.begin(), ids.end()); + output = join("\n", ids) + '\n'; - Context::getContext ().headers.clear (); + Context::getContext().headers.clear(); return 0; } //////////////////////////////////////////////////////////////////////////////// -CmdZshCompletionIds::CmdZshCompletionIds () -{ - _keyword = "_zshids"; - _usage = "task _zshids"; - _description = "Shows the IDs and descriptions of matching tasks"; - _read_only = true; - _displays_id = true; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdZshCompletionIds::CmdZshCompletionIds() { + _keyword = "_zshids"; + _usage = "task _zshids"; + _description = "Shows the IDs and descriptions of matching tasks"; + _read_only = true; + _displays_id = true; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdZshCompletionIds::execute (std::string& output) -{ +int CmdZshCompletionIds::execute(std::string& output) { // Apply filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); std::stringstream out; for (auto& task : filtered) - if (task.getStatus () != Task::deleted && - task.getStatus () != Task::completed) - out << task.id - << ':' - << str_replace(task.get ("description"), ":", zshColonReplacement) + if (task.getStatus() != Task::deleted && task.getStatus() != Task::completed) + out << task.id << ':' << str_replace(task.get("description"), ":", zshColonReplacement) << '\n'; - output = out.str (); + output = out.str(); - Context::getContext ().headers.clear (); + Context::getContext().headers.clear(); return 0; } //////////////////////////////////////////////////////////////////////////////// -CmdUUIDs::CmdUUIDs () -{ - _keyword = "uuids"; - _usage = "task uuids"; - _description = "Shows the UUIDs of matching tasks, as a space-separated list"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdUUIDs::CmdUUIDs() { + _keyword = "uuids"; + _usage = "task uuids"; + _description = "Shows the UUIDs of matching tasks, as a space-separated list"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::metadata; + _category = Command::Category::metadata; } //////////////////////////////////////////////////////////////////////////////// -int CmdUUIDs::execute (std::string& output) -{ +int CmdUUIDs::execute(std::string& output) { // Apply filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); - std::vector uuids; + std::vector uuids; uuids.reserve(filtered.size()); - for (auto& task : filtered) - uuids.push_back (task.get ("uuid")); + for (auto& task : filtered) uuids.push_back(task.get("uuid")); - std::sort (uuids.begin (), uuids.end ()); - output = join (" ", uuids) + '\n'; + std::sort(uuids.begin(), uuids.end()); + output = join(" ", uuids) + '\n'; - Context::getContext ().headers.clear (); + Context::getContext().headers.clear(); return 0; } //////////////////////////////////////////////////////////////////////////////// -CmdCompletionUuids::CmdCompletionUuids () -{ - _keyword = "_uuids"; - _usage = "task _uuids"; - _description = "Shows the UUIDs of matching tasks, as a list"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdCompletionUuids::CmdCompletionUuids() { + _keyword = "_uuids"; + _usage = "task _uuids"; + _description = "Shows the UUIDs of matching tasks, as a list"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionUuids::execute (std::string& output) -{ +int CmdCompletionUuids::execute(std::string& output) { // Apply filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); - std::vector uuids; + std::vector uuids; uuids.reserve(filtered.size()); - for (auto& task : filtered) - uuids.push_back (task.get ("uuid")); + for (auto& task : filtered) uuids.push_back(task.get("uuid")); - std::sort (uuids.begin (), uuids.end ()); - output = join ("\n", uuids) + '\n'; + std::sort(uuids.begin(), uuids.end()); + output = join("\n", uuids) + '\n'; - Context::getContext ().headers.clear (); + Context::getContext().headers.clear(); return 0; } //////////////////////////////////////////////////////////////////////////////// -CmdZshCompletionUuids::CmdZshCompletionUuids () -{ - _keyword = "_zshuuids"; - _usage = "task _zshuuids"; - _description = "Shows the UUIDs and descriptions of matching tasks"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdZshCompletionUuids::CmdZshCompletionUuids() { + _keyword = "_zshuuids"; + _usage = "task _zshuuids"; + _description = "Shows the UUIDs and descriptions of matching tasks"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdZshCompletionUuids::execute (std::string& output) -{ +int CmdZshCompletionUuids::execute(std::string& output) { // Apply filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); std::stringstream out; for (auto& task : filtered) - out << task.get ("uuid") - << ':' - << str_replace (task.get ("description"), ":", zshColonReplacement) + out << task.get("uuid") << ':' << str_replace(task.get("description"), ":", zshColonReplacement) << '\n'; - output = out.str (); + output = out.str(); - Context::getContext ().headers.clear (); + Context::getContext().headers.clear(); return 0; } diff --git a/src/commands/CmdIDs.h b/src/commands/CmdIDs.h index 6a345c2df..963b1e29e 100644 --- a/src/commands/CmdIDs.h +++ b/src/commands/CmdIDs.h @@ -27,52 +27,47 @@ #ifndef INCLUDED_CMDIDS #define INCLUDED_CMDIDS -#include #include -class CmdIDs : public Command -{ -public: - CmdIDs (); - int execute (std::string&); +#include -private: - std::string compressIds (const std::vector &); +class CmdIDs : public Command { + public: + CmdIDs(); + int execute(std::string&); + + private: + std::string compressIds(const std::vector&); }; -class CmdCompletionIds : public Command -{ -public: - CmdCompletionIds (); - int execute (std::string&); +class CmdCompletionIds : public Command { + public: + CmdCompletionIds(); + int execute(std::string&); }; -class CmdZshCompletionIds : public Command -{ -public: - CmdZshCompletionIds (); - int execute (std::string&); +class CmdZshCompletionIds : public Command { + public: + CmdZshCompletionIds(); + int execute(std::string&); }; -class CmdUUIDs : public Command -{ -public: - CmdUUIDs (); - int execute (std::string&); +class CmdUUIDs : public Command { + public: + CmdUUIDs(); + int execute(std::string&); }; -class CmdCompletionUuids : public Command -{ -public: - CmdCompletionUuids (); - int execute (std::string&); +class CmdCompletionUuids : public Command { + public: + CmdCompletionUuids(); + int execute(std::string&); }; -class CmdZshCompletionUuids : public Command -{ -public: - CmdZshCompletionUuids (); - int execute (std::string&); +class CmdZshCompletionUuids : public Command { + public: + CmdZshCompletionUuids(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdImport.cpp b/src/commands/CmdImport.cpp index 19cef20fc..8c7aa3486 100644 --- a/src/commands/CmdImport.cpp +++ b/src/commands/CmdImport.cpp @@ -29,118 +29,102 @@ #include #include -#include #include #include #include #include + +#include #include //////////////////////////////////////////////////////////////////////////////// -CmdImport::CmdImport () -{ - _keyword = "import"; - _usage = "task import [ ...]"; - _description = "Imports JSON files"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdImport::CmdImport() { + _keyword = "import"; + _usage = "task import [ ...]"; + _description = "Imports JSON files"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::migration; + _category = Command::Category::migration; } //////////////////////////////////////////////////////////////////////////////// -int CmdImport::execute (std::string&) -{ +int CmdImport::execute(std::string&) { auto rc = 0; auto count = 0; // Get filenames from command line arguments. - auto words = Context::getContext ().cli2.getWords (); - if (! words.size () || - (words.size () == 1 && words[0] == "-")) - { - std::cout << format ("Importing '{1}'\n", "STDIN"); + auto words = Context::getContext().cli2.getWords(); + if (!words.size() || (words.size() == 1 && words[0] == "-")) { + std::cout << format("Importing '{1}'\n", "STDIN"); std::string json; std::string line; - while (std::getline (std::cin, line)) - json += line + '\n'; + while (std::getline(std::cin, line)) json += line + '\n'; - if (nontrivial (json)) - count = import (json); - } - else - { + if (nontrivial(json)) count = import(json); + } else { // Import tasks from all specified files. - for (auto& word : words) - { - File incoming (word); - if (! incoming.exists ()) - throw format ("File '{1}' not found.", word); + for (auto& word : words) { + File incoming(word); + if (!incoming.exists()) throw format("File '{1}' not found.", word); - std::cout << format ("Importing '{1}'\n", word); + std::cout << format("Importing '{1}'\n", word); // Load the file. std::string json; - incoming.read (json); - if (nontrivial (json)) - count += import (json); + incoming.read(json); + if (nontrivial(json)) count += import(json); } } - Context::getContext ().footnote (format ("Imported {1} tasks.", count)); + Context::getContext().footnote(format("Imported {1} tasks.", count)); // Warn the user about multiple occurrences of the same UUID. auto duplicates = false; - for (const auto& [uuid, occurrences] : uuid_occurrences) - { - if (occurrences > 1) - { + for (const auto& [uuid, occurrences] : uuid_occurrences) { + if (occurrences > 1) { duplicates = true; - Context::getContext ().footnote (format ("Input contains UUID '{1}' {2} times.", uuid, occurrences)); + Context::getContext().footnote( + format("Input contains UUID '{1}' {2} times.", uuid, occurrences)); } } if (duplicates) - Context::getContext ().footnote ("Tasks with the same UUID have been merged. Please check the results."); + Context::getContext().footnote( + "Tasks with the same UUID have been merged. Please check the results."); return rc; } //////////////////////////////////////////////////////////////////////////////// -int CmdImport::import (const std::string& input) -{ +int CmdImport::import(const std::string& input) { auto count = 0; - try - { - json::value* root = json::parse (input); - if (root) - { + try { + json::value* root = json::parse(input); + if (root) { // Single object parse. Input looks like: // { ... } - if (root->type () == json::j_object) - { + if (root->type() == json::j_object) { // For each object element... auto root_obj = (json::object*)root; - importSingleTask (root_obj); + importSingleTask(root_obj); ++count; } // Multiple object array. Input looks like: // [ { ... } , { ... } ] - else if (root->type () == json::j_array) - { + else if (root->type() == json::j_array) { auto root_arr = (json::array*)root; // For each object element... - for (auto& element : root_arr->_data) - { + for (auto& element : root_arr->_data) { // For each object element... auto root_obj = (json::object*)element; - importSingleTask (root_obj); + importSingleTask(root_obj); ++count; } } @@ -157,8 +141,7 @@ int CmdImport::import (const std::string& input) // Input looks like: // { ... } // { ... } - catch (std::string& e) - { + catch (std::string& e) { // Make a very cursory check for the old-style format. if (input[0] != '{') { throw e; @@ -166,21 +149,19 @@ int CmdImport::import (const std::string& input) // Read the entire file, so that errors do not result in a partial import. std::vector> objects; - for (auto& line : split (input, '\n')) - { - if (line.length ()) - { - json::value* root = json::parse (line); - if (root && root->type () == json::j_object) - objects.push_back (std::unique_ptr ((json::object *)root)); + for (auto& line : split(input, '\n')) { + if (line.length()) { + json::value* root = json::parse(line); + if (root && root->type() == json::j_object) + objects.push_back(std::unique_ptr((json::object*)root)); else - throw format ("Invalid JSON: {1}", line); + throw format("Invalid JSON: {1}", line); } } // Import the tasks. for (auto& root : objects) { - importSingleTask (root.get()); + importSingleTask(root.get()); ++count; } } @@ -189,24 +170,22 @@ int CmdImport::import (const std::string& input) } //////////////////////////////////////////////////////////////////////////////// -void CmdImport::importSingleTask (json::object* obj) -{ +void CmdImport::importSingleTask(json::object* obj) { // Parse the whole thing, validate the data. - Task task (obj); + Task task(obj); - auto hasGeneratedEntry = not task.has ("entry"); - auto hasExplicitEnd = task.has ("end"); + auto hasGeneratedEntry = not task.has("entry"); + auto hasExplicitEnd = task.has("end"); - task.validate (); + task.validate(); - auto hasGeneratedEnd = not hasExplicitEnd and task.has ("end"); + auto hasGeneratedEnd = not hasExplicitEnd and task.has("end"); // Check whether the imported task is new or a modified existing task. Task before; auto uuid = task.get("uuid"); uuid_occurrences[uuid]++; - if (Context::getContext ().tdb2.get (uuid, before)) - { + if (Context::getContext().tdb2.get(uuid, before)) { // We need to neglect updates from attributes with dynamic defaults // unless they have been explicitly specified on import. // @@ -218,38 +197,28 @@ void CmdImport::importSingleTask (json::object* obj) // The 'modified' attribute is ignored in any case, since if it // were the only difference between the tasks, it would have been // neglected anyway, since it is bumped on each modification. - task.set ("modified", before.get ("modified")); + task.set("modified", before.get("modified")); // Other generated values are replaced by values from existing task, // so that they are ignored on comparison. - if (hasGeneratedEntry) - task.set ("entry", before.get ("entry")); + if (hasGeneratedEntry) task.set("entry", before.get("entry")); - if (hasGeneratedEnd) - task.set ("end", before.get ("end")); + if (hasGeneratedEnd) task.set("end", before.get("end")); - if (before != task) - { + if (before != task) { CmdModify modHelper; - modHelper.checkConsistency (before, task); - modHelper.modifyAndUpdate (before, task); + modHelper.checkConsistency(before, task); + modHelper.modifyAndUpdate(before, task); std::cout << " mod "; - } - else - { + } else { std::cout << " skip "; } - } - else - { - Context::getContext ().tdb2.add (task); + } else { + Context::getContext().tdb2.add(task); std::cout << " add "; } - std::cout << task.get ("uuid") - << ' ' - << task.get ("description") - << '\n'; + std::cout << task.get("uuid") << ' ' << task.get("description") << '\n'; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdImport.h b/src/commands/CmdImport.h index b27d94362..e46f9a0d5 100644 --- a/src/commands/CmdImport.h +++ b/src/commands/CmdImport.h @@ -27,20 +27,20 @@ #ifndef INCLUDED_CMDIMPORT #define INCLUDED_CMDIMPORT -#include #include #include -class CmdImport : public Command -{ -public: - CmdImport (); - int execute (std::string&); +#include -private: +class CmdImport : public Command { + public: + CmdImport(); + int execute(std::string&); + + private: std::unordered_map uuid_occurrences; - int import (const std::string&); - void importSingleTask (json::object*); + int import(const std::string&); + void importSingleTask(json::object*); }; #endif diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp index 3a2771a6d..e6d52d0a6 100644 --- a/src/commands/CmdInfo.cpp +++ b/src/commands/CmdInfo.cpp @@ -28,294 +28,263 @@ // cmake.h include header must come first #include -#include -#include -#include #include -#include #include #include -#include -#include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -CmdInfo::CmdInfo () -{ - _keyword = "information"; - _usage = "task information"; - _description = "Shows all data and metadata"; - _read_only = true; +CmdInfo::CmdInfo() { + _keyword = "information"; + _usage = "task information"; + _description = "Shows all data and metadata"; + _read_only = true; // This is inaccurate, but it does prevent a GC. While this doesn't make a // lot of sense, given that the info command shows the ID, it does mimic the // behavior of versions prior to 2.0, which the test suite relies upon. // // Once the test suite is completely modified, this can be corrected. - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::metadata; + _category = Command::Category::metadata; } //////////////////////////////////////////////////////////////////////////////// -int CmdInfo::execute (std::string& output) -{ +int CmdInfo::execute(std::string& output) { auto rc = 0; // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); - if (! filtered.size ()) - { - Context::getContext ().footnote ("No matches."); + if (!filtered.size()) { + Context::getContext().footnote("No matches."); rc = 1; } // Determine the output date format, which uses a hierarchy of definitions. // rc.dateformat.info // rc.dateformat - auto dateformat = Context::getContext ().config.get ("dateformat.info"); - if (dateformat == "") - dateformat = Context::getContext ().config.get ("dateformat"); + auto dateformat = Context::getContext().config.get("dateformat.info"); + if (dateformat == "") dateformat = Context::getContext().config.get("dateformat"); - auto dateformatanno = Context::getContext ().config.get ("dateformat.annotation"); - if (dateformatanno == "") - dateformatanno = dateformat; + auto dateformatanno = Context::getContext().config.get("dateformat.annotation"); + if (dateformatanno == "") dateformatanno = dateformat; // Render each task. std::stringstream out; - for (auto& task : filtered) - { + for (auto& task : filtered) { Table view; - view.width (Context::getContext ().getWidth ()); - if (Context::getContext ().config.getBoolean ("obfuscate")) - view.obfuscate (); - if (Context::getContext ().color ()) - view.forceColor (); - view.add ("Name"); - view.add ("Value"); - setHeaderUnderline (view); + view.width(Context::getContext().getWidth()); + if (Context::getContext().config.getBoolean("obfuscate")) view.obfuscate(); + if (Context::getContext().color()) view.forceColor(); + view.add("Name"); + view.add("Value"); + setHeaderUnderline(view); Datetime now; // id - auto row = view.addRow (); - view.set (row, 0, "ID"); - view.set (row, 1, (task.id ? format (task.id) : "-")); + auto row = view.addRow(); + view.set(row, 0, "ID"); + view.set(row, 1, (task.id ? format(task.id) : "-")); - std::string status = Lexer::ucFirst (Task::statusToText (task.getStatus ())); + std::string status = Lexer::ucFirst(Task::statusToText(task.getStatus())); // description Color c; - autoColorize (task, c); - auto description = task.get ("description"); - auto indent = Context::getContext ().config.getInteger ("indent.annotation"); + autoColorize(task, c); + auto description = task.get("description"); + auto indent = Context::getContext().config.getInteger("indent.annotation"); - for (auto& anno : task.getAnnotations ()) - description += '\n' - + std::string (indent, ' ') - + Datetime (anno.first.substr (11)).toString (dateformatanno) - + ' ' - + anno.second; + for (auto& anno : task.getAnnotations()) + description += '\n' + std::string(indent, ' ') + + Datetime(anno.first.substr(11)).toString(dateformatanno) + ' ' + anno.second; - row = view.addRow (); - view.set (row, 0, "Description"); - view.set (row, 1, description, c); + row = view.addRow(); + view.set(row, 0, "Description"); + view.set(row, 1, description, c); // status - row = view.addRow (); - view.set (row, 0, "Status"); - view.set (row, 1, status); + row = view.addRow(); + view.set(row, 0, "Status"); + view.set(row, 1, status); // project - if (task.has ("project")) - { - row = view.addRow (); - view.set (row, 0, "Project"); - view.set (row, 1, task.get ("project")); + if (task.has("project")) { + row = view.addRow(); + view.set(row, 0, "Project"); + view.set(row, 1, task.get("project")); } // dependencies: blocked { - auto blocked = task.getDependencyTasks (); - if (blocked.size ()) - { + auto blocked = task.getDependencyTasks(); + if (blocked.size()) { std::stringstream message; - for (auto& block : blocked) - message << block.id << ' ' << block.get ("description") << '\n'; + for (auto& block : blocked) message << block.id << ' ' << block.get("description") << '\n'; - row = view.addRow (); - view.set (row, 0, "This task blocked by"); - view.set (row, 1, message.str ()); + row = view.addRow(); + view.set(row, 0, "This task blocked by"); + view.set(row, 1, message.str()); } } // dependencies: blocking { - auto blocking = task.getBlockedTasks (); - if (blocking.size ()) - { + auto blocking = task.getBlockedTasks(); + if (blocking.size()) { std::stringstream message; - for (auto& block : blocking) - message << block.id << ' ' << block.get ("description") << '\n'; + for (auto& block : blocking) message << block.id << ' ' << block.get("description") << '\n'; - row = view.addRow (); - view.set (row, 0, "This task is blocking"); - view.set (row, 1, message.str ()); + row = view.addRow(); + view.set(row, 0, "This task is blocking"); + view.set(row, 1, message.str()); } } // recur - if (task.has ("recur")) - { - row = view.addRow (); - view.set (row, 0, "Recurrence"); - view.set (row, 1, task.get ("recur")); + if (task.has("recur")) { + row = view.addRow(); + view.set(row, 0, "Recurrence"); + view.set(row, 1, task.get("recur")); } // parent // 2017-01-07: Deprecated in 2.6.0 - if (task.has ("parent")) - { - row = view.addRow (); - view.set (row, 0, "Parent task"); - view.set (row, 1, task.get ("parent")); + if (task.has("parent")) { + row = view.addRow(); + view.set(row, 0, "Parent task"); + view.set(row, 1, task.get("parent")); } // mask // 2017-01-07: Deprecated in 2.6.0 - if (task.has ("mask")) - { - row = view.addRow (); - view.set (row, 0, "Mask"); - view.set (row, 1, task.get ("mask")); + if (task.has("mask")) { + row = view.addRow(); + view.set(row, 0, "Mask"); + view.set(row, 1, task.get("mask")); } // imask // 2017-01-07: Deprecated in 2.6.0 - if (task.has ("imask")) - { - row = view.addRow (); - view.set (row, 0, "Mask Index"); - view.set (row, 1, task.get ("imask")); + if (task.has("imask")) { + row = view.addRow(); + view.set(row, 0, "Mask Index"); + view.set(row, 1, task.get("imask")); } // template - if (task.has ("template")) - { - row = view.addRow (); - view.set (row, 0, "Template task"); - view.set (row, 1, task.get ("template")); + if (task.has("template")) { + row = view.addRow(); + view.set(row, 0, "Template task"); + view.set(row, 1, task.get("template")); } // last - if (task.has ("last")) - { - row = view.addRow (); - view.set (row, 0, "Last instance"); - view.set (row, 1, task.get ("last")); + if (task.has("last")) { + row = view.addRow(); + view.set(row, 0, "Last instance"); + view.set(row, 1, task.get("last")); } // rtype - if (task.has ("rtype")) - { - row = view.addRow (); - view.set (row, 0, "Recurrence type"); - view.set (row, 1, task.get ("rtype")); + if (task.has("rtype")) { + row = view.addRow(); + view.set(row, 0, "Recurrence type"); + view.set(row, 1, task.get("rtype")); } // entry - row = view.addRow (); - view.set (row, 0, "Entered"); - Datetime dt (task.get_date ("entry")); - std::string entry = dt.toString (dateformat); + row = view.addRow(); + view.set(row, 0, "Entered"); + Datetime dt(task.get_date("entry")); + std::string entry = dt.toString(dateformat); std::string age; - auto created = task.get ("entry"); - if (created.length ()) - { - Datetime dt (strtoll (created.c_str (), nullptr, 10)); - age = Duration (now - dt).formatVague (); + auto created = task.get("entry"); + if (created.length()) { + Datetime dt(strtoll(created.c_str(), nullptr, 10)); + age = Duration(now - dt).formatVague(); } - view.set (row, 1, entry + " (" + age + ')'); + view.set(row, 1, entry + " (" + age + ')'); // wait - if (task.has ("wait")) - { - row = view.addRow (); - view.set (row, 0, "Waiting until"); - view.set (row, 1, Datetime (task.get_date ("wait")).toString (dateformat)); + if (task.has("wait")) { + row = view.addRow(); + view.set(row, 0, "Waiting until"); + view.set(row, 1, Datetime(task.get_date("wait")).toString(dateformat)); } // scheduled - if (task.has ("scheduled")) - { - row = view.addRow (); - view.set (row, 0, "Scheduled"); - view.set (row, 1, Datetime (task.get_date ("scheduled")).toString (dateformat)); + if (task.has("scheduled")) { + row = view.addRow(); + view.set(row, 0, "Scheduled"); + view.set(row, 1, Datetime(task.get_date("scheduled")).toString(dateformat)); } // start - if (task.has ("start")) - { - row = view.addRow (); - view.set (row, 0, "Start"); - view.set (row, 1, Datetime (task.get_date ("start")).toString (dateformat)); + if (task.has("start")) { + row = view.addRow(); + view.set(row, 0, "Start"); + view.set(row, 1, Datetime(task.get_date("start")).toString(dateformat)); } // due (colored) - if (task.has ("due")) - { - row = view.addRow (); - view.set (row, 0, "Due"); - view.set (row, 1, Datetime (task.get_date ("due")).toString (dateformat)); + if (task.has("due")) { + row = view.addRow(); + view.set(row, 0, "Due"); + view.set(row, 1, Datetime(task.get_date("due")).toString(dateformat)); } // end - if (task.has ("end")) - { - row = view.addRow (); - view.set (row, 0, "End"); - view.set (row, 1, Datetime (task.get_date ("end")).toString (dateformat)); + if (task.has("end")) { + row = view.addRow(); + view.set(row, 0, "End"); + view.set(row, 1, Datetime(task.get_date("end")).toString(dateformat)); } // until - if (task.has ("until")) - { - row = view.addRow (); - view.set (row, 0, "Until"); - view.set (row, 1, Datetime (task.get_date ("until")).toString (dateformat)); + if (task.has("until")) { + row = view.addRow(); + view.set(row, 0, "Until"); + view.set(row, 1, Datetime(task.get_date("until")).toString(dateformat)); } // modified - if (task.has ("modified")) - { - row = view.addRow (); - view.set (row, 0, "Last modified"); + if (task.has("modified")) { + row = view.addRow(); + view.set(row, 0, "Last modified"); - Datetime mod (task.get_date ("modified")); - std::string age = Duration (now - mod).formatVague (); - view.set (row, 1, mod.toString (dateformat) + " (" + age + ')'); + Datetime mod(task.get_date("modified")); + std::string age = Duration(now - mod).formatVague(); + view.set(row, 1, mod.toString(dateformat) + " (" + age + ')'); } // tags ... - auto tags = task.getTags (); - if (tags.size ()) - { - auto allTags = join (" ", tags); + auto tags = task.getTags(); + if (tags.size()) { + auto allTags = join(" ", tags); - row = view.addRow (); - view.set (row, 0, "Tags"); - view.set (row, 1, allTags); + row = view.addRow(); + view.set(row, 0, "Tags"); + view.set(row, 1, allTags); } // Virtual tags. @@ -323,84 +292,79 @@ int CmdInfo::execute (std::string& output) // Note: This list must match that in Task::hasTag. // Note: This list must match that in ::feedback_reserved_tags. std::string virtualTags = ""; - if (task.hasTag ("ACTIVE")) virtualTags += "ACTIVE "; - if (task.hasTag ("ANNOTATED")) virtualTags += "ANNOTATED "; - if (task.hasTag ("BLOCKED")) virtualTags += "BLOCKED "; - if (task.hasTag ("BLOCKING")) virtualTags += "BLOCKING "; - if (task.hasTag ("CHILD")) virtualTags += "CHILD "; // 2017-01-07: Deprecated in 2.6.0 - if (task.hasTag ("COMPLETED")) virtualTags += "COMPLETED "; - if (task.hasTag ("DELETED")) virtualTags += "DELETED "; - if (task.hasTag ("DUE")) virtualTags += "DUE "; - if (task.hasTag ("DUETODAY")) virtualTags += "DUETODAY "; // 2016-03-29: Deprecated in 2.6.0 - if (task.hasTag ("INSTANCE")) virtualTags += "INSTANCE "; - if (task.hasTag ("LATEST")) virtualTags += "LATEST "; - if (task.hasTag ("MONTH")) virtualTags += "MONTH "; - if (task.hasTag ("ORPHAN")) virtualTags += "ORPHAN "; - if (task.hasTag ("OVERDUE")) virtualTags += "OVERDUE "; - if (task.hasTag ("PARENT")) virtualTags += "PARENT "; // 2017-01-07: Deprecated in 2.6.0 - if (task.hasTag ("PENDING")) virtualTags += "PENDING "; - if (task.hasTag ("PRIORITY")) virtualTags += "PRIORITY "; - if (task.hasTag ("PROJECT")) virtualTags += "PROJECT "; - if (task.hasTag ("QUARTER")) virtualTags += "QUARTER "; - if (task.hasTag ("READY")) virtualTags += "READY "; - if (task.hasTag ("SCHEDULED")) virtualTags += "SCHEDULED "; - if (task.hasTag ("TAGGED")) virtualTags += "TAGGED "; - if (task.hasTag ("TEMPLATE")) virtualTags += "TEMPLATE "; - if (task.hasTag ("TODAY")) virtualTags += "TODAY "; - if (task.hasTag ("TOMORROW")) virtualTags += "TOMORROW "; - if (task.hasTag ("UDA")) virtualTags += "UDA "; - if (task.hasTag ("UNBLOCKED")) virtualTags += "UNBLOCKED "; - if (task.hasTag ("UNTIL")) virtualTags += "UNTIL "; - if (task.hasTag ("WAITING")) virtualTags += "WAITING "; - if (task.hasTag ("WEEK")) virtualTags += "WEEK "; - if (task.hasTag ("YEAR")) virtualTags += "YEAR "; - if (task.hasTag ("YESTERDAY")) virtualTags += "YESTERDAY "; + if (task.hasTag("ACTIVE")) virtualTags += "ACTIVE "; + if (task.hasTag("ANNOTATED")) virtualTags += "ANNOTATED "; + if (task.hasTag("BLOCKED")) virtualTags += "BLOCKED "; + if (task.hasTag("BLOCKING")) virtualTags += "BLOCKING "; + if (task.hasTag("CHILD")) virtualTags += "CHILD "; // 2017-01-07: Deprecated in 2.6.0 + if (task.hasTag("COMPLETED")) virtualTags += "COMPLETED "; + if (task.hasTag("DELETED")) virtualTags += "DELETED "; + if (task.hasTag("DUE")) virtualTags += "DUE "; + if (task.hasTag("DUETODAY")) virtualTags += "DUETODAY "; // 2016-03-29: Deprecated in 2.6.0 + if (task.hasTag("INSTANCE")) virtualTags += "INSTANCE "; + if (task.hasTag("LATEST")) virtualTags += "LATEST "; + if (task.hasTag("MONTH")) virtualTags += "MONTH "; + if (task.hasTag("ORPHAN")) virtualTags += "ORPHAN "; + if (task.hasTag("OVERDUE")) virtualTags += "OVERDUE "; + if (task.hasTag("PARENT")) virtualTags += "PARENT "; // 2017-01-07: Deprecated in 2.6.0 + if (task.hasTag("PENDING")) virtualTags += "PENDING "; + if (task.hasTag("PRIORITY")) virtualTags += "PRIORITY "; + if (task.hasTag("PROJECT")) virtualTags += "PROJECT "; + if (task.hasTag("QUARTER")) virtualTags += "QUARTER "; + if (task.hasTag("READY")) virtualTags += "READY "; + if (task.hasTag("SCHEDULED")) virtualTags += "SCHEDULED "; + if (task.hasTag("TAGGED")) virtualTags += "TAGGED "; + if (task.hasTag("TEMPLATE")) virtualTags += "TEMPLATE "; + if (task.hasTag("TODAY")) virtualTags += "TODAY "; + if (task.hasTag("TOMORROW")) virtualTags += "TOMORROW "; + if (task.hasTag("UDA")) virtualTags += "UDA "; + if (task.hasTag("UNBLOCKED")) virtualTags += "UNBLOCKED "; + if (task.hasTag("UNTIL")) virtualTags += "UNTIL "; + if (task.hasTag("WAITING")) virtualTags += "WAITING "; + if (task.hasTag("WEEK")) virtualTags += "WEEK "; + if (task.hasTag("YEAR")) virtualTags += "YEAR "; + if (task.hasTag("YESTERDAY")) virtualTags += "YESTERDAY "; // If you update the above list, update src/Task.cpp and src/commands/CmdTags.cpp as well. - row = view.addRow (); - view.set (row, 0, "Virtual tags"); - view.set (row, 1, virtualTags); + row = view.addRow(); + view.set(row, 0, "Virtual tags"); + view.set(row, 1, virtualTags); } // uuid - row = view.addRow (); - view.set (row, 0, "UUID"); - auto uuid = task.get ("uuid"); - view.set (row, 1, uuid); + row = view.addRow(); + view.set(row, 0, "UUID"); + auto uuid = task.get("uuid"); + view.set(row, 1, uuid); // Task::urgency - row = view.addRow (); - view.set (row, 0, "Urgency"); - view.set (row, 1, Lexer::trimLeft (format (task.urgency (), 4, 4))); + row = view.addRow(); + view.set(row, 0, "Urgency"); + view.set(row, 1, Lexer::trimLeft(format(task.urgency(), 4, 4))); // Show any UDAs - auto all = task.all (); - for (auto& att: all) - { - if (Context::getContext ().columns.find (att) != Context::getContext ().columns.end ()) - { - Column* col = Context::getContext ().columns[att]; - if (col->is_uda ()) - { - auto value = task.get (att); - if (value != "") - { - row = view.addRow (); - view.set (row, 0, col->label ()); + auto all = task.all(); + for (auto& att : all) { + if (Context::getContext().columns.find(att) != Context::getContext().columns.end()) { + Column* col = Context::getContext().columns[att]; + if (col->is_uda()) { + auto value = task.get(att); + if (value != "") { + row = view.addRow(); + view.set(row, 0, col->label()); - if (col->type () == "date") - value = Datetime (value).toString (dateformat); - else if (col->type () == "duration") - { + if (col->type() == "date") + value = Datetime(value).toString(dateformat); + else if (col->type() == "duration") { Duration iso; std::string::size_type cursor = 0; - if (iso.parse (value, cursor)) - value = (std::string) Variant (iso.toTime_t (), Variant::type_duration); + if (iso.parse(value, cursor)) + value = (std::string)Variant(iso.toTime_t(), Variant::type_duration); else value = "PT0S"; } - view.set (row, 1, value); + view.set(row, 1, value); } } } @@ -408,155 +372,132 @@ int CmdInfo::execute (std::string& output) // Show any orphaned UDAs, which are identified by not being represented in // the context.columns map. - for (auto& att : all) - { - if (att.substr (0, 11) != "annotation_" && - att.substr (0, 5) != "tags_" && - att.substr (0, 4) != "dep_" && - Context::getContext ().columns.find (att) == Context::getContext ().columns.end ()) - { - row = view.addRow (); - view.set (row, 0, '[' + att); - view.set (row, 1, task.get (att) + ']'); + for (auto& att : all) { + if (att.substr(0, 11) != "annotation_" && att.substr(0, 5) != "tags_" && + att.substr(0, 4) != "dep_" && + Context::getContext().columns.find(att) == Context::getContext().columns.end()) { + row = view.addRow(); + view.set(row, 0, '[' + att); + view.set(row, 1, task.get(att) + ']'); } } // Create a second table, containing urgency details, if necessary. Table urgencyDetails; - if (task.urgency () != 0.0) - { - setHeaderUnderline (urgencyDetails); - if (Context::getContext ().color ()) - { - Color alternate (Context::getContext ().config.get ("color.alternate")); - urgencyDetails.colorOdd (alternate); - urgencyDetails.intraColorOdd (alternate); + if (task.urgency() != 0.0) { + setHeaderUnderline(urgencyDetails); + if (Context::getContext().color()) { + Color alternate(Context::getContext().config.get("color.alternate")); + urgencyDetails.colorOdd(alternate); + urgencyDetails.intraColorOdd(alternate); } - if (Context::getContext ().config.getBoolean ("obfuscate")) - urgencyDetails.obfuscate (); - if (Context::getContext ().config.getBoolean ("color")) - view.forceColor (); + if (Context::getContext().config.getBoolean("obfuscate")) urgencyDetails.obfuscate(); + if (Context::getContext().config.getBoolean("color")) view.forceColor(); - urgencyDetails.width (Context::getContext ().getWidth ()); - urgencyDetails.add (""); // Attribute - urgencyDetails.add (""); // Value - urgencyDetails.add (""); // * - urgencyDetails.add (""); // Coefficient - urgencyDetails.add (""); // = - urgencyDetails.add (""); // Result + urgencyDetails.width(Context::getContext().getWidth()); + urgencyDetails.add(""); // Attribute + urgencyDetails.add(""); // Value + urgencyDetails.add(""); // * + urgencyDetails.add(""); // Coefficient + urgencyDetails.add(""); // = + urgencyDetails.add(""); // Result - urgencyTerm (urgencyDetails, "project", task.urgency_project (), Task::urgencyProjectCoefficient); - urgencyTerm (urgencyDetails, "active", task.urgency_active (), Task::urgencyActiveCoefficient); - urgencyTerm (urgencyDetails, "scheduled", task.urgency_scheduled (), Task::urgencyScheduledCoefficient); - urgencyTerm (urgencyDetails, "waiting", task.urgency_waiting (), Task::urgencyWaitingCoefficient); - urgencyTerm (urgencyDetails, "blocked", task.urgency_blocked (), Task::urgencyBlockedCoefficient); - urgencyTerm (urgencyDetails, "blocking", task.urgency_blocking (), Task::urgencyBlockingCoefficient); - urgencyTerm (urgencyDetails, "annotations", task.urgency_annotations (), Task::urgencyAnnotationsCoefficient); - urgencyTerm (urgencyDetails, "tags", task.urgency_tags (), Task::urgencyTagsCoefficient); - urgencyTerm (urgencyDetails, "due", task.urgency_due (), Task::urgencyDueCoefficient); - urgencyTerm (urgencyDetails, "age", task.urgency_age (), Task::urgencyAgeCoefficient); + urgencyTerm(urgencyDetails, "project", task.urgency_project(), + Task::urgencyProjectCoefficient); + urgencyTerm(urgencyDetails, "active", task.urgency_active(), Task::urgencyActiveCoefficient); + urgencyTerm(urgencyDetails, "scheduled", task.urgency_scheduled(), + Task::urgencyScheduledCoefficient); + urgencyTerm(urgencyDetails, "waiting", task.urgency_waiting(), + Task::urgencyWaitingCoefficient); + urgencyTerm(urgencyDetails, "blocked", task.urgency_blocked(), + Task::urgencyBlockedCoefficient); + urgencyTerm(urgencyDetails, "blocking", task.urgency_blocking(), + Task::urgencyBlockingCoefficient); + urgencyTerm(urgencyDetails, "annotations", task.urgency_annotations(), + Task::urgencyAnnotationsCoefficient); + urgencyTerm(urgencyDetails, "tags", task.urgency_tags(), Task::urgencyTagsCoefficient); + urgencyTerm(urgencyDetails, "due", task.urgency_due(), Task::urgencyDueCoefficient); + urgencyTerm(urgencyDetails, "age", task.urgency_age(), Task::urgencyAgeCoefficient); // Tag, Project- and UDA-specific coefficients. - for (auto& var : Task::coefficients) - { - if (var.first.substr (0, 13) == "urgency.user.") - { + for (auto& var : Task::coefficients) { + if (var.first.substr(0, 13) == "urgency.user.") { // urgency.user.project..coefficient auto end = std::string::npos; - if (var.first.substr (13, 8) == "project." && - (end = var.first.find (".coefficient")) != std::string::npos) - { - auto project = var.first.substr (21, end - 21); + if (var.first.substr(13, 8) == "project." && + (end = var.first.find(".coefficient")) != std::string::npos) { + auto project = var.first.substr(21, end - 21); const std::string taskProjectName = task.get("project"); - if (taskProjectName == project || - taskProjectName.find(project + '.') == 0) - { - urgencyTerm (urgencyDetails, "PROJECT " + project, 1.0, var.second); + if (taskProjectName == project || taskProjectName.find(project + '.') == 0) { + urgencyTerm(urgencyDetails, "PROJECT " + project, 1.0, var.second); } } // urgency.user.tag..coefficient - if (var.first.substr (13, 4) == "tag." && - (end = var.first.find (".coefficient")) != std::string::npos) - { - auto name = var.first.substr (17, end - 17); - if (task.hasTag (name)) - urgencyTerm (urgencyDetails, "TAG " + name, 1.0, var.second); + if (var.first.substr(13, 4) == "tag." && + (end = var.first.find(".coefficient")) != std::string::npos) { + auto name = var.first.substr(17, end - 17); + if (task.hasTag(name)) urgencyTerm(urgencyDetails, "TAG " + name, 1.0, var.second); } // urgency.user.keyword..coefficient - if (var.first.substr (13, 8) == "keyword." && - (end = var.first.find (".coefficient")) != std::string::npos) - { - auto keyword = var.first.substr (21, end - 21); - if (task.get ("description").find (keyword) != std::string::npos) - urgencyTerm (urgencyDetails, "KEYWORD " + keyword, 1.0, var.second); + if (var.first.substr(13, 8) == "keyword." && + (end = var.first.find(".coefficient")) != std::string::npos) { + auto keyword = var.first.substr(21, end - 21); + if (task.get("description").find(keyword) != std::string::npos) + urgencyTerm(urgencyDetails, "KEYWORD " + keyword, 1.0, var.second); } } // urgency.uda..coefficient - else if (var.first.substr (0, 12) == "urgency.uda.") - { + else if (var.first.substr(0, 12) == "urgency.uda.") { // urgency.uda..coefficient // urgency.uda...coefficient - auto end = var.first.find (".coefficient"); - if (end != std::string::npos) - { - auto uda = var.first.substr (12, end - 12); - auto dot = uda.find ('.'); - if (dot == std::string::npos) - { + auto end = var.first.find(".coefficient"); + if (end != std::string::npos) { + auto uda = var.first.substr(12, end - 12); + auto dot = uda.find('.'); + if (dot == std::string::npos) { // urgency.uda..coefficient - if (task.has (uda)) - urgencyTerm (urgencyDetails, std::string ("UDA ") + uda, 1.0, var.second); - } - else - { + if (task.has(uda)) + urgencyTerm(urgencyDetails, std::string("UDA ") + uda, 1.0, var.second); + } else { // urgency.uda...coefficient - if (task.get (uda.substr(0, dot)) == uda.substr(dot+1)) - urgencyTerm (urgencyDetails, std::string ("UDA ") + uda, 1.0, var.second); + if (task.get(uda.substr(0, dot)) == uda.substr(dot + 1)) + urgencyTerm(urgencyDetails, std::string("UDA ") + uda, 1.0, var.second); } } } } - row = urgencyDetails.addRow (); - urgencyDetails.set (row, 5, rightJustify ("------", 6)); - row = urgencyDetails.addRow (); - urgencyDetails.set (row, 5, rightJustify (format (task.urgency (), 4, 4), 6)); + row = urgencyDetails.addRow(); + urgencyDetails.set(row, 5, rightJustify("------", 6)); + row = urgencyDetails.addRow(); + urgencyDetails.set(row, 5, rightJustify(format(task.urgency(), 4, 4), 6)); } - out << optionalBlankLine () - << view.render () - << '\n'; + out << optionalBlankLine() << view.render() << '\n'; - if (urgencyDetails.rows () > 0) - out << urgencyDetails.render () - << '\n'; + if (urgencyDetails.rows() > 0) out << urgencyDetails.render() << '\n'; } - output = out.str (); + output = out.str(); return rc; } //////////////////////////////////////////////////////////////////////////////// -void CmdInfo::urgencyTerm ( - Table& view, - const std::string& label, - float measure, - float coefficient) const -{ +void CmdInfo::urgencyTerm(Table& view, const std::string& label, float measure, + float coefficient) const { auto value = measure * coefficient; - if (value != 0.0) - { - auto row = view.addRow (); - view.set (row, 0, " " + label); - view.set (row, 1, rightJustify (format (measure, 5, 3), 6)); - view.set (row, 2, "*"); - view.set (row, 3, rightJustify (format (coefficient, 4, 2), 4)); - view.set (row, 4, "="); - view.set (row, 5, rightJustify (format (value, 5, 3), 6)); + if (value != 0.0) { + auto row = view.addRow(); + view.set(row, 0, " " + label); + view.set(row, 1, rightJustify(format(measure, 5, 3), 6)); + view.set(row, 2, "*"); + view.set(row, 3, rightJustify(format(coefficient, 4, 2), 4)); + view.set(row, 4, "="); + view.set(row, 5, rightJustify(format(value, 5, 3), 6)); } } diff --git a/src/commands/CmdInfo.h b/src/commands/CmdInfo.h index ed3b647bd..06516e920 100644 --- a/src/commands/CmdInfo.h +++ b/src/commands/CmdInfo.h @@ -27,18 +27,18 @@ #ifndef INCLUDED_CMDINFO #define INCLUDED_CMDINFO -#include #include #include -class CmdInfo : public Command -{ -public: - CmdInfo (); - int execute (std::string&); +#include -private: - void urgencyTerm (Table&, const std::string&, float, float) const; +class CmdInfo : public Command { + public: + CmdInfo(); + int execute(std::string&); + + private: + void urgencyTerm(Table&, const std::string&, float, float) const; }; #endif diff --git a/src/commands/CmdLog.cpp b/src/commands/CmdLog.cpp index c58032e57..af3e9f361 100644 --- a/src/commands/CmdLog.cpp +++ b/src/commands/CmdLog.cpp @@ -33,44 +33,40 @@ #include //////////////////////////////////////////////////////////////////////////////// -CmdLog::CmdLog () -{ - _keyword = "log"; - _usage = "task log "; - _description = "Adds a new task that is already completed"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = true; - _accepts_filter = false; +CmdLog::CmdLog() { + _keyword = "log"; + _usage = "task log "; + _description = "Adds a new task that is already completed"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = true; + _accepts_filter = false; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdLog::execute (std::string& output) -{ +int CmdLog::execute(std::string& output) { // Apply the command line modifications to the new task. Task task; - task.modify (Task::modReplace, true); - task.setStatus (Task::completed); + task.modify(Task::modReplace, true); + task.setStatus(Task::completed); // Cannot log recurring tasks. - if (task.has ("recur")) - throw std::string ("You cannot log recurring tasks."); + if (task.has("recur")) throw std::string("You cannot log recurring tasks."); // Cannot log waiting tasks. - if (task.has ("wait")) - throw std::string ("You cannot log waiting tasks."); + if (task.has("wait")) throw std::string("You cannot log waiting tasks."); - Context::getContext ().tdb2.add (task); + Context::getContext().tdb2.add(task); - if (Context::getContext ().verbose ("project")) - Context::getContext ().footnote (onProjectChange (task)); + if (Context::getContext().verbose("project")) + Context::getContext().footnote(onProjectChange(task)); - if (Context::getContext ().verbose ("new-uuid")) - output = format ("Logged task {1}.\n", task.get ("uuid")); + if (Context::getContext().verbose("new-uuid")) + output = format("Logged task {1}.\n", task.get("uuid")); return 0; } diff --git a/src/commands/CmdLog.h b/src/commands/CmdLog.h index b7b322671..f5561472f 100644 --- a/src/commands/CmdLog.h +++ b/src/commands/CmdLog.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDLOG #define INCLUDED_CMDLOG -#include #include -class CmdLog : public Command -{ -public: - CmdLog (); - int execute (std::string&); +#include + +class CmdLog : public Command { + public: + CmdLog(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdLogo.cpp b/src/commands/CmdLogo.cpp index c6d3138d8..37efec23d 100644 --- a/src/commands/CmdLogo.cpp +++ b/src/commands/CmdLogo.cpp @@ -32,91 +32,81 @@ #include //////////////////////////////////////////////////////////////////////////////// -CmdLogo::CmdLogo () -{ - _keyword = "logo"; - _usage = "task logo"; - _description = "Displays the Taskwarrior logo"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdLogo::CmdLogo() { + _keyword = "logo"; + _usage = "task logo"; + _description = "Displays the Taskwarrior logo"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::misc; + _category = Command::Category::misc; } //////////////////////////////////////////////////////////////////////////////// -int CmdLogo::execute (std::string& output) -{ - static const char* data[] = - { - ".........ABDEF", - ".......BDILNMM", - ".....ACJNLHFEE", - "....AFMMFDEEEE", - "...AFOIDEEEEDD", - "..AFOHDEEEDIOR", - "..DMIDEEEEOXXX", - ".BJMDEEEDMXXXX", - ".ENFEEEEFVXXXX", - "BILDEEEEJXXXXX", - "DLHEEEEDLXXXXX", - "GMFEEEEDKXXXXX", - "GMEEEEEEGTIKOR", - "GMEEEEEEETODDD", - "GMEEEEEEETXRGE", - "GMEEEEEEFVXXSE", - "ENFEEEEEHWXXUE", - "CLHEEEEDLXXXUE", - "AILDEEEDRXXXUE", - ".DNFEEEEPXXXUE", - ".BJMDEEEEPXXUE", - "..DMIDEEEDLXUE", - "..AFOHDEEEDHME", - "...AFOIDEEEEDE", - "....AFMMFDEEEE", - ".....ACJNLHFEE", - ".......BDILNMM", - ".........ABDEF", - "" - }; +int CmdLogo::execute(std::string& output) { + static const char* data[] = {".........ABDEF", + ".......BDILNMM", + ".....ACJNLHFEE", + "....AFMMFDEEEE", + "...AFOIDEEEEDD", + "..AFOHDEEEDIOR", + "..DMIDEEEEOXXX", + ".BJMDEEEDMXXXX", + ".ENFEEEEFVXXXX", + "BILDEEEEJXXXXX", + "DLHEEEEDLXXXXX", + "GMFEEEEDKXXXXX", + "GMEEEEEEGTIKOR", + "GMEEEEEEETODDD", + "GMEEEEEEETXRGE", + "GMEEEEEEFVXXSE", + "ENFEEEEEHWXXUE", + "CLHEEEEDLXXXUE", + "AILDEEEDRXXXUE", + ".DNFEEEEPXXXUE", + ".BJMDEEEEPXXUE", + "..DMIDEEEDLXUE", + "..AFOHDEEEDHME", + "...AFOIDEEEEDE", + "....AFMMFDEEEE", + ".....ACJNLHFEE", + ".......BDILNMM", + ".........ABDEF", + ""}; - if (! Context::getContext ().color ()) - throw std::string ("The logo command requires that color support is enabled."); + if (!Context::getContext().color()) + throw std::string("The logo command requires that color support is enabled."); - std::string indent (Context::getContext ().config.getInteger ("indent.report"), ' '); - output += optionalBlankLine (); + std::string indent(Context::getContext().config.getInteger("indent.report"), ' '); + output += optionalBlankLine(); - for (int line = 0; data[line][0]; ++line) - { + for (int line = 0; data[line][0]; ++line) { output += indent; - for (int c = 0; c < 14; ++c) - { - int value = (int) data[line][c]; + for (int c = 0; c < 14; ++c) { + int value = (int)data[line][c]; if (value == '.') output += " "; - else - { + else { value += 167; - char block [24]; - snprintf (block, 24, "\033[48;5;%dm \033[0m", value); + char block[24]; + snprintf(block, 24, "\033[48;5;%dm \033[0m", value); output += block; } } - for (int c = 13; c >= 0; --c) - { + for (int c = 13; c >= 0; --c) { int value = data[line][c]; if (value == '.') output += " "; - else - { + else { value += 167; - char block [24]; - snprintf (block, 24, "\033[48;5;%dm \033[0m", value); + char block[24]; + snprintf(block, 24, "\033[48;5;%dm \033[0m", value); output += block; } } @@ -124,7 +114,7 @@ int CmdLogo::execute (std::string& output) output += '\n'; } - output += optionalBlankLine (); + output += optionalBlankLine(); return 0; } diff --git a/src/commands/CmdLogo.h b/src/commands/CmdLogo.h index 441ec123a..bea0bfecf 100644 --- a/src/commands/CmdLogo.h +++ b/src/commands/CmdLogo.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDLOGO #define INCLUDED_CMDLOGO -#include #include -class CmdLogo : public Command -{ -public: - CmdLogo (); - int execute (std::string&); +#include + +class CmdLogo : public Command { + public: + CmdLogo(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdModify.cpp b/src/commands/CmdModify.cpp index 4a16a9427..659c076d3 100644 --- a/src/commands/CmdModify.cpp +++ b/src/commands/CmdModify.cpp @@ -28,197 +28,169 @@ // cmake.h include header must come first #include -#include #include #include -#include #include +#include #include -#define STRING_CMD_MODIFY_TASK_R "Modifying recurring task {1} '{2}'." -#define STRING_CMD_MODIFY_RECUR "This is a recurring task. Do you want to modify all pending recurrences of this same task?" +#include + +#define STRING_CMD_MODIFY_TASK_R "Modifying recurring task {1} '{2}'." +#define STRING_CMD_MODIFY_RECUR \ + "This is a recurring task. Do you want to modify all pending recurrences of this same task?" //////////////////////////////////////////////////////////////////////////////// -CmdModify::CmdModify () -{ - _keyword = "modify"; - _usage = "task modify "; - _description = "Modifies the existing task with provided arguments."; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = true; +CmdModify::CmdModify() { + _keyword = "modify"; + _usage = "task modify "; + _description = "Modifies the existing task with provided arguments."; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdModify::execute (std::string&) -{ +int CmdModify::execute(std::string &) { auto rc = 0; // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + std::vector filtered; + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // Accumulated project change notifications. - std::map projectChanges; + std::map projectChanges; auto count = 0; - if(filtered.size() > 1) { + if (filtered.size() > 1) { feedback_affected("This command will alter {1} tasks.", filtered.size()); } - for (auto& task : filtered) - { - Task before (task); - task.modify (Task::modReplace); + for (auto &task : filtered) { + Task before(task); + task.modify(Task::modReplace); - if (before != task) - { + if (before != task) { // Abort if change introduces inconsistencies. checkConsistency(before, task); - auto question = format ("Modify task {1} '{2}'?", - task.identifier (true), - task.get ("description")); + auto question = + format("Modify task {1} '{2}'?", task.identifier(true), task.get("description")); - if (permission (before.diff (task) + question, filtered.size ())) - { - count += modifyAndUpdate (before, task, &projectChanges); - } - else - { + if (permission(before.diff(task) + question, filtered.size())) { + count += modifyAndUpdate(before, task, &projectChanges); + } else { std::cout << "Task not modified.\n"; rc = 1; - if (_permission_quit) - break; + if (_permission_quit) break; } } } // Now list the project changes. - for (const auto& change : projectChanges) - if (change.first != "") - Context::getContext ().footnote (change.second); + for (const auto &change : projectChanges) + if (change.first != "") Context::getContext().footnote(change.second); - feedback_affected (count == 1 ? "Modified {1} task." : "Modified {1} tasks.", count); + feedback_affected(count == 1 ? "Modified {1} task." : "Modified {1} tasks.", count); return rc; } //////////////////////////////////////////////////////////////////////////////// // TODO Why is this not in Task::validate? -void CmdModify::checkConsistency (Task &before, Task &after) -{ +void CmdModify::checkConsistency(Task &before, Task &after) { // Perform some logical consistency checks. - if (after.has ("recur") && - ! after.has ("due") && - ! before.has ("due")) - throw std::string ("You cannot specify a recurring task without a due date."); + if (after.has("recur") && !after.has("due") && !before.has("due")) + throw std::string("You cannot specify a recurring task without a due date."); - if (before.has ("recur") && - before.has ("due") && - (! after.has ("due") || - after.get ("due") == "")) - throw std::string ("You cannot remove the due date from a recurring task."); + if (before.has("recur") && before.has("due") && (!after.has("due") || after.get("due") == "")) + throw std::string("You cannot remove the due date from a recurring task."); - if (before.has ("recur") && - (! after.has ("recur") || - after.get ("recur") == "")) - throw std::string ("You cannot remove the recurrence from a recurring task."); + if (before.has("recur") && (!after.has("recur") || after.get("recur") == "")) + throw std::string("You cannot remove the recurrence from a recurring task."); } //////////////////////////////////////////////////////////////////////////////// -int CmdModify::modifyAndUpdate ( - Task &before, Task &after, - std::map *projectChanges /* = NULL */) -{ +int CmdModify::modifyAndUpdate(Task &before, Task &after, + std::map *projectChanges /* = NULL */) { // This task. auto count = 1; - updateRecurrenceMask (after); - feedback_affected ("Modifying task {1} '{2}'.", after); - feedback_unblocked (after); - Context::getContext ().tdb2.modify (after); - if (Context::getContext ().verbose ("project") && projectChanges) - (*projectChanges)[after.get ("project")] = onProjectChange (before, after); + updateRecurrenceMask(after); + feedback_affected("Modifying task {1} '{2}'.", after); + feedback_unblocked(after); + Context::getContext().tdb2.modify(after); + if (Context::getContext().verbose("project") && projectChanges) + (*projectChanges)[after.get("project")] = onProjectChange(before, after); // Task has siblings - modify them. - if (after.has ("parent")) - count += modifyRecurrenceSiblings (after, projectChanges); + if (after.has("parent")) count += modifyRecurrenceSiblings(after, projectChanges); // Task has child tasks - modify them. - else if (after.get ("status") == "recurring") - count += modifyRecurrenceParent (after, projectChanges); + else if (after.get("status") == "recurring") + count += modifyRecurrenceParent(after, projectChanges); return count; } //////////////////////////////////////////////////////////////////////////////// -int CmdModify::modifyRecurrenceSiblings ( - Task &task, - std::map *projectChanges /* = NULL */) -{ +int CmdModify::modifyRecurrenceSiblings( + Task &task, std::map *projectChanges /* = NULL */) { auto count = 0; - if ((Context::getContext ().config.get ("recurrence.confirmation") == "prompt" - && confirm (STRING_CMD_MODIFY_RECUR)) || - Context::getContext ().config.getBoolean ("recurrence.confirmation")) - { - std::vector siblings = Context::getContext ().tdb2.siblings (task); - for (auto& sibling : siblings) - { - Task alternate (sibling); - sibling.modify (Task::modReplace); - updateRecurrenceMask (sibling); + if ((Context::getContext().config.get("recurrence.confirmation") == "prompt" && + confirm(STRING_CMD_MODIFY_RECUR)) || + Context::getContext().config.getBoolean("recurrence.confirmation")) { + std::vector siblings = Context::getContext().tdb2.siblings(task); + for (auto &sibling : siblings) { + Task alternate(sibling); + sibling.modify(Task::modReplace); + updateRecurrenceMask(sibling); ++count; - feedback_affected (STRING_CMD_MODIFY_TASK_R, sibling); - feedback_unblocked (sibling); - Context::getContext ().tdb2.modify (sibling); - if (Context::getContext ().verbose ("project") && projectChanges) - (*projectChanges)[sibling.get ("project")] = onProjectChange (alternate, sibling); + feedback_affected(STRING_CMD_MODIFY_TASK_R, sibling); + feedback_unblocked(sibling); + Context::getContext().tdb2.modify(sibling); + if (Context::getContext().verbose("project") && projectChanges) + (*projectChanges)[sibling.get("project")] = onProjectChange(alternate, sibling); } // Modify the parent Task parent; - Context::getContext ().tdb2.get (task.get ("parent"), parent); - parent.modify (Task::modReplace); - Context::getContext ().tdb2.modify (parent); + Context::getContext().tdb2.get(task.get("parent"), parent); + parent.modify(Task::modReplace); + Context::getContext().tdb2.modify(parent); } return count; } //////////////////////////////////////////////////////////////////////////////// -int CmdModify::modifyRecurrenceParent ( - Task &task, - std::map *projectChanges /* = NULL */) -{ +int CmdModify::modifyRecurrenceParent( + Task &task, std::map *projectChanges /* = NULL */) { auto count = 0; - auto children = Context::getContext ().tdb2.children (task); - if (children.size () && - ((Context::getContext ().config.get ("recurrence.confirmation") == "prompt" - && confirm (STRING_CMD_MODIFY_RECUR)) || - Context::getContext ().config.getBoolean ("recurrence.confirmation"))) - { - for (auto& child : children) - { - Task alternate (child); - child.modify (Task::modReplace); - updateRecurrenceMask (child); - Context::getContext ().tdb2.modify (child); - if (Context::getContext ().verbose ("project") && projectChanges) - (*projectChanges)[child.get ("project")] = onProjectChange (alternate, child); + auto children = Context::getContext().tdb2.children(task); + if (children.size() && + ((Context::getContext().config.get("recurrence.confirmation") == "prompt" && + confirm(STRING_CMD_MODIFY_RECUR)) || + Context::getContext().config.getBoolean("recurrence.confirmation"))) { + for (auto &child : children) { + Task alternate(child); + child.modify(Task::modReplace); + updateRecurrenceMask(child); + Context::getContext().tdb2.modify(child); + if (Context::getContext().verbose("project") && projectChanges) + (*projectChanges)[child.get("project")] = onProjectChange(alternate, child); ++count; - feedback_affected (STRING_CMD_MODIFY_TASK_R, child); + feedback_affected(STRING_CMD_MODIFY_TASK_R, child); } } @@ -226,4 +198,3 @@ int CmdModify::modifyRecurrenceParent ( } //////////////////////////////////////////////////////////////////////////////// - diff --git a/src/commands/CmdModify.h b/src/commands/CmdModify.h index 1dcfb4fd5..17313e798 100644 --- a/src/commands/CmdModify.h +++ b/src/commands/CmdModify.h @@ -27,21 +27,21 @@ #ifndef INCLUDED_CMDMODIFY #define INCLUDED_CMDMODIFY -#include #include -class CmdModify : public Command -{ -public: - CmdModify (); - int execute (std::string&); - void checkConsistency (Task &before, Task &after); - int modifyAndUpdate (Task &before, Task &after, - std::map *projectChanges = nullptr); - int modifyRecurrenceSiblings (Task &task, - std::map *projectChanges = nullptr); - int modifyRecurrenceParent (Task &task, - std::map *projectChanges = nullptr); +#include + +class CmdModify : public Command { + public: + CmdModify(); + int execute(std::string &); + void checkConsistency(Task &before, Task &after); + int modifyAndUpdate(Task &before, Task &after, + std::map *projectChanges = nullptr); + int modifyRecurrenceSiblings(Task &task, + std::map *projectChanges = nullptr); + int modifyRecurrenceParent(Task &task, + std::map *projectChanges = nullptr); }; #endif diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 419372095..cb1e8a3c0 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -28,17 +28,18 @@ // cmake.h include header must come first #include -#include -#include -#include -#include #include #include #include -#include +#include #include -#include #include +#include +#include + +#include +#include +#include /* Adding a new version: * @@ -49,67 +50,56 @@ */ //////////////////////////////////////////////////////////////////////////////// -CmdNews::CmdNews () -{ - _keyword = "news"; - _usage = "task news"; - _description = "Displays news about the recent releases"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdNews::CmdNews() { + _keyword = "news"; + _usage = "task news"; + _description = "Displays news about the recent releases"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::misc; + _category = Command::Category::misc; } //////////////////////////////////////////////////////////////////////////////// -static void signal_handler (int s) -{ - if (s == SIGINT) - { +static void signal_handler(int s) { + if (s == SIGINT) { Color footnote; - if (Context::getContext ().color ()) { - if (Context::getContext ().config.has ("color.footnote")) - footnote = Color (Context::getContext ().config.get ("color.footnote")); + if (Context::getContext().color()) { + if (Context::getContext().config.has("color.footnote")) + footnote = Color(Context::getContext().config.get("color.footnote")); } std::cout << "\n\nCome back and read about new features later!\n"; - std::cout << footnote.colorize ( - "\nIf you enjoy Taskwarrior, please consider supporting the project at:\n" - " https://github.com/sponsors/GothenburgBitFactory/\n" - ); - exit (1); + std::cout << footnote.colorize( + "\nIf you enjoy Taskwarrior, please consider supporting the project at:\n" + " https://github.com/sponsors/GothenburgBitFactory/\n"); + exit(1); } } -void wait_for_enter () -{ - signal (SIGINT, signal_handler); +void wait_for_enter() { + signal(SIGINT, signal_handler); std::string dummy; std::cout << "\nPress enter to continue.."; - std::getline (std::cin, dummy); + std::getline(std::cin, dummy); std::cout << "\33[2K\033[A\33[2K"; // Erase current line, move up, and erase again - signal (SIGINT, SIG_DFL); + signal(SIGINT, SIG_DFL); } //////////////////////////////////////////////////////////////////////////////// // Holds information about single improvement / bug. // -NewsItem::NewsItem ( - Version version, - const std::string& title, - const std::string& bg_title, - const std::string& background, - const std::string& punchline, - const std::string& update, - const std::string& reasoning, - const std::string& actions -) { +NewsItem::NewsItem(Version version, const std::string& title, const std::string& bg_title, + const std::string& background, const std::string& punchline, + const std::string& update, const std::string& reasoning, + const std::string& actions) { _version = version; _title = title; _bg_title = bg_title; @@ -120,57 +110,50 @@ NewsItem::NewsItem ( _actions = actions; } -void NewsItem::render () { - auto config = Context::getContext ().config; +void NewsItem::render() { + auto config = Context::getContext().config; Color header; Color footnote; Color bold; Color underline; - if (Context::getContext ().color ()) { - bold = Color ("bold"); - underline = Color ("underline"); - if (config.has ("color.header")) - header = Color (config.get ("color.header")); - if (config.has ("color.footnote")) - footnote = Color (config.get ("color.footnote")); + if (Context::getContext().color()) { + bold = Color("bold"); + underline = Color("underline"); + if (config.has("color.header")) header = Color(config.get("color.header")); + if (config.has("color.footnote")) footnote = Color(config.get("color.footnote")); } // TODO: For some reason, bold cannot be blended in 256-color terminals // Apply this workaround of colorizing twice. - std::cout << bold.colorize (header.colorize (format ("{1} ({2})\n", _title, _version))); - if (_background.size ()) { - if (_bg_title.empty ()) - _bg_title = "Background"; + std::cout << bold.colorize(header.colorize(format("{1} ({2})\n", _title, _version))); + if (_background.size()) { + if (_bg_title.empty()) _bg_title = "Background"; - std::cout << "\n " << underline.colorize (_bg_title) << std::endl - << _background << std::endl; + std::cout << "\n " << underline.colorize(_bg_title) << std::endl << _background << std::endl; } - wait_for_enter (); + wait_for_enter(); - std::cout << " " << underline.colorize (format ("What changed in {1}?\n", _version)); - if (_punchline.size ()) - std::cout << footnote.colorize (format ("{1}\n", _punchline)); + std::cout << " " << underline.colorize(format("What changed in {1}?\n", _version)); + if (_punchline.size()) std::cout << footnote.colorize(format("{1}\n", _punchline)); - if (_update.size ()) - std::cout << format ("{1}\n", _update); + if (_update.size()) std::cout << format("{1}\n", _update); - wait_for_enter (); + wait_for_enter(); - if (_reasoning.size ()) { - std::cout << " " << underline.colorize ("What was the motivation behind this feature?\n") + if (_reasoning.size()) { + std::cout << " " << underline.colorize("What was the motivation behind this feature?\n") << _reasoning << std::endl; - wait_for_enter (); + wait_for_enter(); } - if (_actions.size ()) { - std::cout << " " << underline.colorize ("What do I have to do?\n") - << _actions << std::endl; - wait_for_enter (); + if (_actions.size()) { + std::cout << " " << underline.colorize("What do I have to do?\n") << _actions << std::endl; + wait_for_enter(); } } -std::vector NewsItem::all () { +std::vector NewsItem::all() { std::vector items; version2_6_0(items); version3_0_0(items); @@ -192,484 +175,420 @@ std::vector NewsItem::all () { // - The .by attribute modifier // - Exporting a report // - Multi-day holidays -void NewsItem::version2_6_0 (std::vector& items) { +void NewsItem::version2_6_0(std::vector& items) { Version version("2.6.0"); ///////////////////////////////////////////////////////////////////////////// // - Writeable context // Detect whether user uses any contexts - auto config = Context::getContext ().config; + auto config = Context::getContext().config; std::stringstream advice; - auto defined = CmdContext::getContexts (); - if (defined.size ()) - { + auto defined = CmdContext::getContexts(); + if (defined.size()) { // Detect the old-style contexts std::vector old_style; - std::copy_if ( - defined.begin(), - defined.end(), - std::back_inserter(old_style), - [&](auto& name){return config.has ("context." + name);} - ); + std::copy_if(defined.begin(), defined.end(), std::back_inserter(old_style), + [&](auto& name) { return config.has("context." + name); }); - if (old_style.size ()) - { - advice << format ( - " You have {1} defined contexts, out of which {2} are old-style:\n", - defined.size (), - std::count_if ( - defined.begin (), - defined.end (), - [&](auto& name){return config.has ("context." + name);} - )); + if (old_style.size()) { + advice << format(" You have {1} defined contexts, out of which {2} are old-style:\n", + defined.size(), + std::count_if(defined.begin(), defined.end(), + [&](auto& name) { return config.has("context." + name); })); - for (auto context: defined) { - std::string old_definition = config.get ("context." + context); - if (old_definition != "") - advice << format (" * {1}: {2}\n", context, old_definition); + for (auto context : defined) { + std::string old_definition = config.get("context." + context); + if (old_definition != "") advice << format(" * {1}: {2}\n", context, old_definition); } advice << "\n" " These need to be migrated to new-style, which uses context..read and\n" " context..write config variables. Please run the following commands:\n"; - for (auto context: defined) { - std::string old_definition = config.get ("context." + context); + for (auto context : defined) { + std::string old_definition = config.get("context." + context); if (old_definition != "") - advice << format (" $ task context define {1} '{2}'\n", context, old_definition); + advice << format(" $ task context define {1} '{2}'\n", context, old_definition); } - advice << "\n" - " Please check these filters are also valid modifications. If a context filter is not\n" - " a valid modification, you can set the context..write configuration variable to\n" - " specify the write context explicitly. Read more in CONTEXT section of man taskrc."; - } - else + advice + << "\n" + " Please check these filters are also valid modifications. If a context filter is " + "not\n" + " a valid modification, you can set the context..write configuration variable " + "to\n" + " specify the write context explicitly. Read more in CONTEXT section of man taskrc."; + } else advice << " You don't have any old-style contexts defined, so you're good to go as is!"; - } - else + } else advice << " You don't have any contexts defined, so you're good to go as is!\n" " Read more about how to use contexts in CONTEXT section of 'man task'."; - NewsItem writeable_context ( - version, - "'Writeable' context", - "Background - what is context?", - " The 'context' is a feature (introduced in 2.5.0) that allows users to apply a\n" - " predefined filter to all task reports.\n" - " \n" - " $ task context define work \"project:Work or +urgent\"\n" - " $ task context work\n" - " Context 'work' set. Use 'task context none' to remove.\n" - " \n" - " Now if we proceed to add two tasks:\n" - " $ task add Talk to Jeff pro:Work\n" - " $ task add Call mom pro:Personal\n" - " \n" - " $ task\n" - " ID Age Project Description Urg\n" - " 1 16s Work Talk to Jeff 1\n" - " \n" - " The task \"Call mom\" will not be listed, because it does not match\n" - " the active context (its project is 'Personal' and not 'Work').", - " The currently active context definition is now applied as default modifications\n" - " when creating new tasks using 'task add' and 'task log'.", - " \n" - " Consider following example, using context 'work' defined as 'project:Work' above:\n" - " \n" - " $ task context work\n" - " $ task add Talk to Jeff\n" - " $ task\n" - " ID Age Project Description Urg \n" - " 1 1s Work Talk to Jeff 1\n" - " ^^^^^^^\n" - " \n" - " Note that project attribute was set to 'Work' automatically.", - " This was a popular feature request. Now, if you have a context active,\n" - " newly added tasks no longer \"fall outside\" of the context by default.", - advice.str () - ); + NewsItem writeable_context( + version, "'Writeable' context", "Background - what is context?", + " The 'context' is a feature (introduced in 2.5.0) that allows users to apply a\n" + " predefined filter to all task reports.\n" + " \n" + " $ task context define work \"project:Work or +urgent\"\n" + " $ task context work\n" + " Context 'work' set. Use 'task context none' to remove.\n" + " \n" + " Now if we proceed to add two tasks:\n" + " $ task add Talk to Jeff pro:Work\n" + " $ task add Call mom pro:Personal\n" + " \n" + " $ task\n" + " ID Age Project Description Urg\n" + " 1 16s Work Talk to Jeff 1\n" + " \n" + " The task \"Call mom\" will not be listed, because it does not match\n" + " the active context (its project is 'Personal' and not 'Work').", + " The currently active context definition is now applied as default modifications\n" + " when creating new tasks using 'task add' and 'task log'.", + " \n" + " Consider following example, using context 'work' defined as 'project:Work' above:\n" + " \n" + " $ task context work\n" + " $ task add Talk to Jeff\n" + " $ task\n" + " ID Age Project Description Urg \n" + " 1 1s Work Talk to Jeff 1\n" + " ^^^^^^^\n" + " \n" + " Note that project attribute was set to 'Work' automatically.", + " This was a popular feature request. Now, if you have a context active,\n" + " newly added tasks no longer \"fall outside\" of the context by default.", + advice.str()); items.push_back(writeable_context); ///////////////////////////////////////////////////////////////////////////// // - 64-bit datetime support - NewsItem uint64_support ( - version, - "Support for 64-bit timestamps and numeric values", - "", - "", - " Taskwarrior now supports 64-bit timestamps, making it possible to set due dates\n" - " and other date attributes beyond 19 January 2038 (limit of 32-bit timestamps).\n", - " The current limit is 31 December 9999 for display reasons (last 4-digit year).", - " With each year passing by faster than the last, setting tasks for 2040s\n" - " is not as unfeasible as it once was.", - " Don't forget that 50-year anniversary and 'task add' a long-term task today!" - ); + NewsItem uint64_support( + version, "Support for 64-bit timestamps and numeric values", "", "", + " Taskwarrior now supports 64-bit timestamps, making it possible to set due dates\n" + " and other date attributes beyond 19 January 2038 (limit of 32-bit timestamps).\n", + " The current limit is 31 December 9999 for display reasons (last 4-digit year).", + " With each year passing by faster than the last, setting tasks for 2040s\n" + " is not as unfeasible as it once was.", + " Don't forget that 50-year anniversary and 'task add' a long-term task today!"); items.push_back(uint64_support); ///////////////////////////////////////////////////////////////////////////// // - Waiting is a virtual status - NewsItem waiting_status ( - version, - "Deprecation of the status:waiting", - "", - " If a task has a 'wait' attribute set to a date in the future, it is modified\n" - " to have a 'waiting' status. Once that date is no longer in the future, the status\n" - " is modified to back to 'pending'.", - " The 'waiting' value of status is deprecated, instead users should use +WAITING\n" - " virtual tag, or explicitly query for wait.after:now (the two are equivalent).", - " \n" - " The status:waiting query still works in 2.6.0, but support will be dropped in 3.0.", - "", - " In your custom report definitions, the following expressions should be replaced:\n" - " * 'status:pending or status:waiting' should be replaced by 'status:pending'\n" - " * 'status:pending' should be replaced by 'status:pending -WAITING'" - ); + NewsItem waiting_status( + version, "Deprecation of the status:waiting", "", + " If a task has a 'wait' attribute set to a date in the future, it is modified\n" + " to have a 'waiting' status. Once that date is no longer in the future, the status\n" + " is modified to back to 'pending'.", + " The 'waiting' value of status is deprecated, instead users should use +WAITING\n" + " virtual tag, or explicitly query for wait.after:now (the two are equivalent).", + " \n" + " The status:waiting query still works in 2.6.0, but support will be dropped in 3.0.", + "", + " In your custom report definitions, the following expressions should be replaced:\n" + " * 'status:pending or status:waiting' should be replaced by 'status:pending'\n" + " * 'status:pending' should be replaced by 'status:pending -WAITING'"); items.push_back(waiting_status); ///////////////////////////////////////////////////////////////////////////// // - Support for environment variables in the taskrc - NewsItem env_vars ( - version, - "Environment variables in the taskrc", - "", - "", - " Taskwarrior now supports expanding environment variables in the taskrc file,\n" - " allowing users to customize the behaviour of 'task' based on the current env.\n", - " The environment variables can either be used in paths, or as separate values:\n" - " data.location=$XDG_DATA_HOME/task/\n" - " default.project=$PROJECT", - "", - "" - ); + NewsItem env_vars( + version, "Environment variables in the taskrc", "", "", + " Taskwarrior now supports expanding environment variables in the taskrc file,\n" + " allowing users to customize the behaviour of 'task' based on the current env.\n", + " The environment variables can either be used in paths, or as separate values:\n" + " data.location=$XDG_DATA_HOME/task/\n" + " default.project=$PROJECT", + "", ""); items.push_back(env_vars); ///////////////////////////////////////////////////////////////////////////// // - Reports outside of context - NewsItem contextless_reports ( - version, - "Context-less reports", - "", - " By default, every report is affected by currently active context.", - " You can now make a selected report ignore currently active context by setting\n" - " 'report..context' configuration variable to 0.", - "", - " This is useful for users who utilize a single place (such as project:Inbox)\n" - " to collect their new tasks that are then triaged on a regular basis\n" - " (such as in GTD methodology).\n" - " \n" - " In such a case, defining a report that filters for project:Inbox and making it\n" - " fully accessible from any context is a major usability improvement.", - "" - ); + NewsItem contextless_reports( + version, "Context-less reports", "", + " By default, every report is affected by currently active context.", + " You can now make a selected report ignore currently active context by setting\n" + " 'report..context' configuration variable to 0.", + "", + " This is useful for users who utilize a single place (such as project:Inbox)\n" + " to collect their new tasks that are then triaged on a regular basis\n" + " (such as in GTD methodology).\n" + " \n" + " In such a case, defining a report that filters for project:Inbox and making it\n" + " fully accessible from any context is a major usability improvement.", + ""); items.push_back(contextless_reports); ///////////////////////////////////////////////////////////////////////////// // - Exporting a particular report - NewsItem exportable_reports ( - version, - "Exporting a particular report", - "", - "", - " You can now export the tasks listed by a particular report as JSON by simply\n" - " calling 'task export '.\n", - " The export mirrors the filter and the sort order of the report.", - " This feature can be used to quickly process the data displayed in a particular\n" - " report using other CLI tools. For example, the following oneliner\n" - " \n" - " $ task export next | jq '.[].urgency' | datamash mean 1\n" - " 3.3455535142857\n" - " \n" - " combines jq and GNU datamash to compute average urgency of the tasks displayed\n" - " in the 'next' report.", - "" - ); + NewsItem exportable_reports( + version, "Exporting a particular report", "", "", + " You can now export the tasks listed by a particular report as JSON by simply\n" + " calling 'task export '.\n", + " The export mirrors the filter and the sort order of the report.", + " This feature can be used to quickly process the data displayed in a particular\n" + " report using other CLI tools. For example, the following oneliner\n" + " \n" + " $ task export next | jq '.[].urgency' | datamash mean 1\n" + " 3.3455535142857\n" + " \n" + " combines jq and GNU datamash to compute average urgency of the tasks displayed\n" + " in the 'next' report.", + ""); items.push_back(exportable_reports); ///////////////////////////////////////////////////////////////////////////// // - Multi-day holidays - NewsItem multi_holidays ( - version, - "Multi-day holidays", - "", - " Holidays are currently used in 'task calendar' to visualize the workload during\n" - " the upcoming weeks/months. Up to date country-specific holiday data files can be\n" - " obtained from our website, holidata.net.", - " Instead of single-day holiday entries only, Taskwarrior now supports holidays\n" - " that span a range of days (i.e. vacation).\n", - " Use a holiday..start and holiday..end to configure a multi-day holiday:\n" - " \n" - " holiday.sysadmin.name=System Administrator Appreciation Week\n" - " holiday.sysadmin.start=20100730\n" - " holiday.sysadmin.end=20100805", - "", - "" - ); + NewsItem multi_holidays( + version, "Multi-day holidays", "", + " Holidays are currently used in 'task calendar' to visualize the workload during\n" + " the upcoming weeks/months. Up to date country-specific holiday data files can be\n" + " obtained from our website, holidata.net.", + " Instead of single-day holiday entries only, Taskwarrior now supports holidays\n" + " that span a range of days (i.e. vacation).\n", + " Use a holiday..start and holiday..end to configure a multi-day holiday:\n" + " \n" + " holiday.sysadmin.name=System Administrator Appreciation Week\n" + " holiday.sysadmin.start=20100730\n" + " holiday.sysadmin.end=20100805", + "", ""); items.push_back(multi_holidays); ///////////////////////////////////////////////////////////////////////////// // - Unicode 12 - NewsItem unicode_12 ( - version, - "Extended Unicode support (Unicode 12)", - "", - "", - " The support for Unicode character set was improved to support Unicode 12.\n" - " This means better support for various language-specific characters - and emojis!", - "", - " Extended unicode support for language-specific characters helps non-English speakers.\n" - " While most users don't enter emojis as task metadata, automated task creation tools,\n" - " such as bugwarrior, might create tasks with exotic Unicode data.", - " You can try it out - 'task add Prepare for an 👽 invasion!'" - ); + NewsItem unicode_12( + version, "Extended Unicode support (Unicode 12)", "", "", + " The support for Unicode character set was improved to support Unicode 12.\n" + " This means better support for various language-specific characters - and emojis!", + "", + " Extended unicode support for language-specific characters helps non-English speakers.\n" + " While most users don't enter emojis as task metadata, automated task creation tools,\n" + " such as bugwarrior, might create tasks with exotic Unicode data.", + " You can try it out - 'task add Prepare for an 👽 invasion!'"); items.push_back(unicode_12); ///////////////////////////////////////////////////////////////////////////// // - The .by attribute modifier - NewsItem by_modifier ( - version, - "The .by attribute modifier", - "", - "", - " A new attribute modifier '.by' was introduced, equivalent to the operator '<='.\n", - " This modifier can be used to list all tasks due by the end of the months,\n" - " including the last day of the month, using: 'due.by:eom' query", - " There was no convenient way to express '<=' relation using attribute modifiers.\n" - " As a workaround, instead of 'due.by:eom' one could use 'due.before:eom+1d',\n" - " but that requires a certain amount of mental overhead.", - "" - ); + NewsItem by_modifier( + version, "The .by attribute modifier", "", "", + " A new attribute modifier '.by' was introduced, equivalent to the operator '<='.\n", + " This modifier can be used to list all tasks due by the end of the months,\n" + " including the last day of the month, using: 'due.by:eom' query", + " There was no convenient way to express '<=' relation using attribute modifiers.\n" + " As a workaround, instead of 'due.by:eom' one could use 'due.before:eom+1d',\n" + " but that requires a certain amount of mental overhead.", + ""); items.push_back(by_modifier); ///////////////////////////////////////////////////////////////////////////// // - Context-specific configuration overrides - NewsItem context_config ( - version, - "Context-specific configuration overrides", - "", - "", - " Any context can now define context-specific configuration overrides\n" - " via context..rc.=.\n", - " This allows the user to customize the behaviour of Taskwarrior in a given context,\n" - " for example, to change the default command in the 'work' context to 'overdue':\n" - "\n" - " $ task config context.work.rc.default.command overdue\n" - "\n" - " Another example would be to ensure that while context 'work' is active, tasks get\n" - " stored in a ~/.worktasks directory:\n" - "\n" - " $ task config context.work.rc.data.location=~/.worktasks", - "", - "" - ); + NewsItem context_config( + version, "Context-specific configuration overrides", "", "", + " Any context can now define context-specific configuration overrides\n" + " via context..rc.=.\n", + " This allows the user to customize the behaviour of Taskwarrior in a given context,\n" + " for example, to change the default command in the 'work' context to 'overdue':\n" + "\n" + " $ task config context.work.rc.default.command overdue\n" + "\n" + " Another example would be to ensure that while context 'work' is active, tasks get\n" + " stored in a ~/.worktasks directory:\n" + "\n" + " $ task config context.work.rc.data.location=~/.worktasks", + "", ""); items.push_back(context_config); ///////////////////////////////////////////////////////////////////////////// // - XDG config home support - NewsItem xdg_support ( - version, - "Support for XDG Base Directory Specification", - "", - " The XDG Base Directory specification provides standard locations to store\n" - " application data, configuration, state, and cached data in order to keep $HOME\n" - " clutter-free. The locations are usually set to ~/.local/share, ~/.config,\n" - " ~/.local/state and ~/.cache respectively.", - " If taskrc is not found at '~/.taskrc', Taskwarrior will attempt to find it\n" - " at '$XDG_CONFIG_HOME/task/taskrc' (defaults to '~/.config/task/taskrc').", - "", - " This allows users to fully follow XDG Base Directory Spec by moving their taskrc:\n" - " $ mkdir $XDG_CONFIG_HOME/task\n" - " $ mv ~/.taskrc $XDG_CONFIG_HOME/task/taskrc\n\n" - " and further setting:\n" - " data.location=$XDG_DATA_HOME/task/\n" - " hooks.location=$XDG_CONFIG_HOME/task/hooks/\n\n" - " Solutions in the past required symlinks or more cumbersome configuration overrides.", - " If you configure your data.location and hooks.location as above, ensure\n" - " that the XDG_DATA_HOME and XDG_CONFIG_HOME environment variables are set,\n" - " otherwise they're going to expand to empty string. Alternatively you can\n" - " hardcode the desired paths on your system." - ); + NewsItem xdg_support( + version, "Support for XDG Base Directory Specification", "", + " The XDG Base Directory specification provides standard locations to store\n" + " application data, configuration, state, and cached data in order to keep $HOME\n" + " clutter-free. The locations are usually set to ~/.local/share, ~/.config,\n" + " ~/.local/state and ~/.cache respectively.", + " If taskrc is not found at '~/.taskrc', Taskwarrior will attempt to find it\n" + " at '$XDG_CONFIG_HOME/task/taskrc' (defaults to '~/.config/task/taskrc').", + "", + " This allows users to fully follow XDG Base Directory Spec by moving their taskrc:\n" + " $ mkdir $XDG_CONFIG_HOME/task\n" + " $ mv ~/.taskrc $XDG_CONFIG_HOME/task/taskrc\n\n" + " and further setting:\n" + " data.location=$XDG_DATA_HOME/task/\n" + " hooks.location=$XDG_CONFIG_HOME/task/hooks/\n\n" + " Solutions in the past required symlinks or more cumbersome configuration overrides.", + " If you configure your data.location and hooks.location as above, ensure\n" + " that the XDG_DATA_HOME and XDG_CONFIG_HOME environment variables are set,\n" + " otherwise they're going to expand to empty string. Alternatively you can\n" + " hardcode the desired paths on your system."); items.push_back(xdg_support); ///////////////////////////////////////////////////////////////////////////// // - Update holiday data - NewsItem holidata_2022 ( - version, - "Updated holiday data for 2022", - "", - "", - " Holiday data has been refreshed for 2022 and five more holiday locales\n" - " have been added: fr-CA, hu-HU, pt-BR, sk-SK and sv-FI.", - "", - " Refreshing the holiday data is part of every release. The addition of the new\n" - " locales allows us to better support users in those particular countries." - ); + NewsItem holidata_2022( + version, "Updated holiday data for 2022", "", "", + " Holiday data has been refreshed for 2022 and five more holiday locales\n" + " have been added: fr-CA, hu-HU, pt-BR, sk-SK and sv-FI.", + "", + " Refreshing the holiday data is part of every release. The addition of the new\n" + " locales allows us to better support users in those particular countries."); items.push_back(holidata_2022); } -void NewsItem::version3_0_0 (std::vector& items) { +void NewsItem::version3_0_0(std::vector& items) { Version version("3.0.0"); - NewsItem sync { - version, - /*title=*/"New data model and sync backend", - /*bg_title=*/"", - /*background=*/"", - /*punchline=*/ - "The sync functionality for Taskwarrior has been rewritten entirely, and no longer\n" - "supports taskserver/taskd. The most robust solution is a cloud-storage backend,\n" - "although a less-mature taskchampion-sync-server is also available. See `task-sync(5)`\n" - "For details. As part of this change, the on-disk storage format has also changed.\n", - /*update=*/ - "This is a breaking upgrade: you must export your task database from 2.x and re-import\n" - "it into 3.x. Hooks run during task import, so if you have any hooks defined,\n" - "temporarily disable them for this operation.\n\n" - "See https://taskwarrior.org/docs/upgrade-3/ for information on upgrading to Taskwarrior 3.0.", + NewsItem sync{ + version, + /*title=*/"New data model and sync backend", + /*bg_title=*/"", + /*background=*/"", + /*punchline=*/ + "The sync functionality for Taskwarrior has been rewritten entirely, and no longer\n" + "supports taskserver/taskd. The most robust solution is a cloud-storage backend,\n" + "although a less-mature taskchampion-sync-server is also available. See `task-sync(5)`\n" + "For details. As part of this change, the on-disk storage format has also changed.\n", + /*update=*/ + "This is a breaking upgrade: you must export your task database from 2.x and re-import\n" + "it into 3.x. Hooks run during task import, so if you have any hooks defined,\n" + "temporarily disable them for this operation.\n\n" + "See https://taskwarrior.org/docs/upgrade-3/ for information on upgrading to Taskwarrior " + "3.0.", }; items.push_back(sync); } -void NewsItem::version3_1_0 (std::vector& items) { +void NewsItem::version3_1_0(std::vector& items) { Version version("3.1.0"); - NewsItem sync { - version, - /*title=*/"Purging Tasks, Manually or Automatically", - /*bg_title=*/"", - /*background=*/"", - /*punchline=*/ - "Support for `task purge` has been restored, and new support added for automatically expiring\n" - "old tasks.\n\n" - /*update=*/ - "The `task purge` command removes tasks entirely, in contrast to `task delete` which merely sets\n" - "the task status to 'Deleted'. This functionality existed in versions 2.x but was temporarily\n" - "removed in 3.0.\n\n" - "The new `purge.on-sync` configuration parameter controls automatic purging of old tasks.\n" - "An old task is one with status 'Deleted' that has not been modified in 180 days. This\n" - "functionality is optional and not enabled by default." - }; + NewsItem sync{ + version, + /*title=*/"Purging Tasks, Manually or Automatically", + /*bg_title=*/"", + /*background=*/"", + /*punchline=*/ + "Support for `task purge` has been restored, and new support added for automatically " + "expiring\n" + "old tasks.\n\n" + /*update=*/ + "The `task purge` command removes tasks entirely, in contrast to `task delete` which merely " + "sets\n" + "the task status to 'Deleted'. This functionality existed in versions 2.x but was " + "temporarily\n" + "removed in 3.0.\n\n" + "The new `purge.on-sync` configuration parameter controls automatic purging of old tasks.\n" + "An old task is one with status 'Deleted' that has not been modified in 180 days. This\n" + "functionality is optional and not enabled by default."}; items.push_back(sync); } //////////////////////////////////////////////////////////////////////////////// -int CmdNews::execute (std::string& output) -{ - auto words = Context::getContext ().cli2.getWords (); - auto config = Context::getContext ().config; +int CmdNews::execute(std::string& output) { + auto words = Context::getContext().cli2.getWords(); + auto config = Context::getContext().config; // Supress compiler warning about unused argument output = ""; std::vector items = NewsItem::all(); - Version news_version(Context::getContext ().config.get ("news.version")); + Version news_version(Context::getContext().config.get("news.version")); Version current_version = Version::Current(); // 2.6.0 is the earliest version with news support. - if (!news_version.is_valid()) - news_version = Version("2.6.0"); + if (!news_version.is_valid()) news_version = Version("2.6.0"); - signal (SIGINT, signal_handler); + signal(SIGINT, signal_handler); // Remove items that have already been shown - items.erase ( - std::remove_if (items.begin (), items.end (), [&](const NewsItem& n){return n._version <= news_version;}), - items.end () - ); + items.erase(std::remove_if(items.begin(), items.end(), + [&](const NewsItem& n) { return n._version <= news_version; }), + items.end()); - - Color bold = Color ("bold"); - if (items.empty ()) { - std::cout << bold.colorize ("You are up to date!\n"); + Color bold = Color("bold"); + if (items.empty()) { + std::cout << bold.colorize("You are up to date!\n"); } else { // Print release notes - std::cout << bold.colorize (format ( - "\n" - "================================================\n" - "Taskwarrior {1} through {2} Release Highlights\n" - "================================================\n", - news_version, - current_version)); + std::cout << bold.colorize( + format("\n" + "================================================\n" + "Taskwarrior {1} through {2} Release Highlights\n" + "================================================\n", + news_version, current_version)); - for (unsigned short i=0; i < items.size (); i++) { - std::cout << format ("\n({1}/{2}) ", i+1, items.size ()); - items[i].render (); + for (unsigned short i = 0; i < items.size(); i++) { + std::cout << format("\n({1}/{2}) ", i + 1, items.size()); + items[i].render(); } std::cout << "Thank you for catching up on the new features!\n"; } - wait_for_enter (); + wait_for_enter(); // Display outro Datetime now; - Datetime beginning (2006, 11, 29); - Duration development_time = Duration (now - beginning); + Datetime beginning(2006, 11, 29); + Duration development_time = Duration(now - beginning); - Color underline = Color ("underline"); + 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(development_time.days ()) / 365.25), - now.year () - ); + 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(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 (); + std::cout << outro.str(); // 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) { std::cout << "UPDATING\n"; - 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); + signal(SIGINT, SIG_DFL); - std::string question = format ( - "\nWould you like to open Taskwarrior {1} fundraising campaign to read more?", - now.year () - ); + std::string question = format( + "\nWould you like to open Taskwarrior {1} fundraising campaign to read more?", now.year()); - std::vector options {"yes", "no"}; - std::vector matches; + std::vector options{"yes", "no"}; + std::vector matches; std::cout << question << " (YES/no) "; std::string answer; - std::getline (std::cin, answer); + std::getline(std::cin, answer); - if (std::cin.eof () || trim (answer).empty ()) + if (std::cin.eof() || trim(answer).empty()) answer = "yes"; else - lowerCase (trim (answer)); + lowerCase(trim(answer)); - autoComplete (answer, options, matches, 1); // Hard-coded 1. + 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/'"); + 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/'"); + 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 + } else + wait_for_enter(); // Do not display the outro and footnote at once return 0; } diff --git a/src/commands/CmdNews.h b/src/commands/CmdNews.h index beee1af1e..7423954f7 100644 --- a/src/commands/CmdNews.h +++ b/src/commands/CmdNews.h @@ -27,14 +27,15 @@ #ifndef INCLUDED_CMDNEWS #define INCLUDED_CMDNEWS -#include -#include #include #include +#include #include +#include + class NewsItem { -public: + public: Version _version; std::string _title; std::string _bg_title; @@ -44,31 +45,23 @@ public: std::string _reasoning; std::string _actions; - void render (); + void render(); static std::vector all(); - static void version2_6_0 (std::vector&); - static void version3_0_0 (std::vector&); - static void version3_1_0 (std::vector&); + static void version2_6_0(std::vector&); + static void version3_0_0(std::vector&); + static void version3_1_0(std::vector&); -private: - NewsItem ( - Version, - const std::string&, - const std::string& = "", - const std::string& = "", - const std::string& = "", - const std::string& = "", - const std::string& = "", - const std::string& = "" - ); + private: + NewsItem(Version, const std::string&, const std::string& = "", const std::string& = "", + const std::string& = "", const std::string& = "", const std::string& = "", + const std::string& = ""); }; -class CmdNews : public Command -{ -public: - CmdNews (); - int execute (std::string&); +class CmdNews : public Command { + public: + CmdNews(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdPrepend.cpp b/src/commands/CmdPrepend.cpp index 25a307c49..79d67a881 100644 --- a/src/commands/CmdPrepend.cpp +++ b/src/commands/CmdPrepend.cpp @@ -28,111 +28,100 @@ // cmake.h include header must come first #include -#include #include #include -#include #include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -CmdPrepend::CmdPrepend () -{ - _keyword = "prepend"; - _usage = "task prepend "; - _description = "Prepends text to an existing task description"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = true; - _accepts_filter = true; +CmdPrepend::CmdPrepend() { + _keyword = "prepend"; + _usage = "task prepend "; + _description = "Prepends text to an existing task description"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdPrepend::execute (std::string&) -{ +int CmdPrepend::execute(std::string&) { int rc = 0; int count = 0; // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + std::vector filtered; + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // TODO Complain when no modifications are specified. // Accumulated project change notifications. - std::map projectChanges; + std::map projectChanges; - if(filtered.size() > 1) { + if (filtered.size() > 1) { feedback_affected("This command will alter {1} tasks.", filtered.size()); } - for (auto& task : filtered) - { - Task before (task); + for (auto& task : filtered) { + Task before(task); // Prepend to the specified task. - std::string question = format ("Prepend to task {1} '{2}'?", - task.identifier (true), - task.get ("description")); + std::string question = + format("Prepend to task {1} '{2}'?", task.identifier(true), task.get("description")); - task.modify (Task::modPrepend, true); + task.modify(Task::modPrepend, true); - if (permission (before.diff (task) + question, filtered.size ())) - { - Context::getContext ().tdb2.modify (task); + if (permission(before.diff(task) + question, filtered.size())) { + Context::getContext().tdb2.modify(task); ++count; - feedback_affected ("Prepending to task {1} '{2}'.", task); - if (Context::getContext ().verbose ("project")) - projectChanges[task.get ("project")] = onProjectChange (task, false); + feedback_affected("Prepending to task {1} '{2}'.", task); + if (Context::getContext().verbose("project")) + projectChanges[task.get("project")] = onProjectChange(task, false); // Prepend to siblings. - if (task.has ("parent")) - { - if ((Context::getContext ().config.get ("recurrence.confirmation") == "prompt" - && confirm ("This is a recurring task. Do you want to prepend to all pending recurrences of this same task?")) || - Context::getContext ().config.getBoolean ("recurrence.confirmation")) - { - std::vector siblings = Context::getContext ().tdb2.siblings (task); - for (auto& sibling : siblings) - { - sibling.modify (Task::modPrepend, true); - Context::getContext ().tdb2.modify (sibling); + if (task.has("parent")) { + if ((Context::getContext().config.get("recurrence.confirmation") == "prompt" && + confirm("This is a recurring task. Do you want to prepend to all pending recurrences " + "of this same task?")) || + Context::getContext().config.getBoolean("recurrence.confirmation")) { + std::vector siblings = Context::getContext().tdb2.siblings(task); + for (auto& sibling : siblings) { + sibling.modify(Task::modPrepend, true); + Context::getContext().tdb2.modify(sibling); ++count; - feedback_affected ("Prepending to recurring task {1} '{2}'.", sibling); + feedback_affected("Prepending to recurring task {1} '{2}'.", sibling); } // Prepend to the parent Task parent; - Context::getContext ().tdb2.get (task.get ("parent"), parent); - parent.modify (Task::modPrepend, true); - Context::getContext ().tdb2.modify (parent); + Context::getContext().tdb2.get(task.get("parent"), parent); + parent.modify(Task::modPrepend, true); + Context::getContext().tdb2.modify(parent); } } - } - else - { + } else { std::cout << "Task not prepended.\n"; rc = 1; - if (_permission_quit) - break; + if (_permission_quit) break; } } // Now list the project changes. for (auto& change : projectChanges) - if (change.first != "") - Context::getContext ().footnote (change.second); + if (change.first != "") Context::getContext().footnote(change.second); - feedback_affected (count == 1 ? "Prepended {1} task." : "Prepended {1} tasks.", count); + feedback_affected(count == 1 ? "Prepended {1} task." : "Prepended {1} tasks.", count); return rc; } diff --git a/src/commands/CmdPrepend.h b/src/commands/CmdPrepend.h index 2e07a6253..4fb499f96 100644 --- a/src/commands/CmdPrepend.h +++ b/src/commands/CmdPrepend.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDPREPEND #define INCLUDED_CMDPREPEND -#include #include -class CmdPrepend : public Command -{ -public: - CmdPrepend (); - int execute (std::string&); +#include + +class CmdPrepend : public Command { + public: + CmdPrepend(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdProjects.cpp b/src/commands/CmdProjects.cpp index 97d668f80..dc98f8af3 100644 --- a/src/commands/CmdProjects.cpp +++ b/src/commands/CmdProjects.cpp @@ -28,172 +28,148 @@ // cmake.h include header must come first #include -#include -#include #include #include #include #include -#include #include +#include + +#include #include +#include //////////////////////////////////////////////////////////////////////////////// -CmdProjects::CmdProjects () -{ - _keyword = "projects"; - _usage = "task projects"; - _description = "Shows all project names used"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = true; - _accepts_filter = true; +CmdProjects::CmdProjects() { + _keyword = "projects"; + _usage = "task projects"; + _description = "Shows all project names used"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::metadata; + _category = Command::Category::metadata; } //////////////////////////////////////////////////////////////////////////////// -int CmdProjects::execute (std::string& output) -{ +int CmdProjects::execute(std::string& output) { int rc = 0; // Get all the tasks. - handleUntil (); - handleRecurrence (); - auto tasks = Context::getContext ().tdb2.pending_tasks (); + handleUntil(); + handleRecurrence(); + auto tasks = Context::getContext().tdb2.pending_tasks(); - if (Context::getContext ().config.getBoolean ("list.all.projects")) - for (auto& task : Context::getContext ().tdb2.completed_tasks ()) - tasks.push_back (task); + if (Context::getContext().config.getBoolean("list.all.projects")) + for (auto& task : Context::getContext().tdb2.completed_tasks()) tasks.push_back(task); // Apply the filter. Filter filter; - std::vector filtered; - filter.subset (tasks, filtered); - int quantity = filtered.size (); + std::vector filtered; + filter.subset(tasks, filtered); + int quantity = filtered.size(); std::stringstream out; // Scan all the tasks for their project name, building a map using project // names as keys. - std::map unique; + std::map unique; bool no_project = false; std::string project; - for (auto& task : filtered) - { - if (task.getStatus () == Task::deleted) - { + for (auto& task : filtered) { + if (task.getStatus() == Task::deleted) { --quantity; continue; } // Increase the count for the project the task belongs to and all // its super-projects - project = task.get ("project"); + project = task.get("project"); - std::vector projects = extractParents (project); - projects.push_back (project); + std::vector projects = extractParents(project); + projects.push_back(project); - for (auto& parent : projects) - unique[parent] += 1; + for (auto& parent : projects) unique[parent] += 1; - if (project == "") - no_project = true; + if (project == "") no_project = true; } - if (unique.size ()) - { + if (unique.size()) { // Render a list of project names from the map. Table view; - view.width (Context::getContext ().getWidth ()); - view.add ("Project"); - view.add ("Tasks", false); - setHeaderUnderline (view); + view.width(Context::getContext().getWidth()); + view.add("Project"); + view.add("Tasks", false); + setHeaderUnderline(view); // create sorted list of table entries - std::list > sorted_view; - sort_projects (sorted_view, unique); + std::list> sorted_view; + sort_projects(sorted_view, unique); // construct view from sorted list - for (auto& item: sorted_view) - { - int row = view.addRow (); - view.set (row, 0, (item.first == "" - ? "(none)" - : indentProject (item.first, " ", '.'))); - view.set (row, 1, item.second); + for (auto& item : sorted_view) { + int row = view.addRow(); + view.set(row, 0, (item.first == "" ? "(none)" : indentProject(item.first, " ", '.'))); + view.set(row, 1, item.second); } - int number_projects = unique.size (); - if (no_project) - --number_projects; + int number_projects = unique.size(); + if (no_project) --number_projects; - out << optionalBlankLine () - << view.render () - << optionalBlankLine () - << (number_projects == 1 - ? format ("{1} project", number_projects) - : format ("{1} projects", number_projects)) - << ' ' - << (quantity == 1 - ? format ("({1} task)", quantity) - : format ("({1} tasks)", quantity)) + out << optionalBlankLine() << view.render() << optionalBlankLine() + << (number_projects == 1 ? format("{1} project", number_projects) + : format("{1} projects", number_projects)) + << ' ' << (quantity == 1 ? format("({1} task)", quantity) : format("({1} tasks)", quantity)) << '\n'; - } - else - { + } else { out << "No projects.\n"; rc = 1; } - output = out.str (); + output = out.str(); return rc; } //////////////////////////////////////////////////////////////////////////////// -CmdCompletionProjects::CmdCompletionProjects () -{ - _keyword = "_projects"; - _usage = "task _projects"; - _description = "Shows only a list of all project names used"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdCompletionProjects::CmdCompletionProjects() { + _keyword = "_projects"; + _usage = "task _projects"; + _description = "Shows only a list of all project names used"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionProjects::execute (std::string& output) -{ +int CmdCompletionProjects::execute(std::string& output) { // Get all the tasks. - handleUntil (); - handleRecurrence (); - auto tasks = Context::getContext ().tdb2.pending_tasks (); + handleUntil(); + handleRecurrence(); + auto tasks = Context::getContext().tdb2.pending_tasks(); - if (Context::getContext ().config.getBoolean ("list.all.projects")) - for (auto& task : Context::getContext ().tdb2.completed_tasks ()) - tasks.push_back (task); + if (Context::getContext().config.getBoolean("list.all.projects")) + for (auto& task : Context::getContext().tdb2.completed_tasks()) tasks.push_back(task); // Apply the filter. Filter filter; - std::vector filtered; - filter.subset (tasks, filtered); + std::vector filtered; + filter.subset(tasks, filtered); // Scan all the tasks for their project name, building a map using project // names as keys. - std::map unique; - for (auto& task : filtered) - unique[task.get ("project")] = 0; + std::map unique; + for (auto& task : filtered) unique[task.get("project")] = 0; for (auto& project : unique) - if (project.first.length ()) - output += project.first + '\n'; + if (project.first.length()) output += project.first + '\n'; return 0; } diff --git a/src/commands/CmdProjects.h b/src/commands/CmdProjects.h index 206d3a664..180db8f8b 100644 --- a/src/commands/CmdProjects.h +++ b/src/commands/CmdProjects.h @@ -27,21 +27,20 @@ #ifndef INCLUDED_CMDPROJECTS #define INCLUDED_CMDPROJECTS -#include #include -class CmdProjects : public Command -{ -public: - CmdProjects (); - int execute (std::string&); +#include + +class CmdProjects : public Command { + public: + CmdProjects(); + int execute(std::string&); }; -class CmdCompletionProjects : public Command -{ -public: - CmdCompletionProjects (); - int execute (std::string&); +class CmdCompletionProjects : public Command { + public: + CmdCompletionProjects(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdPurge.cpp b/src/commands/CmdPurge.cpp index cee795820..5bb309ddb 100644 --- a/src/commands/CmdPurge.cpp +++ b/src/commands/CmdPurge.cpp @@ -30,155 +30,138 @@ #include #include #include -#include #include +#include #include //////////////////////////////////////////////////////////////////////////////// -CmdPurge::CmdPurge () -{ - _keyword = "purge"; - _usage = "task purge"; - _description = "Removes the specified tasks from the data files. Causes permanent loss of data."; - _read_only = false; - _displays_id = false; - _needs_confirm = true; - _needs_gc = true; - _uses_context = true; - _accepts_filter = true; +CmdPurge::CmdPurge() { + _keyword = "purge"; + _usage = "task purge"; + _description = "Removes the specified tasks from the data files. Causes permanent loss of data."; + _read_only = false; + _displays_id = false; + _needs_confirm = true; + _needs_gc = true; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// // Purges the task, while taking care of: // - dependencies on this task // - child tasks -void CmdPurge::handleRelations (Task& task, std::vector& tasks) -{ - handleDeps (task); - handleChildren (task, tasks); +void CmdPurge::handleRelations(Task& task, std::vector& tasks) { + handleDeps(task); + handleChildren(task, tasks); tasks.push_back(task); } //////////////////////////////////////////////////////////////////////////////// // Makes sure that any task having the dependency on the task being purged // has that dependency removed, to preserve referential integrity. -void CmdPurge::handleDeps (Task& task) -{ - std::string uuid = task.get ("uuid"); +void CmdPurge::handleDeps(Task& task) { + std::string uuid = task.get("uuid"); - for (auto& blockedConst: Context::getContext ().tdb2.all_tasks ()) - { - Task& blocked = const_cast(blockedConst); - if (blocked.hasDependency (uuid)) - { - blocked.removeDependency (uuid); - Context::getContext ().tdb2.modify (blocked); - } - } + for (auto& blockedConst : Context::getContext().tdb2.all_tasks()) { + Task& blocked = const_cast(blockedConst); + if (blocked.hasDependency(uuid)) { + blocked.removeDependency(uuid); + Context::getContext().tdb2.modify(blocked); + } + } } //////////////////////////////////////////////////////////////////////////////// // Makes sure that with any recurrence parent are all the child tasks removed // as well. If user chooses not to, the whole command is aborted. -void CmdPurge::handleChildren (Task& task, std::vector& tasks) -{ +void CmdPurge::handleChildren(Task& task, std::vector& tasks) { // If this is not a recurrence parent, we have no job here - if (!task.has ("mask")) - return; + if (!task.has("mask")) return; - std::string uuid = task.get ("uuid"); + std::string uuid = task.get("uuid"); std::vector children; // Find all child tasks - for (auto& childConst: Context::getContext ().tdb2.all_tasks ()) - { - Task& child = const_cast (childConst); + for (auto& childConst : Context::getContext().tdb2.all_tasks()) { + Task& child = const_cast(childConst); - if (child.get ("parent") == uuid) - { - if (child.getStatus () != Task::deleted) + if (child.get("parent") == uuid) { + if (child.getStatus() != Task::deleted) // In case any child task is not deleted, bail out - throw format ("Task '{1}' is a recurrence template. Its child task {2} must be deleted before it can be purged.", - task.get ("description"), - child.identifier (true)); + throw format( + "Task '{1}' is a recurrence template. Its child task {2} must be deleted before it can " + "be purged.", + task.get("description"), child.identifier(true)); else - children.push_back (child); + children.push_back(child); } } // If there are no children, our job is done - if (children.empty ()) - return; + if (children.empty()) return; // Ask for confirmation to purge them, if needed - std::string question = format ("Task '{1}' is a recurrence template. All its {2} deleted children tasks will be purged as well. Continue?", - task.get ("description"), - children.size ()); + std::string question = format( + "Task '{1}' is a recurrence template. All its {2} deleted children tasks will be purged as " + "well. Continue?", + task.get("description"), children.size()); - if (Context::getContext ().config.getBoolean ("recurrence.confirmation") || - (Context::getContext ().config.get ("recurrence.confirmation") == "prompt" - && confirm (question))) - { - for (auto& child: children) - handleRelations (child, tasks); - } - else - throw std::string ("Purge operation aborted."); + if (Context::getContext().config.getBoolean("recurrence.confirmation") || + (Context::getContext().config.get("recurrence.confirmation") == "prompt" && + confirm(question))) { + for (auto& child : children) handleRelations(child, tasks); + } else + throw std::string("Purge operation aborted."); } - //////////////////////////////////////////////////////////////////////////////// -int CmdPurge::execute (std::string&) -{ +int CmdPurge::execute(std::string&) { int rc = 0; std::vector tasks; bool matched_deleted = false; Filter filter; - std::vector filtered; + std::vector filtered; // Apply filter. - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } - for (auto& task : filtered) - { + for (auto& task : filtered) { // Allow purging of deleted tasks only. Hence no need to deal with: // - unblocked tasks notifications (deleted tasks are not blocking) // - project changes (deleted tasks not included in progress) // It also has the nice property of being explicit - users need to // mark tasks as deleted before purging. - if (task.getStatus () == Task::deleted) - { + if (task.getStatus() == Task::deleted) { // Mark that at least one deleted task matched the filter matched_deleted = true; std::string question; - question = format ("Permanently remove task {1} '{2}'?", - task.identifier (true), - task.get ("description")); + question = format("Permanently remove task {1} '{2}'?", task.identifier(true), + task.get("description")); - if (permission (question, filtered.size ())) - handleRelations (task, tasks); + if (permission(question, filtered.size())) handleRelations(task, tasks); } } // Now that any exceptions are handled, actually purge the tasks. - for (auto& task: tasks) { - Context::getContext ().tdb2.purge (task); + for (auto& task : tasks) { + Context::getContext().tdb2.purge(task); } - if (filtered.size () > 0 and ! matched_deleted) - Context::getContext ().footnote ("No deleted tasks specified. Maybe you forgot to delete tasks first?"); + if (filtered.size() > 0 and !matched_deleted) + Context::getContext().footnote( + "No deleted tasks specified. Maybe you forgot to delete tasks first?"); - feedback_affected (tasks.size() == 1 ? "Purged {1} task." : "Purged {1} tasks.", tasks.size()); + feedback_affected(tasks.size() == 1 ? "Purged {1} task." : "Purged {1} tasks.", tasks.size()); return rc; } diff --git a/src/commands/CmdPurge.h b/src/commands/CmdPurge.h index c97fbf970..f46137745 100644 --- a/src/commands/CmdPurge.h +++ b/src/commands/CmdPurge.h @@ -27,19 +27,20 @@ #ifndef INCLUDED_CMDPURGE #define INCLUDED_CMDPURGE -#include -#include #include -class CmdPurge : public Command -{ -private: - void handleRelations (Task& task, std::vector& tasks); - void handleChildren (Task& task, std::vector& tasks); - void handleDeps (Task& task); -public: - CmdPurge (); - int execute (std::string&); +#include +#include + +class CmdPurge : public Command { + private: + void handleRelations(Task& task, std::vector& tasks); + void handleChildren(Task& task, std::vector& tasks); + void handleDeps(Task& task); + + public: + CmdPurge(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdReports.cpp b/src/commands/CmdReports.cpp index e8bfa80dc..1b2d83eed 100644 --- a/src/commands/CmdReports.cpp +++ b/src/commands/CmdReports.cpp @@ -28,81 +28,74 @@ // cmake.h include header must come first #include -#include #include #include #include #include +#include + //////////////////////////////////////////////////////////////////////////////// -CmdReports::CmdReports () -{ - _keyword = "reports"; - _usage = "task reports"; - _description = "Lists all supported reports"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdReports::CmdReports() { + _keyword = "reports"; + _usage = "task reports"; + _description = "Lists all supported reports"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::config; + _category = Command::Category::config; } //////////////////////////////////////////////////////////////////////////////// -int CmdReports::execute (std::string& output) -{ - std::vector reports; +int CmdReports::execute(std::string& output) { + std::vector reports; // Add custom reports. - for (auto& i : Context::getContext ().config) - { - if (i.first.substr (0, 7) == "report.") - { - std::string report = i.first.substr (7); - auto columns = report.find (".columns"); - if (columns != std::string::npos) - reports.push_back (report.substr (0, columns)); + for (auto& i : Context::getContext().config) { + if (i.first.substr(0, 7) == "report.") { + std::string report = i.first.substr(7); + auto columns = report.find(".columns"); + if (columns != std::string::npos) reports.push_back(report.substr(0, columns)); } } // Add known reports. - reports.push_back ("burndown.daily"); - reports.push_back ("burndown.monthly"); - reports.push_back ("burndown.weekly"); - reports.push_back ("ghistory.annual"); - reports.push_back ("ghistory.monthly"); - reports.push_back ("history.annual"); - reports.push_back ("history.monthly"); - reports.push_back ("information"); - reports.push_back ("projects"); - reports.push_back ("summary"); - reports.push_back ("tags"); + reports.push_back("burndown.daily"); + reports.push_back("burndown.monthly"); + reports.push_back("burndown.weekly"); + reports.push_back("ghistory.annual"); + reports.push_back("ghistory.monthly"); + reports.push_back("history.annual"); + reports.push_back("history.monthly"); + reports.push_back("information"); + reports.push_back("projects"); + reports.push_back("summary"); + reports.push_back("tags"); - std::sort (reports.begin (), reports.end ()); + std::sort(reports.begin(), reports.end()); // Compose the output. std::stringstream out; Table view; - view.width (Context::getContext ().getWidth ()); - view.add ("Report"); - view.add ("Description"); - setHeaderUnderline (view); + view.width(Context::getContext().getWidth()); + view.add("Report"); + view.add("Description"); + setHeaderUnderline(view); - for (auto& report : reports) - { - int row = view.addRow (); - view.set (row, 0, report); - view.set (row, 1, Context::getContext ().commands[report]->description ()); + for (auto& report : reports) { + int row = view.addRow(); + view.set(row, 0, report); + view.set(row, 1, Context::getContext().commands[report]->description()); } - out << optionalBlankLine () - << view.render () - << optionalBlankLine () - << format ("{1} reports\n", reports.size ()); + out << optionalBlankLine() << view.render() << optionalBlankLine() + << format("{1} reports\n", reports.size()); - output = out.str (); + output = out.str(); return 0; } diff --git a/src/commands/CmdReports.h b/src/commands/CmdReports.h index 0dc74e4fc..893558d3a 100644 --- a/src/commands/CmdReports.h +++ b/src/commands/CmdReports.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDREPORTS #define INCLUDED_CMDREPORTS -#include #include -class CmdReports : public Command -{ -public: - CmdReports (); - int execute (std::string&); +#include + +class CmdReports : public Command { + public: + CmdReports(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index 4bb797d71..bacc5c464 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -28,257 +28,245 @@ // cmake.h include header must come first #include -#include -#include -#include -#include -#include #include #include #include +#include +#include #include +#include +#include +#include + #define STRING_CMD_SHOW_DIFFER_COLOR "These are highlighted in {1} above." -#define STRING_CMD_SHOW_CONFIG_ERROR "Configuration error: {1} contains an unrecognized value '{2}'." +#define STRING_CMD_SHOW_CONFIG_ERROR \ + "Configuration error: {1} contains an unrecognized value '{2}'." extern std::string configurationDefaults; //////////////////////////////////////////////////////////////////////////////// -CmdShow::CmdShow () -{ - _keyword = "show"; - _usage = "task show [all | substring]"; - _description = "Shows all configuration variables or subset"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdShow::CmdShow() { + _keyword = "show"; + _usage = "task show [all | substring]"; + _description = "Shows all configuration variables or subset"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::config; + _category = Command::Category::config; } //////////////////////////////////////////////////////////////////////////////// -int CmdShow::execute (std::string& output) -{ +int CmdShow::execute(std::string& output) { int rc = 0; std::stringstream out; // Obtain the arguments from the description. That way, things like '--' // have already been handled. - std::vector words = Context::getContext ().cli2.getWords (); - if (words.size () > 1) - throw std::string ("You can only specify 'all' or a search string."); + std::vector words = Context::getContext().cli2.getWords(); + if (words.size() > 1) throw std::string("You can only specify 'all' or a search string."); - int width = Context::getContext ().getWidth (); + int width = Context::getContext().getWidth(); // Complain about configuration variables that are not recognized. // These are the regular configuration variables. // Note that there is a leading and trailing space, to make it easier to // search for whole words. std::string recognized = - " abbreviation.minimum" - " active.indicator" - " allow.empty.filter" - " avoidlastcolumn" - " bulk" - " calendar.details" - " calendar.details.report" - " calendar.holidays" - " calendar.legend" - " calendar.monthsperline" - " calendar.offset" - " calendar.offset.value" - " color" - " color.active" - " color.alternate" - " color.blocked" - " color.blocking" - " color.burndown.done" - " color.burndown.pending" - " color.burndown.started" - " color.calendar.due" - " color.calendar.due.today" - " color.calendar.holiday" - " color.calendar.scheduled" - " color.calendar.overdue" - " color.calendar.today" - " color.calendar.weekend" - " color.calendar.weeknumber" - " color.completed" - " color.debug" - " color.deleted" - " color.due" - " color.due.today" - " color.warning" - " color.error" - " color.footnote" - " color.header" - " color.history.add" - " color.history.delete" - " color.history.done" - " color.label" - " color.label.sort" - " color.overdue" - " color.recurring" - " color.scheduled" - " color.summary.background" - " color.summary.bar" - " color.sync.added" - " color.sync.changed" - " color.sync.rejected" - " color.tagged" - " color.undo.after" - " color.undo.before" - " color.until" - " column.padding" - " complete.all.tags" - " confirmation" - " context" - " data.location" - " dateformat" - " dateformat.annotation" - " dateformat.edit" - " dateformat.holiday" - " dateformat.info" - " dateformat.report" - " date.iso" - " debug" - " debug.hooks" - " debug.parser" - " default.command" - " default.due" - " default.project" - " default.scheduled" - " defaultheight" - " defaultwidth" - " default.timesheet.filter" - " dependency.confirmation" - " dependency.indicator" - " dependency.reminder" - " detection" - " displayweeknumber" - " due" - " editor" - " exit.on.missing.db" - " purge.on-sync" - " expressions" - " fontunderline" - " gc" - " hooks" - " hooks.location" - " hyphenate" - " indent.annotation" - " indent.report" - " journal.info" - " journal.time" - " journal.time.start.annotation" - " journal.time.stop.annotation" - " json.array" - " limit" - " list.all.projects" - " list.all.tags" - " nag" - " news.version" - " obfuscate" - " print.empty.columns" - " recurrence" - " recurrence.confirmation" - " recurrence.indicator" - " recurrence.limit" - " regex" - " reserved.lines" - " row.padding" - " rule.color.merge" - " rule.precedence.color" - " search.case.sensitive" - " sugar" - " summary.all.projects" - " sync.local.server_dir" - " sync.gcp.credential_path" - " sync.gcp.bucket" - " sync.server.client_id" - " sync.encryption_secret" - " sync.server.url" - " sync.server.origin" - " tag.indicator" - " undo.style" - " urgency.active.coefficient" - " urgency.scheduled.coefficient" - " urgency.annotations.coefficient" - " urgency.blocked.coefficient" - " urgency.blocking.coefficient" - " urgency.due.coefficient" - " urgency.project.coefficient" - " urgency.tags.coefficient" - " urgency.waiting.coefficient" - " urgency.age.coefficient" - " urgency.age.max" - " urgency.inherit" - " verbose" - " weekstart" - " xterm.title" - " "; + " abbreviation.minimum" + " active.indicator" + " allow.empty.filter" + " avoidlastcolumn" + " bulk" + " calendar.details" + " calendar.details.report" + " calendar.holidays" + " calendar.legend" + " calendar.monthsperline" + " calendar.offset" + " calendar.offset.value" + " color" + " color.active" + " color.alternate" + " color.blocked" + " color.blocking" + " color.burndown.done" + " color.burndown.pending" + " color.burndown.started" + " color.calendar.due" + " color.calendar.due.today" + " color.calendar.holiday" + " color.calendar.scheduled" + " color.calendar.overdue" + " color.calendar.today" + " color.calendar.weekend" + " color.calendar.weeknumber" + " color.completed" + " color.debug" + " color.deleted" + " color.due" + " color.due.today" + " color.warning" + " color.error" + " color.footnote" + " color.header" + " color.history.add" + " color.history.delete" + " color.history.done" + " color.label" + " color.label.sort" + " color.overdue" + " color.recurring" + " color.scheduled" + " color.summary.background" + " color.summary.bar" + " color.sync.added" + " color.sync.changed" + " color.sync.rejected" + " color.tagged" + " color.undo.after" + " color.undo.before" + " color.until" + " column.padding" + " complete.all.tags" + " confirmation" + " context" + " data.location" + " dateformat" + " dateformat.annotation" + " dateformat.edit" + " dateformat.holiday" + " dateformat.info" + " dateformat.report" + " date.iso" + " debug" + " debug.hooks" + " debug.parser" + " default.command" + " default.due" + " default.project" + " default.scheduled" + " defaultheight" + " defaultwidth" + " default.timesheet.filter" + " dependency.confirmation" + " dependency.indicator" + " dependency.reminder" + " detection" + " displayweeknumber" + " due" + " editor" + " exit.on.missing.db" + " purge.on-sync" + " expressions" + " fontunderline" + " gc" + " hooks" + " hooks.location" + " hyphenate" + " indent.annotation" + " indent.report" + " journal.info" + " journal.time" + " journal.time.start.annotation" + " journal.time.stop.annotation" + " json.array" + " limit" + " list.all.projects" + " list.all.tags" + " nag" + " news.version" + " obfuscate" + " print.empty.columns" + " recurrence" + " recurrence.confirmation" + " recurrence.indicator" + " recurrence.limit" + " regex" + " reserved.lines" + " row.padding" + " rule.color.merge" + " rule.precedence.color" + " search.case.sensitive" + " sugar" + " summary.all.projects" + " sync.local.server_dir" + " sync.gcp.credential_path" + " sync.gcp.bucket" + " sync.server.client_id" + " sync.encryption_secret" + " sync.server.url" + " sync.server.origin" + " tag.indicator" + " undo.style" + " urgency.active.coefficient" + " urgency.scheduled.coefficient" + " urgency.annotations.coefficient" + " urgency.blocked.coefficient" + " urgency.blocking.coefficient" + " urgency.due.coefficient" + " urgency.project.coefficient" + " urgency.tags.coefficient" + " urgency.waiting.coefficient" + " urgency.age.coefficient" + " urgency.age.max" + " urgency.inherit" + " verbose" + " weekstart" + " xterm.title" + " "; // This configuration variable is supported, but not documented. It exists // so that unit tests can force color to be on even when the output from task // is redirected to a file, or stdout is not a tty. recognized += "_forcecolor "; - std::vector unrecognized; - for (auto& i : Context::getContext ().config) - { + std::vector unrecognized; + for (auto& i : Context::getContext().config) { // Disallow partial matches by tacking a leading and trailing space on each // variable name. std::string pattern = ' ' + i.first + ' '; - if (recognized.find (pattern) == std::string::npos) - { + if (recognized.find(pattern) == std::string::npos) { // These are special configuration variables, because their name is // dynamic. - if (i.first.substr (0, 14) != "color.keyword." && - i.first.substr (0, 14) != "color.project." && - i.first.substr (0, 10) != "color.tag." && - i.first.substr (0, 10) != "color.uda." && - i.first.substr (0, 8) != "context." && - i.first.substr (0, 8) != "holiday." && - i.first.substr (0, 7) != "report." && - i.first.substr (0, 6) != "alias." && - i.first.substr (0, 5) != "hook." && - i.first.substr (0, 4) != "uda." && - i.first.substr (0, 8) != "default." && - i.first.substr (0, 21) != "urgency.user.project." && - i.first.substr (0, 17) != "urgency.user.tag." && - i.first.substr (0, 21) != "urgency.user.keyword." && - i.first.substr (0, 12) != "urgency.uda.") - { - unrecognized.push_back (i.first); + if (i.first.substr(0, 14) != "color.keyword." && i.first.substr(0, 14) != "color.project." && + i.first.substr(0, 10) != "color.tag." && i.first.substr(0, 10) != "color.uda." && + i.first.substr(0, 8) != "context." && i.first.substr(0, 8) != "holiday." && + i.first.substr(0, 7) != "report." && i.first.substr(0, 6) != "alias." && + i.first.substr(0, 5) != "hook." && i.first.substr(0, 4) != "uda." && + i.first.substr(0, 8) != "default." && i.first.substr(0, 21) != "urgency.user.project." && + i.first.substr(0, 17) != "urgency.user.tag." && + i.first.substr(0, 21) != "urgency.user.keyword." && + i.first.substr(0, 12) != "urgency.uda.") { + unrecognized.push_back(i.first); } } } // Find all the values that match the defaults, for highlighting. - std::vector default_values; + std::vector default_values; Configuration default_config; - default_config.parse (configurationDefaults); + default_config.parse(configurationDefaults); - for (auto& i : Context::getContext ().config) - if (i.second != default_config.get (i.first)) - default_values.push_back (i.first); + for (auto& i : Context::getContext().config) + if (i.second != default_config.get(i.first)) default_values.push_back(i.first); // Create output view. Table view; - view.width (width); - view.add ("Config Variable"); - view.add ("Value"); - setHeaderUnderline (view); + view.width(width); + view.add("Config Variable"); + view.add("Value"); + setHeaderUnderline(view); Color error; Color warning; - if (Context::getContext ().color ()) - { - error = Color (Context::getContext ().config.get ("color.error")); - warning = Color (Context::getContext ().config.get ("color.warning")); + if (Context::getContext().color()) { + error = Color(Context::getContext().config.get("color.error")); + warning = Color(Context::getContext().config.get("color.warning")); } bool issue_error = false; @@ -287,144 +275,119 @@ int CmdShow::execute (std::string& output) std::string section; // Look for the first plausible argument which could be a pattern - if (words.size ()) - section = words[0]; + if (words.size()) section = words[0]; - if (section == "all") - section = ""; + if (section == "all") section = ""; std::string::size_type loc; - for (auto& i : Context::getContext ().config) - { - loc = i.first.find (section, 0); - if (loc != std::string::npos) - { + for (auto& i : Context::getContext().config) { + loc = i.first.find(section, 0); + if (loc != std::string::npos) { // Look for unrecognized. Color color; - if (std::find (unrecognized.begin (), unrecognized.end (), i.first) != unrecognized.end ()) - { + if (std::find(unrecognized.begin(), unrecognized.end(), i.first) != unrecognized.end()) { issue_error = true; color = error; - } - else if (std::find (default_values.begin (), default_values.end (), i.first) != default_values.end ()) - { + } else if (std::find(default_values.begin(), default_values.end(), i.first) != + default_values.end()) { issue_warning = true; color = warning; } std::string value = i.second; - int row = view.addRow (); - view.set (row, 0, i.first, color); - view.set (row, 1, value, color); + int row = view.addRow(); + view.set(row, 0, i.first, color); + view.set(row, 1, value, color); - if (default_config[i.first] != value && - default_config[i.first] != "") - { - row = view.addRow (); - view.set (row, 0, std::string (" ") + "Default value", color); - view.set (row, 1, default_config[i.first], color); + if (default_config[i.first] != value && default_config[i.first] != "") { + row = view.addRow(); + view.set(row, 0, std::string(" ") + "Default value", color); + view.set(row, 1, default_config[i.first], color); } } } out << '\n' - << view.render () - << (view.rows () == 0 ? "No matching configuration variables." : "") - << (view.rows () == 0 ? "\n\n" : "\n"); + << view.render() << (view.rows() == 0 ? "No matching configuration variables." : "") + << (view.rows() == 0 ? "\n\n" : "\n"); - if (issue_warning) - { + if (issue_warning) { out << "Some of your .taskrc variables differ from the default values.\n"; - if (Context::getContext ().color () && warning.nontrivial ()) - out << " " - << format (STRING_CMD_SHOW_DIFFER_COLOR, warning.colorize ("color")) - << "\n\n"; + if (Context::getContext().color() && warning.nontrivial()) + out << " " << format(STRING_CMD_SHOW_DIFFER_COLOR, warning.colorize("color")) << "\n\n"; } // Display the unrecognized variables. - if (issue_error) - { + if (issue_error) { out << "Your .taskrc file contains these unrecognized variables:\n"; - for (auto& i : unrecognized) - out << " " << i << '\n'; + for (auto& i : unrecognized) out << " " << i << '\n'; - if (Context::getContext ().color () && error.nontrivial ()) - out << '\n' << format (STRING_CMD_SHOW_DIFFER_COLOR, error.colorize ("color")); + if (Context::getContext().color() && error.nontrivial()) + out << '\n' << format(STRING_CMD_SHOW_DIFFER_COLOR, error.colorize("color")); out << "\n\n"; } - out << legacyCheckForDeprecatedVariables (); - out << legacyCheckForDeprecatedColumns (); + out << legacyCheckForDeprecatedVariables(); + out << legacyCheckForDeprecatedColumns(); // TODO Check for referenced but missing theme files. // TODO Check for referenced but missing string files. // Check for bad values in rc.calendar.details. - std::string calendardetails = Context::getContext ().config.get ("calendar.details"); - if (calendardetails != "full" && - calendardetails != "sparse" && - calendardetails != "none") - out << format (STRING_CMD_SHOW_CONFIG_ERROR, "calendar.details", calendardetails) - << '\n'; + std::string calendardetails = Context::getContext().config.get("calendar.details"); + if (calendardetails != "full" && calendardetails != "sparse" && calendardetails != "none") + out << format(STRING_CMD_SHOW_CONFIG_ERROR, "calendar.details", calendardetails) << '\n'; // Check for bad values in rc.calendar.holidays. - std::string calendarholidays = Context::getContext ().config.get ("calendar.holidays"); - if (calendarholidays != "full" && - calendarholidays != "sparse" && - calendarholidays != "none") - out << format (STRING_CMD_SHOW_CONFIG_ERROR, "calendar.holidays", calendarholidays) - << '\n'; + std::string calendarholidays = Context::getContext().config.get("calendar.holidays"); + if (calendarholidays != "full" && calendarholidays != "sparse" && calendarholidays != "none") + out << format(STRING_CMD_SHOW_CONFIG_ERROR, "calendar.holidays", calendarholidays) << '\n'; // Verify installation. This is mentioned in the documentation as the way // to ensure everything is properly installed. - if (Context::getContext ().config.size () == 0) - { + if (Context::getContext().config.size() == 0) { out << "Configuration error: .taskrc contains no entries.\n"; rc = 1; - } - else - { - Directory location (Context::getContext ().config.get ("data.location")); + } else { + Directory location(Context::getContext().config.get("data.location")); if (location._data == "") out << "Configuration error: data.location not specified in .taskrc file.\n"; - if (! location.exists ()) - out << "Configuration error: data.location contains a directory name that doesn't exist, or is unreadable.\n"; + if (!location.exists()) + out << "Configuration error: data.location contains a directory name that doesn't exist, or " + "is unreadable.\n"; } - output = out.str (); + output = out.str(); return rc; } //////////////////////////////////////////////////////////////////////////////// -CmdShowRaw::CmdShowRaw () -{ - _keyword = "_show"; - _usage = "task _show"; +CmdShowRaw::CmdShowRaw() { + _keyword = "_show"; + _usage = "task _show"; _description = "Shows all configuration settings in a machine-readable format"; - _read_only = true; + _read_only = true; _displays_id = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdShowRaw::execute (std::string& output) -{ +int CmdShowRaw::execute(std::string& output) { // Get all the settings and sort alphabetically by name. - auto all = Context::getContext ().config.all (); - std::sort (all.begin (), all.end ()); + auto all = Context::getContext().config.all(); + std::sort(all.begin(), all.end()); // Display them all. std::stringstream out; - for (auto& i : all) - out << i << '=' << Context::getContext ().config.get (i) << '\n'; + for (auto& i : all) out << i << '=' << Context::getContext().config.get(i) << '\n'; - output = out.str (); + output = out.str(); return 0; } diff --git a/src/commands/CmdShow.h b/src/commands/CmdShow.h index 2bee52518..cc3f064fd 100644 --- a/src/commands/CmdShow.h +++ b/src/commands/CmdShow.h @@ -27,21 +27,20 @@ #ifndef INCLUDED_CMDSHOW #define INCLUDED_CMDSHOW -#include #include -class CmdShow : public Command -{ -public: - CmdShow (); - int execute (std::string&); +#include + +class CmdShow : public Command { + public: + CmdShow(); + int execute(std::string&); }; -class CmdShowRaw : public Command -{ -public: - CmdShowRaw (); - int execute (std::string&); +class CmdShowRaw : public Command { + public: + CmdShowRaw(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdStart.cpp b/src/commands/CmdStart.cpp index 1566e1bfb..10a5c9e5c 100644 --- a/src/commands/CmdStart.cpp +++ b/src/commands/CmdStart.cpp @@ -28,115 +28,100 @@ // cmake.h include header must come first #include -#include #include #include -#include #include +#include #include +#include + //////////////////////////////////////////////////////////////////////////////// -CmdStart::CmdStart () -{ - _keyword = "start"; - _usage = "task start "; - _description = "Marks specified task as started"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = true; - _accepts_filter = true; +CmdStart::CmdStart() { + _keyword = "start"; + _usage = "task start "; + _description = "Marks specified task as started"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdStart::execute (std::string&) -{ +int CmdStart::execute(std::string&) { int rc = 0; int count = 0; // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + std::vector filtered; + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // Accumulated project change notifications. - std::map projectChanges; + std::map projectChanges; - if(filtered.size() > 1) { + if (filtered.size() > 1) { feedback_affected("This command will alter {1} tasks.", filtered.size()); } - std::vector modified; - for (auto& task : filtered) - { - if (! task.has ("start")) - { - Task before (task); + std::vector modified; + for (auto& task : filtered) { + if (!task.has("start")) { + Task before(task); // Start the specified task. - std::string question = format ("Start task {1} '{2}'?", - task.identifier (true), - task.get ("description")); - task.setAsNow ("start"); + std::string question = + format("Start task {1} '{2}'?", task.identifier(true), task.get("description")); + task.setAsNow("start"); - Task::status status = task.getStatus (); - if (status == Task::completed || status == Task::deleted) - { + Task::status status = task.getStatus(); + if (status == Task::completed || status == Task::deleted) { // "waiting" handled by Task::validate(), no special care needed here. - task.setStatus (Task::pending); + task.setStatus(Task::pending); } - task.modify (Task::modAnnotate); - if (Context::getContext ().config.getBoolean ("journal.time")) - task.addAnnotation (Context::getContext ().config.get ("journal.time.start.annotation")); + task.modify(Task::modAnnotate); + if (Context::getContext().config.getBoolean("journal.time")) + task.addAnnotation(Context::getContext().config.get("journal.time.start.annotation")); - if (permission (before.diff (task) + question, filtered.size ())) - { - updateRecurrenceMask (task); - Context::getContext ().tdb2.modify (task); + if (permission(before.diff(task) + question, filtered.size())) { + updateRecurrenceMask(task); + Context::getContext().tdb2.modify(task); ++count; - feedback_affected ("Starting task {1} '{2}'.", task); - dependencyChainOnStart (task); - if (Context::getContext ().verbose ("project")) - projectChanges[task.get ("project")] = onProjectChange (task, false); + feedback_affected("Starting task {1} '{2}'.", task); + dependencyChainOnStart(task); + if (Context::getContext().verbose("project")) + projectChanges[task.get("project")] = onProjectChange(task, false); // Save unmodified task for potential nagging later modified.push_back(before); - } - else - { + } else { std::cout << "Task not started.\n"; rc = 1; - if (_permission_quit) - break; + if (_permission_quit) break; } - } - else - { - std::cout << format ("Task {1} '{2}' already started.", - task.id, - task.get ("description")) + } else { + std::cout << format("Task {1} '{2}' already started.", task.id, task.get("description")) << '\n'; rc = 1; } } - nag (modified); + nag(modified); // Now list the project changes. for (auto& change : projectChanges) - if (change.first != "") - Context::getContext ().footnote (change.second); + if (change.first != "") Context::getContext().footnote(change.second); - feedback_affected (count == 1 ? "Started {1} task." : "Started {1} tasks.", count); + feedback_affected(count == 1 ? "Started {1} task." : "Started {1} tasks.", count); return rc; } diff --git a/src/commands/CmdStart.h b/src/commands/CmdStart.h index 81951de17..2c16f46d8 100644 --- a/src/commands/CmdStart.h +++ b/src/commands/CmdStart.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDSTART #define INCLUDED_CMDSTART -#include #include -class CmdStart : public Command -{ -public: - CmdStart (); - int execute (std::string&); +#include + +class CmdStart : public Command { + public: + CmdStart(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdStats.cpp b/src/commands/CmdStats.cpp index be658bc5f..3450d8f11 100644 --- a/src/commands/CmdStats.cpp +++ b/src/commands/CmdStats.cpp @@ -28,256 +28,248 @@ // cmake.h include header must come first #include -#include -#include -#include -#include +#include #include #include -#include #include -#include +#include #include +#include +#include #include +#include +#include + //////////////////////////////////////////////////////////////////////////////// -CmdStats::CmdStats () -{ - _keyword = "stats"; - _usage = "task stats"; - _description = "Shows task database statistics"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = true; - _accepts_filter = true; +CmdStats::CmdStats() { + _keyword = "stats"; + _usage = "task stats"; + _description = "Shows task database statistics"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::metadata; + _category = Command::Category::metadata; } //////////////////////////////////////////////////////////////////////////////// -int CmdStats::execute (std::string& output) -{ +int CmdStats::execute(std::string& output) { int rc = 0; std::stringstream out; - std::string dateformat = Context::getContext ().config.get ("dateformat"); + std::string dateformat = Context::getContext().config.get("dateformat"); // Count the possible reverts. - int undoCount = Context::getContext ().tdb2.num_reverts_possible (); + int undoCount = Context::getContext().tdb2.num_reverts_possible(); // Count the backlog transactions. - int numLocalChanges = Context::getContext ().tdb2.num_local_changes (); + int numLocalChanges = Context::getContext().tdb2.num_local_changes(); // Get all the tasks. Filter filter; - std::vector all = Context::getContext ().tdb2.all_tasks (); - std::vector filtered; - filter.subset (all, filtered); + std::vector all = Context::getContext().tdb2.all_tasks(); + std::vector filtered; + filter.subset(all, filtered); Datetime now; - time_t earliest = time (nullptr); - time_t latest = 1; - int totalT = 0; - int deletedT = 0; - int pendingT = 0; - int completedT = 0; - int waitingT = 0; - int taggedT = 0; - int annotationsT = 0; - int recurringT = 0; - int blockingT = 0; - int blockedT = 0; + time_t earliest = time(nullptr); + time_t latest = 1; + int totalT = 0; + int deletedT = 0; + int pendingT = 0; + int completedT = 0; + int waitingT = 0; + int taggedT = 0; + int annotationsT = 0; + int recurringT = 0; + int blockingT = 0; + int blockedT = 0; float daysPending = 0.0; - int descLength = 0; - std::map allTags; - std::map allProjects; + int descLength = 0; + std::map allTags; + std::map allProjects; - for (auto& task : filtered) - { + for (auto& task : filtered) { ++totalT; - Task::status status = task.getStatus (); - switch (status) - { - case Task::deleted: ++deletedT; break; - case Task::pending: ++pendingT; break; - case Task::completed: ++completedT; break; - case Task::recurring: ++recurringT; break; - case Task::waiting: ++waitingT; break; + Task::status status = task.getStatus(); + switch (status) { + case Task::deleted: + ++deletedT; + break; + case Task::pending: + ++pendingT; + break; + case Task::completed: + ++completedT; + break; + case Task::recurring: + ++recurringT; + break; + case Task::waiting: + ++waitingT; + break; } - if (task.is_blocked) ++blockedT; + if (task.is_blocked) ++blockedT; if (task.is_blocking) ++blockingT; - time_t entry = strtoll (task.get ("entry").c_str (), nullptr, 10); + time_t entry = strtoll(task.get("entry").c_str(), nullptr, 10); if (entry < earliest) earliest = entry; - if (entry > latest) latest = entry; + if (entry > latest) latest = entry; - if (status == Task::completed) - { - time_t end = strtoll (task.get ("end").c_str (), nullptr, 10); + if (status == Task::completed) { + time_t end = strtoll(task.get("end").c_str(), nullptr, 10); daysPending += (end - entry) / 86400.0; } - if (status == Task::pending) - daysPending += (now.toEpoch () - entry) / 86400.0; + if (status == Task::pending) daysPending += (now.toEpoch() - entry) / 86400.0; - descLength += task.get ("description").length (); - annotationsT += task.getAnnotations ().size (); + descLength += task.get("description").length(); + annotationsT += task.getAnnotations().size(); - auto tags = task.getTags (); - if (tags.size ()) - ++taggedT; + auto tags = task.getTags(); + if (tags.size()) ++taggedT; - for (auto& tag : tags) - allTags[tag] = 0; + for (auto& tag : tags) allTags[tag] = 0; - std::string project = task.get ("project"); - if (project != "") - allProjects[project] = 0; + std::string project = task.get("project"); + if (project != "") allProjects[project] = 0; } // Create a table for output. Table view; - view.width (Context::getContext ().getWidth ()); - view.intraPadding (2); - view.add ("Category"); - view.add ("Data"); - setHeaderUnderline (view); + view.width(Context::getContext().getWidth()); + view.intraPadding(2); + view.add("Category"); + view.add("Data"); + setHeaderUnderline(view); - int row = view.addRow (); - view.set (row, 0, "Pending"); - view.set (row, 1, pendingT); + int row = view.addRow(); + view.set(row, 0, "Pending"); + view.set(row, 1, pendingT); - row = view.addRow (); - view.set (row, 0, "Waiting"); - view.set (row, 1, waitingT); + row = view.addRow(); + view.set(row, 0, "Waiting"); + view.set(row, 1, waitingT); - row = view.addRow (); - view.set (row, 0, "Recurring"); - view.set (row, 1, recurringT); + row = view.addRow(); + view.set(row, 0, "Recurring"); + view.set(row, 1, recurringT); - row = view.addRow (); - view.set (row, 0, "Completed"); - view.set (row, 1, completedT); + row = view.addRow(); + view.set(row, 0, "Completed"); + view.set(row, 1, completedT); - row = view.addRow (); - view.set (row, 0, "Deleted"); - view.set (row, 1, deletedT); + row = view.addRow(); + view.set(row, 0, "Deleted"); + view.set(row, 1, deletedT); - row = view.addRow (); - view.set (row, 0, "Total"); - view.set (row, 1, totalT); + row = view.addRow(); + view.set(row, 0, "Total"); + view.set(row, 1, totalT); - row = view.addRow (); - view.set (row, 0, "Annotations"); - view.set (row, 1, annotationsT); + row = view.addRow(); + view.set(row, 0, "Annotations"); + view.set(row, 1, annotationsT); - row = view.addRow (); - view.set (row, 0, "Unique tags"); - view.set (row, 1, (int)allTags.size ()); + row = view.addRow(); + view.set(row, 0, "Unique tags"); + view.set(row, 1, (int)allTags.size()); - row = view.addRow (); - view.set (row, 0, "Projects"); - view.set (row, 1, (int)allProjects.size ()); + row = view.addRow(); + view.set(row, 0, "Projects"); + view.set(row, 1, (int)allProjects.size()); - row = view.addRow (); - view.set (row, 0, "Blocked tasks"); - view.set (row, 1, blockedT); + row = view.addRow(); + view.set(row, 0, "Blocked tasks"); + view.set(row, 1, blockedT); - row = view.addRow (); - view.set (row, 0, "Blocking tasks"); - view.set (row, 1, blockingT); + row = view.addRow(); + view.set(row, 0, "Blocking tasks"); + view.set(row, 1, blockingT); - row = view.addRow (); - view.set (row, 0, "Undo transactions"); - view.set (row, 1, undoCount); + row = view.addRow(); + view.set(row, 0, "Undo transactions"); + view.set(row, 1, undoCount); - row = view.addRow (); - view.set (row, 0, "Sync backlog transactions"); - view.set (row, 1, numLocalChanges); + row = view.addRow(); + view.set(row, 0, "Sync backlog transactions"); + view.set(row, 1, numLocalChanges); - if (totalT) - { - row = view.addRow (); - view.set (row, 0, "Tasks tagged"); + if (totalT) { + row = view.addRow(); + view.set(row, 0, "Tasks tagged"); std::stringstream value; - value << std::setprecision (3) << (100.0 * taggedT / totalT) << '%'; - view.set (row, 1, value.str ()); + value << std::setprecision(3) << (100.0 * taggedT / totalT) << '%'; + view.set(row, 1, value.str()); } - if (filtered.size ()) - { - Datetime e (earliest); - row = view.addRow (); - view.set (row, 0, "Oldest task"); - view.set (row, 1, e.toString (dateformat)); + if (filtered.size()) { + Datetime e(earliest); + row = view.addRow(); + view.set(row, 0, "Oldest task"); + view.set(row, 1, e.toString(dateformat)); - Datetime l (latest); - row = view.addRow (); - view.set (row, 0, "Newest task"); - view.set (row, 1, l.toString (dateformat)); + Datetime l(latest); + row = view.addRow(); + view.set(row, 0, "Newest task"); + view.set(row, 1, l.toString(dateformat)); - row = view.addRow (); - view.set (row, 0, "Task used for"); - view.set (row, 1, Duration (latest - earliest).formatVague ()); + row = view.addRow(); + view.set(row, 0, "Task used for"); + view.set(row, 1, Duration(latest - earliest).formatVague()); } - if (totalT) - { - row = view.addRow (); - view.set (row, 0, "Task added every"); - view.set (row, 1, Duration (((latest - earliest) / totalT)).formatVague ()); + if (totalT) { + row = view.addRow(); + view.set(row, 0, "Task added every"); + view.set(row, 1, Duration(((latest - earliest) / totalT)).formatVague()); } - if (completedT) - { - row = view.addRow (); - view.set (row, 0, "Task completed every"); - view.set (row, 1, Duration ((latest - earliest) / completedT).formatVague ()); + if (completedT) { + row = view.addRow(); + view.set(row, 0, "Task completed every"); + view.set(row, 1, Duration((latest - earliest) / completedT).formatVague()); } - if (deletedT) - { - row = view.addRow (); - view.set (row, 0, "Task deleted every"); - view.set (row, 1, Duration ((latest - earliest) / deletedT).formatVague ()); + if (deletedT) { + row = view.addRow(); + view.set(row, 0, "Task deleted every"); + view.set(row, 1, Duration((latest - earliest) / deletedT).formatVague()); } - if (pendingT || completedT) - { - row = view.addRow (); - view.set (row, 0, "Average time pending"); - view.set (row, 1, Duration ((int) ((daysPending / (pendingT + completedT)) * 86400)).formatVague ()); + if (pendingT || completedT) { + row = view.addRow(); + view.set(row, 0, "Average time pending"); + view.set(row, 1, + Duration((int)((daysPending / (pendingT + completedT)) * 86400)).formatVague()); } - if (totalT) - { - row = view.addRow (); - view.set (row, 0, "Average desc length"); - view.set (row, 1, format ("{1} characters", (int) (descLength / totalT))); + if (totalT) { + row = view.addRow(); + view.set(row, 0, "Average desc length"); + view.set(row, 1, format("{1} characters", (int)(descLength / totalT))); } // If an alternating row color is specified, notify the table. - if (Context::getContext ().color ()) - { - Color alternate (Context::getContext ().config.get ("color.alternate")); - if (alternate.nontrivial ()) - { - view.colorOdd (alternate); - view.intraColorOdd (alternate); - view.extraColorOdd (alternate); + if (Context::getContext().color()) { + Color alternate(Context::getContext().config.get("color.alternate")); + if (alternate.nontrivial()) { + view.colorOdd(alternate); + view.intraColorOdd(alternate); + view.extraColorOdd(alternate); } } - out << optionalBlankLine () - << view.render () - << optionalBlankLine (); + out << optionalBlankLine() << view.render() << optionalBlankLine(); - output = out.str (); + output = out.str(); return rc; } diff --git a/src/commands/CmdStats.h b/src/commands/CmdStats.h index 2d384c434..b62cbdfd0 100644 --- a/src/commands/CmdStats.h +++ b/src/commands/CmdStats.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDSTATS #define INCLUDED_CMDSTATS -#include #include -class CmdStats : public Command -{ -public: - CmdStats (); - int execute (std::string&); +#include + +class CmdStats : public Command { + public: + CmdStats(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdStop.cpp b/src/commands/CmdStop.cpp index a46b5840d..2698a8436 100644 --- a/src/commands/CmdStop.cpp +++ b/src/commands/CmdStop.cpp @@ -28,90 +28,78 @@ // cmake.h include header must come first #include -#include #include #include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -CmdStop::CmdStop () -{ - _keyword = "stop"; - _usage = "task stop "; - _description = "Removes the 'start' time from a task"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = true; - _accepts_filter = true; +CmdStop::CmdStop() { + _keyword = "stop"; + _usage = "task stop "; + _description = "Removes the 'start' time from a task"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = true; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdStop::execute (std::string&) -{ +int CmdStop::execute(std::string&) { int rc = 0; int count = 0; // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + std::vector filtered; + filter.subset(filtered); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // Accumulated project change notifications. - std::map projectChanges; + std::map projectChanges; - if(filtered.size() > 1) { + if (filtered.size() > 1) { feedback_affected("This command will alter {1} tasks.", filtered.size()); } - for (auto& task : filtered) - { - if (task.has ("start")) - { - Task before (task); + for (auto& task : filtered) { + if (task.has("start")) { + Task before(task); // Stop the specified task. - std::string question = format ("Stop task {1} '{2}'?", - task.identifier (true), - task.get ("description")); + std::string question = + format("Stop task {1} '{2}'?", task.identifier(true), task.get("description")); - task.modify (Task::modAnnotate); - task.remove ("start"); + task.modify(Task::modAnnotate); + task.remove("start"); - if (Context::getContext ().config.getBoolean ("journal.time")) - task.addAnnotation (Context::getContext ().config.get ("journal.time.stop.annotation")); + if (Context::getContext().config.getBoolean("journal.time")) + task.addAnnotation(Context::getContext().config.get("journal.time.stop.annotation")); - if (permission (before.diff (task) + question, filtered.size ())) - { - updateRecurrenceMask (task); - Context::getContext ().tdb2.modify (task); + if (permission(before.diff(task) + question, filtered.size())) { + updateRecurrenceMask(task); + Context::getContext().tdb2.modify(task); ++count; - feedback_affected ("Stopping task {1} '{2}'.", task); - dependencyChainOnStart (task); - if (Context::getContext ().verbose ("project")) - projectChanges[task.get ("project")] = onProjectChange (task, false); - } - else - { + feedback_affected("Stopping task {1} '{2}'.", task); + dependencyChainOnStart(task); + if (Context::getContext().verbose("project")) + projectChanges[task.get("project")] = onProjectChange(task, false); + } else { std::cout << "Task not stopped.\n"; rc = 1; - if (_permission_quit) - break; + if (_permission_quit) break; } - } - else - { - std::cout << format ("Task {1} '{2}' not started.", - task.identifier (true), - task.get ("description")) + } else { + std::cout << format("Task {1} '{2}' not started.", task.identifier(true), + task.get("description")) << '\n'; rc = 1; } @@ -119,10 +107,9 @@ int CmdStop::execute (std::string&) // Now list the project changes. for (auto& change : projectChanges) - if (change.first != "") - Context::getContext ().footnote (change.second); + if (change.first != "") Context::getContext().footnote(change.second); - feedback_affected (count == 1 ? "Stopped {1} task." : "Stopped {1} tasks.", count); + feedback_affected(count == 1 ? "Stopped {1} task." : "Stopped {1} tasks.", count); return rc; } diff --git a/src/commands/CmdStop.h b/src/commands/CmdStop.h index fb28a444c..695a4c25c 100644 --- a/src/commands/CmdStop.h +++ b/src/commands/CmdStop.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDSTOP #define INCLUDED_CMDSTOP -#include #include -class CmdStop : public Command -{ -public: - CmdStop (); - int execute (std::string&); +#include + +class CmdStop : public Command { + public: + CmdStop(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdSummary.cpp b/src/commands/CmdSummary.cpp index 5564a8829..76a83a29b 100644 --- a/src/commands/CmdSummary.cpp +++ b/src/commands/CmdSummary.cpp @@ -28,186 +28,160 @@ // cmake.h include header must come first #include -#include -#include -#include #include +#include #include #include -#include #include -#include #include +#include +#include + +#include #include +#include //////////////////////////////////////////////////////////////////////////////// -CmdSummary::CmdSummary () -{ - _keyword = "summary"; - _usage = "task summary"; - _description = "Shows a report of task status by project"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = true; - _accepts_filter = true; +CmdSummary::CmdSummary() { + _keyword = "summary"; + _usage = "task summary"; + _description = "Shows a report of task status by project"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::graphs; + _category = Command::Category::graphs; } //////////////////////////////////////////////////////////////////////////////// // Project Remaining Avg Age Complete 0% 100% // A 12 13d 55% XXXXXXXXXXXXX----------- // B 109 3d 12h 10% XXX--------------------- -int CmdSummary::execute (std::string& output) -{ +int CmdSummary::execute(std::string& output) { int rc = 0; - bool showAllProjects = Context::getContext ().config.getBoolean ("summary.all.projects"); + bool showAllProjects = Context::getContext().config.getBoolean("summary.all.projects"); // Apply filter. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); // Generate unique list of project names from all pending tasks. - std::map allProjects; + std::map allProjects; for (auto& task : filtered) - if (showAllProjects || task.getStatus () == Task::pending) - allProjects[task.get ("project")] = false; + if (showAllProjects || task.getStatus() == Task::pending) + allProjects[task.get("project")] = false; // Initialize counts, sum. - std::map countPending; - std::map countCompleted; - std::map sumEntry; - std::map counter; - time_t now = time (nullptr); + std::map countPending; + std::map countCompleted; + std::map sumEntry; + std::map counter; + time_t now = time(nullptr); // Initialize counters. - for (auto& project : allProjects) - { - countPending [project.first] = 0; - countCompleted [project.first] = 0; - sumEntry [project.first] = 0.0; - counter [project.first] = 0; + for (auto& project : allProjects) { + countPending[project.first] = 0; + countCompleted[project.first] = 0; + sumEntry[project.first] = 0.0; + counter[project.first] = 0; } // Count the various tasks. - for (auto& task : filtered) - { - std::string project = task.get ("project"); - std::vector projects = extractParents (project); - projects.push_back (project); + for (auto& task : filtered) { + std::string project = task.get("project"); + std::vector projects = extractParents(project); + projects.push_back(project); - for (auto& parent : projects) - ++counter[parent]; + for (auto& parent : projects) ++counter[parent]; - if (task.getStatus () == Task::pending || - task.getStatus () == Task::waiting) - { - for (auto& parent : projects) - { + if (task.getStatus() == Task::pending || task.getStatus() == Task::waiting) { + for (auto& parent : projects) { ++countPending[parent]; - time_t entry = strtoll (task.get ("entry").c_str (), nullptr, 10); - if (entry) - sumEntry[parent] = sumEntry[parent] + (double) (now - entry); + time_t entry = strtoll(task.get("entry").c_str(), nullptr, 10); + if (entry) sumEntry[parent] = sumEntry[parent] + (double)(now - entry); } } - else if (task.getStatus () == Task::completed) - { - for (auto& parent : projects) - { + else if (task.getStatus() == Task::completed) { + for (auto& parent : projects) { ++countCompleted[parent]; - time_t entry = strtoll (task.get ("entry").c_str (), nullptr, 10); - time_t end = strtoll (task.get ("end").c_str (), nullptr, 10); - if (entry && end) - sumEntry[parent] = sumEntry[parent] + (double) (end - entry); + time_t entry = strtoll(task.get("entry").c_str(), nullptr, 10); + time_t end = strtoll(task.get("end").c_str(), nullptr, 10); + if (entry && end) sumEntry[parent] = sumEntry[parent] + (double)(end - entry); } } } // Create a table for output. Table view; - view.width (Context::getContext ().getWidth ()); - view.add ("Project"); - view.add ("Remaining", false); - view.add ("Avg age", false); - view.add ("Complete", false); - view.add ("0% 100%", true, false); - setHeaderUnderline (view); + view.width(Context::getContext().getWidth()); + view.add("Project"); + view.add("Remaining", false); + view.add("Avg age", false); + view.add("Complete", false); + view.add("0% 100%", true, false); + setHeaderUnderline(view); Color bar_color; Color bg_color; - if (Context::getContext ().color ()) - { - bar_color = Color (Context::getContext ().config.get ("color.summary.bar")); - bg_color = Color (Context::getContext ().config.get ("color.summary.background")); + if (Context::getContext().color()) { + bar_color = Color(Context::getContext().config.get("color.summary.bar")); + bg_color = Color(Context::getContext().config.get("color.summary.background")); } // sort projects into sorted list std::list> sortedProjects; - sort_projects (sortedProjects, allProjects); + sort_projects(sortedProjects, allProjects); int barWidth = 30; // construct view from sorted list - for (auto& i : sortedProjects) - { - int row = view.addRow (); - view.set (row, 0, (i.first == "" - ? "(none)" - : indentProject (i.first, " ", '.'))); + for (auto& i : sortedProjects) { + int row = view.addRow(); + view.set(row, 0, (i.first == "" ? "(none)" : indentProject(i.first, " ", '.'))); - view.set (row, 1, countPending[i.first]); - if (counter[i.first]) - view.set (row, 2, Duration ((int) (sumEntry[i.first] / (double)counter[i.first])).formatVague ()); + view.set(row, 1, countPending[i.first]); + if (counter[i.first]) + view.set(row, 2, Duration((int)(sumEntry[i.first] / (double)counter[i.first])).formatVague()); - int c = countCompleted[i.first]; - int p = countPending[i.first]; - int completedBar = 0; - if (c + p) - completedBar = (c * barWidth) / (c + p); + int c = countCompleted[i.first]; + int p = countPending[i.first]; + int completedBar = 0; + if (c + p) completedBar = (c * barWidth) / (c + p); - std::string bar; - std::string subbar; - if (Context::getContext ().color ()) - { - bar += bar_color.colorize (std::string ( completedBar, ' ')); - bar += bg_color.colorize (std::string (barWidth - completedBar, ' ')); - } - else - { - bar += std::string ( completedBar, '=') - + std::string (barWidth - completedBar, ' '); - } - view.set (row, 4, bar); + std::string bar; + std::string subbar; + if (Context::getContext().color()) { + bar += bar_color.colorize(std::string(completedBar, ' ')); + bar += bg_color.colorize(std::string(barWidth - completedBar, ' ')); + } else { + bar += std::string(completedBar, '=') + std::string(barWidth - completedBar, ' '); + } + view.set(row, 4, bar); - char percent[12] = "0%"; - if (c + p) - snprintf (percent, 12, "%d%%", 100 * c / (c + p)); - view.set (row, 3, percent); + char percent[12] = "0%"; + if (c + p) snprintf(percent, 12, "%d%%", 100 * c / (c + p)); + view.set(row, 3, percent); } std::stringstream out; - if (view.rows ()) - { - out << optionalBlankLine () - << view.render () - << optionalBlankLine (); + if (view.rows()) { + out << optionalBlankLine() << view.render() << optionalBlankLine(); - out << format ("{1} projects\n", view.rows ()); - } - else - { + out << format("{1} projects\n", view.rows()); + } else { out << "No projects.\n"; rc = 1; } - output = out.str (); + output = out.str(); return rc; } diff --git a/src/commands/CmdSummary.h b/src/commands/CmdSummary.h index 012a062b9..abfe521a5 100644 --- a/src/commands/CmdSummary.h +++ b/src/commands/CmdSummary.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDSUMMARY #define INCLUDED_CMDSUMMARY -#include #include -class CmdSummary : public Command -{ -public: - CmdSummary (); - int execute (std::string&); +#include + +class CmdSummary : public Command { + public: + CmdSummary(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index 3a6dc8144..6245b5acc 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -28,74 +28,74 @@ // cmake.h include header must come first #include -#include -#include -#include +#include #include #include -#include -#include #include +#include +#include +#include #include + +#include + #include "tc/Server.h" //////////////////////////////////////////////////////////////////////////////// -CmdSync::CmdSync () -{ - _keyword = "synchronize"; - _usage = "task synchronize [initialize]"; - _description = "Synchronizes data with the Taskserver"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdSync::CmdSync() { + _keyword = "synchronize"; + _usage = "task synchronize [initialize]"; + _description = "Synchronizes data with the Taskserver"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::migration; + _category = Command::Category::migration; } //////////////////////////////////////////////////////////////////////////////// -int CmdSync::execute (std::string& output) -{ +int CmdSync::execute(std::string& output) { int status = 0; tc::Server server; std::string server_ident; // If no server is set up, quit. - std::string origin = Context::getContext ().config.get ("sync.server.origin"); - std::string url = Context::getContext ().config.get ("sync.server.url"); - std::string server_dir = Context::getContext ().config.get ("sync.local.server_dir"); - std::string gcp_credential_path = Context::getContext ().config.get ("sync.gcp.credential_path"); - std::string gcp_bucket = Context::getContext ().config.get ("sync.gcp.bucket"); - std::string encryption_secret = Context::getContext ().config.get ("sync.encryption_secret"); + std::string origin = Context::getContext().config.get("sync.server.origin"); + std::string url = Context::getContext().config.get("sync.server.url"); + std::string server_dir = Context::getContext().config.get("sync.local.server_dir"); + std::string gcp_credential_path = Context::getContext().config.get("sync.gcp.credential_path"); + std::string gcp_bucket = Context::getContext().config.get("sync.gcp.bucket"); + std::string encryption_secret = Context::getContext().config.get("sync.encryption_secret"); // sync.server.origin is a deprecated synonym for sync.server.url std::string server_url = url == "" ? origin : url; if (server_dir != "") { - server = tc::Server::new_local (server_dir); + server = tc::Server::new_local(server_dir); server_ident = server_dir; } else if (gcp_bucket != "") { if (encryption_secret == "") { - throw std::string ("sync.encryption_secret is required"); + throw std::string("sync.encryption_secret is required"); } - server = tc::Server::new_gcp (gcp_bucket, gcp_credential_path, encryption_secret); + server = tc::Server::new_gcp(gcp_bucket, gcp_credential_path, encryption_secret); std::ostringstream os; os << "GCP bucket " << gcp_bucket; server_ident = os.str(); } else if (server_url != "") { - std::string client_id = Context::getContext ().config.get ("sync.server.client_id"); + std::string client_id = Context::getContext().config.get("sync.server.client_id"); if (client_id == "" || encryption_secret == "") { - 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"); } - server = tc::Server::new_sync (server_url, client_id, encryption_secret); + server = tc::Server::new_sync(server_url, client_id, encryption_secret); std::ostringstream os; os << "Sync server at " << server_url; server_ident = os.str(); } else { - throw std::string ("No sync.* settings are configured. See task-sync(5)."); + throw std::string("No sync.* settings are configured. See task-sync(5)."); } std::stringstream out; @@ -103,19 +103,18 @@ int CmdSync::execute (std::string& output) out << "sync.server.origin is deprecated. Use sync.server.url instead.\n"; } - if (Context::getContext ().verbose ("sync")) { - out << format ("Syncing with {1}", server_ident) - << '\n'; + if (Context::getContext().verbose("sync")) { + out << format("Syncing with {1}", server_ident) << '\n'; } - Context &context = Context::getContext (); + Context& context = Context::getContext(); context.tdb2.sync(std::move(server), false); - if (context.config.getBoolean ("purge.on-sync")) { - context.tdb2.expire_tasks (); + if (context.config.getBoolean("purge.on-sync")) { + context.tdb2.expire_tasks(); } - output = out.str (); + output = out.str(); return status; } diff --git a/src/commands/CmdSync.h b/src/commands/CmdSync.h index b823a04f9..8f0bbbba4 100644 --- a/src/commands/CmdSync.h +++ b/src/commands/CmdSync.h @@ -27,15 +27,15 @@ #ifndef INCLUDED_CMDSYNC #define INCLUDED_CMDSYNC -#include #include #include -class CmdSync : public Command -{ -public: - CmdSync (); - int execute (std::string&); +#include + +class CmdSync : public Command { + public: + CmdSync(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdTags.cpp b/src/commands/CmdTags.cpp index 9d43d4040..a0317d4fb 100644 --- a/src/commands/CmdTags.cpp +++ b/src/commands/CmdTags.cpp @@ -28,200 +28,184 @@ // cmake.h include header must come first #include -#include -#include -#include #include #include #include #include +#include #include +#include +#include + //////////////////////////////////////////////////////////////////////////////// -CmdTags::CmdTags () -{ - _keyword = "tags"; - _usage = "task tags"; - _description = "Shows a list of all tags used"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = true; - _accepts_filter = true; +CmdTags::CmdTags() { + _keyword = "tags"; + _usage = "task tags"; + _description = "Shows a list of all tags used"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = true; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::metadata; + _category = Command::Category::metadata; } //////////////////////////////////////////////////////////////////////////////// -int CmdTags::execute (std::string& output) -{ +int CmdTags::execute(std::string& output) { int rc = 0; std::stringstream out; // Get all the tasks. - auto tasks = Context::getContext ().tdb2.pending_tasks (); + auto tasks = Context::getContext().tdb2.pending_tasks(); - if (Context::getContext ().config.getBoolean ("list.all.tags")) - for (auto& task : Context::getContext ().tdb2.completed_tasks ()) - tasks.push_back (task); + if (Context::getContext().config.getBoolean("list.all.tags")) + for (auto& task : Context::getContext().tdb2.completed_tasks()) tasks.push_back(task); - int quantity = tasks.size (); + int quantity = tasks.size(); // Apply filter. Filter filter; - std::vector filtered; - filter.subset (tasks, filtered); + std::vector filtered; + filter.subset(tasks, filtered); // Scan all the tasks for their project name, building a map using project // names as keys. - std::map unique; - for (auto& task : filtered) - { - for (auto& tag : task.getTags ()) - if (unique.find (tag) != unique.end ()) + std::map unique; + for (auto& task : filtered) { + for (auto& tag : task.getTags()) + if (unique.find(tag) != unique.end()) unique[tag]++; else unique[tag] = 1; } - if (unique.size ()) - { + if (unique.size()) { // Render a list of tags names from the map. Table view; - view.width (Context::getContext ().getWidth ()); - view.add ("Tag"); - view.add ("Count", false); - setHeaderUnderline (view); + view.width(Context::getContext().getWidth()); + view.add("Tag"); + view.add("Count", false); + setHeaderUnderline(view); Color bold; - if (Context::getContext ().color ()) - bold = Color ("bold"); + if (Context::getContext().color()) bold = Color("bold"); bool special = false; - for (auto& i : unique) - { + for (auto& i : unique) { // Highlight the special tags. - special = (Context::getContext ().color () && - (i.first == "nocolor" || - i.first == "nonag" || - i.first == "nocal" || - i.first == "next")) ? true : false; + special = (Context::getContext().color() && (i.first == "nocolor" || i.first == "nonag" || + i.first == "nocal" || i.first == "next")) + ? true + : false; - int row = view.addRow (); - view.set (row, 0, i.first, special ? bold : Color ()); - view.set (row, 1, i.second, special ? bold : Color ()); + int row = view.addRow(); + view.set(row, 0, i.first, special ? bold : Color()); + view.set(row, 1, i.second, special ? bold : Color()); } - out << optionalBlankLine () - << view.render () - << optionalBlankLine (); + out << optionalBlankLine() << view.render() << optionalBlankLine(); - if (unique.size () == 1) - Context::getContext ().footnote ("1 tag"); + if (unique.size() == 1) + Context::getContext().footnote("1 tag"); else - Context::getContext ().footnote (format ("{1} tags", unique.size ())); + Context::getContext().footnote(format("{1} tags", unique.size())); if (quantity == 1) - Context::getContext ().footnote ("(1 task)"); + Context::getContext().footnote("(1 task)"); else - Context::getContext ().footnote (format ("({1} tasks)", quantity)); + Context::getContext().footnote(format("({1} tasks)", quantity)); out << '\n'; - } - else - { - Context::getContext ().footnote ("No tags."); + } else { + Context::getContext().footnote("No tags."); rc = 1; } - output = out.str (); + output = out.str(); return rc; } //////////////////////////////////////////////////////////////////////////////// -CmdCompletionTags::CmdCompletionTags () -{ - _keyword = "_tags"; - _usage = "task _tags"; - _description = "Shows only a list of all tags used, for autocompletion purposes"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdCompletionTags::CmdCompletionTags() { + _keyword = "_tags"; + _usage = "task _tags"; + _description = "Shows only a list of all tags used, for autocompletion purposes"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionTags::execute (std::string& output) -{ +int CmdCompletionTags::execute(std::string& output) { // Get all the tasks. - auto tasks = Context::getContext ().tdb2.pending_tasks (); + auto tasks = Context::getContext().tdb2.pending_tasks(); - if (Context::getContext ().config.getBoolean ("complete.all.tags")) - for (auto& task : Context::getContext ().tdb2.completed_tasks ()) - tasks.push_back (task); + if (Context::getContext().config.getBoolean("complete.all.tags")) + for (auto& task : Context::getContext().tdb2.completed_tasks()) tasks.push_back(task); // Apply filter. Filter filter; - std::vector filtered; - filter.subset (tasks, filtered); + std::vector filtered; + filter.subset(tasks, filtered); // Scan all the tasks for their tags, building a map using tag // names as keys. - std::map unique; + std::map unique; for (auto& task : filtered) - for (auto& tag : task.getTags ()) - unique[tag] = 0; + for (auto& tag : task.getTags()) unique[tag] = 0; // Add built-in tags to map. - unique["nocolor"] = 0; - unique["nonag"] = 0; - unique["nocal"] = 0; - unique["next"] = 0; - unique["ACTIVE"] = 0; + unique["nocolor"] = 0; + unique["nonag"] = 0; + unique["nocal"] = 0; + unique["next"] = 0; + unique["ACTIVE"] = 0; unique["ANNOTATED"] = 0; - unique["BLOCKED"] = 0; - unique["BLOCKING"] = 0; - unique["CHILD"] = 0; // 2017-01-07: Deprecated in 2.6.0 + unique["BLOCKED"] = 0; + unique["BLOCKING"] = 0; + unique["CHILD"] = 0; // 2017-01-07: Deprecated in 2.6.0 unique["COMPLETED"] = 0; - unique["DELETED"] = 0; - unique["DUE"] = 0; - unique["DUETODAY"] = 0; // 2016-03-29: Deprecated in 2.6.0 - unique["INSTANCE"] = 0; - unique["LATEST"] = 0; - unique["MONTH"] = 0; - unique["ORPHAN"] = 0; - unique["OVERDUE"] = 0; - unique["PARENT"] = 0; // 2017-01-07: Deprecated in 2.6.0 - unique["PENDING"] = 0; - unique["PRIORITY"] = 0; - unique["PROJECT"] = 0; - unique["QUARTER"] = 0; - unique["READY"] = 0; + unique["DELETED"] = 0; + unique["DUE"] = 0; + unique["DUETODAY"] = 0; // 2016-03-29: Deprecated in 2.6.0 + unique["INSTANCE"] = 0; + unique["LATEST"] = 0; + unique["MONTH"] = 0; + unique["ORPHAN"] = 0; + unique["OVERDUE"] = 0; + unique["PARENT"] = 0; // 2017-01-07: Deprecated in 2.6.0 + unique["PENDING"] = 0; + unique["PRIORITY"] = 0; + unique["PROJECT"] = 0; + unique["QUARTER"] = 0; + unique["READY"] = 0; unique["SCHEDULED"] = 0; - unique["TAGGED"] = 0; - unique["TEMPLATE"] = 0; - unique["TODAY"] = 0; - unique["TOMORROW"] = 0; - unique["UDA"] = 0; + unique["TAGGED"] = 0; + unique["TEMPLATE"] = 0; + unique["TODAY"] = 0; + unique["TOMORROW"] = 0; + unique["UDA"] = 0; unique["UNBLOCKED"] = 0; - unique["UNTIL"] = 0; - unique["WAITING"] = 0; - unique["WEEK"] = 0; - unique["YEAR"] = 0; + unique["UNTIL"] = 0; + unique["WAITING"] = 0; + unique["WEEK"] = 0; + unique["YEAR"] = 0; unique["YESTERDAY"] = 0; // If you update the above list, update src/commands/CmdInfo.cpp and src/Task.cpp as well. std::stringstream out; - for (auto& it : unique) - out << it.first << '\n'; + for (auto& it : unique) out << it.first << '\n'; - output = out.str (); + output = out.str(); return 0; } diff --git a/src/commands/CmdTags.h b/src/commands/CmdTags.h index 0e54e7a00..84d2345b6 100644 --- a/src/commands/CmdTags.h +++ b/src/commands/CmdTags.h @@ -27,21 +27,20 @@ #ifndef INCLUDED_CMDTAGS #define INCLUDED_CMDTAGS -#include #include -class CmdTags : public Command -{ -public: - CmdTags (); - int execute (std::string&); +#include + +class CmdTags : public Command { + public: + CmdTags(); + int execute(std::string&); }; -class CmdCompletionTags : public Command -{ -public: - CmdCompletionTags (); - int execute (std::string&); +class CmdCompletionTags : public Command { + public: + CmdCompletionTags(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdTimesheet.cpp b/src/commands/CmdTimesheet.cpp index 63eddf328..bd076acd3 100644 --- a/src/commands/CmdTimesheet.cpp +++ b/src/commands/CmdTimesheet.cpp @@ -28,190 +28,170 @@ // cmake.h include header must come first #include -#include -#include -#include #include +#include #include #include -#include -#include -#include #include +#include +#include +#include + +#include +#include //////////////////////////////////////////////////////////////////////////////// -CmdTimesheet::CmdTimesheet () -{ - _keyword = "timesheet"; - _usage = "task [filter] timesheet"; - _description = "Summary of completed and started tasks"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdTimesheet::CmdTimesheet() { + _keyword = "timesheet"; + _usage = "task [filter] timesheet"; + _description = "Summary of completed and started tasks"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::report; + _category = Command::Category::report; } //////////////////////////////////////////////////////////////////////////////// // Whether a the timesheet uses context is defined by the // report.timesheet.context configuration variable. // -bool CmdTimesheet::uses_context () const -{ - auto config = Context::getContext ().config; +bool CmdTimesheet::uses_context() const { + auto config = Context::getContext().config; auto key = "report.timesheet.context"; - if (config.has (key)) - return config.getBoolean (key); + if (config.has(key)) + return config.getBoolean(key); else return _uses_context; } - //////////////////////////////////////////////////////////////////////////////// -int CmdTimesheet::execute (std::string& output) -{ +int CmdTimesheet::execute(std::string& output) { int rc = 0; // Detect a filter. - bool hasFilter {false}; - for (auto& a : Context::getContext ().cli2._args) - { - if (a.hasTag ("FILTER")) - { + bool hasFilter{false}; + for (auto& a : Context::getContext().cli2._args) { + if (a.hasTag("FILTER")) { hasFilter = true; break; } } - if (! hasFilter) - { - auto defaultFilter = Context::getContext ().config.get ("report.timesheet.filter"); + if (!hasFilter) { + auto defaultFilter = Context::getContext().config.get("report.timesheet.filter"); if (defaultFilter == "") defaultFilter = "(+PENDING and start.after:now-4wks) or (+COMPLETED and end.after:now-4wks)"; - Context::getContext ().cli2.addFilter (defaultFilter); + Context::getContext().cli2.addFilter(defaultFilter); } // Apply filter to get a set of tasks. - handleUntil (); - handleRecurrence (); + handleUntil(); + handleRecurrence(); Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); // Subset the tasks to only those that are either completed or started. // The _key attribute is represents either the 'start' or 'end' date. int num_completed = 0; int num_started = 0; - std::vector shown; - for (auto& task : filtered) - { - if (task.getStatus () == Task::completed) - { - task.set ("_key", task.get ("end")); + std::vector shown; + for (auto& task : filtered) { + if (task.getStatus() == Task::completed) { + task.set("_key", task.get("end")); ++num_completed; } - if (task.getStatus () == Task::pending && task.has ("start")) - { - task.set ("_key", task.get ("start")); + if (task.getStatus() == Task::pending && task.has("start")) { + task.set("_key", task.get("start")); ++num_started; } - shown.push_back (task); + shown.push_back(task); } // Sort tasks by _key. - std::sort (shown.begin (), - shown.end (), - [](const Task& a, const Task& b) { return a.get ("_key") < b.get ("_key"); }); + std::sort(shown.begin(), shown.end(), + [](const Task& a, const Task& b) { return a.get("_key") < b.get("_key"); }); // Render the completed table. Table table; - table.width (Context::getContext ().getWidth ()); - if (Context::getContext ().config.getBoolean ("obfuscate")) - table.obfuscate (); - table.add ("Wk"); - table.add ("Date"); - table.add ("Day"); - table.add ("ID"); - table.add ("Action"); - table.add ("Project"); - table.add ("Due"); - table.add ("Task"); - setHeaderUnderline (table); + table.width(Context::getContext().getWidth()); + if (Context::getContext().config.getBoolean("obfuscate")) table.obfuscate(); + table.add("Wk"); + table.add("Date"); + table.add("Day"); + table.add("ID"); + table.add("Action"); + table.add("Project"); + table.add("Due"); + table.add("Task"); + setHeaderUnderline(table); - auto dateformat = Context::getContext ().config.get ("dateformat"); + auto dateformat = Context::getContext().config.get("dateformat"); int previous_week = -1; std::string previous_date = ""; std::string previous_day = ""; int weekCounter = 0; Color week_color; - for (auto& task : shown) - { - Datetime key (task.get_date ("_key")); + for (auto& task : shown) { + Datetime key(task.get_date("_key")); - std::string label = task.has ("end") ? "Completed" - : task.has ("start") ? "Started" - : ""; + std::string label = task.has("end") ? "Completed" : task.has("start") ? "Started" : ""; - auto week = key.week (); - auto date = key.toString (dateformat); - auto due = task.has ("due") ? Datetime (task.get ("due")).toString (dateformat) : ""; - auto day = Datetime::dayNameShort (key.dayOfWeek ()); + auto week = key.week(); + auto date = key.toString(dateformat); + auto due = task.has("due") ? Datetime(task.get("due")).toString(dateformat) : ""; + auto day = Datetime::dayNameShort(key.dayOfWeek()); Color task_color; - autoColorize (task, task_color); + autoColorize(task, task_color); // Add a blank line between weeks. - if (week != previous_week && previous_week != -1) - { - auto row = table.addRowEven (); - table.set (row, 0, " "); + if (week != previous_week && previous_week != -1) { + auto row = table.addRowEven(); + table.set(row, 0, " "); } // Keep track of unique week numbers. - if (week != previous_week) - ++weekCounter; + if (week != previous_week) ++weekCounter; // User-defined oddness. int row; if (weekCounter % 2) - row = table.addRowOdd (); + row = table.addRowOdd(); else - row = table.addRowEven (); + row = table.addRowEven(); // If the data doesn't change, it doesn't get shown. - table.set (row, 0, (week != previous_week ? format ("W{1}", week) : "")); - table.set (row, 1, (date != previous_date ? date : "")); - table.set (row, 2, (day != previous_day ? day : "")); - table.set (row, 3, task.identifier(true)); - table.set (row, 4, label); - table.set (row, 5, task.get ("project")); - table.set (row, 6, due); - table.set (row, 7, task.get ("description"), task_color); + table.set(row, 0, (week != previous_week ? format("W{1}", week) : "")); + table.set(row, 1, (date != previous_date ? date : "")); + table.set(row, 2, (day != previous_day ? day : "")); + table.set(row, 3, task.identifier(true)); + table.set(row, 4, label); + table.set(row, 5, task.get("project")); + table.set(row, 6, due); + table.set(row, 7, task.get("description"), task_color); previous_week = week; previous_date = date; - previous_day = day; + previous_day = day; } // Render the table. std::stringstream out; - if (table.rows ()) - out << optionalBlankLine () - << table.render () - << '\n'; + if (table.rows()) out << optionalBlankLine() << table.render() << '\n'; - if (Context::getContext ().verbose ("affected")) - out << format ("{1} completed, {2} started.", num_completed, num_started) - << '\n'; + if (Context::getContext().verbose("affected")) + out << format("{1} completed, {2} started.", num_completed, num_started) << '\n'; - output = out.str (); + output = out.str(); return rc; } diff --git a/src/commands/CmdTimesheet.h b/src/commands/CmdTimesheet.h index 3401abec6..4a5c96979 100644 --- a/src/commands/CmdTimesheet.h +++ b/src/commands/CmdTimesheet.h @@ -27,15 +27,15 @@ #ifndef INCLUDED_CMDTIMESHEET #define INCLUDED_CMDTIMESHEET -#include #include -class CmdTimesheet : public Command -{ -public: - CmdTimesheet (); - int execute (std::string&) override; - bool uses_context () const override; +#include + +class CmdTimesheet : public Command { + public: + CmdTimesheet(); + int execute(std::string&) override; + bool uses_context() const override; }; #endif diff --git a/src/commands/CmdUDAs.cpp b/src/commands/CmdUDAs.cpp index 55fa33e6f..540d1726b 100644 --- a/src/commands/CmdUDAs.cpp +++ b/src/commands/CmdUDAs.cpp @@ -28,182 +28,154 @@ // cmake.h include header must come first #include -#include -#include -#include #include #include -#include +#include +#include #include +#include #include #include -#include + +#include +#include //////////////////////////////////////////////////////////////////////////////// -CmdUDAs::CmdUDAs () -{ - _keyword = "udas"; - _usage = "task udas"; - _description = "Shows all the defined UDA details"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdUDAs::CmdUDAs() { + _keyword = "udas"; + _usage = "task udas"; + _description = "Shows all the defined UDA details"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::config; + _category = Command::Category::config; } //////////////////////////////////////////////////////////////////////////////// -int CmdUDAs::execute (std::string& output) -{ +int CmdUDAs::execute(std::string& output) { int rc = 0; std::stringstream out; - std::vector udas; - for (auto& name : Context::getContext ().config) - { - if (name.first.substr (0, 4) == "uda." && - name.first.find (".type") != std::string::npos) - { - auto period = name.first.find ('.', 4); - if (period != std::string::npos) - udas.push_back (name.first.substr (4, period - 4)); + std::vector udas; + for (auto& name : Context::getContext().config) { + if (name.first.substr(0, 4) == "uda." && name.first.find(".type") != std::string::npos) { + auto period = name.first.find('.', 4); + if (period != std::string::npos) udas.push_back(name.first.substr(4, period - 4)); } } // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); - if (udas.size ()) - { - std::sort (udas.begin (), udas.end ()); + if (udas.size()) { + std::sort(udas.begin(), udas.end()); // Render a list of UDA name, type, label, allowed values, // possible default value, and finally the usage count. Table table; - table.width (Context::getContext ().getWidth ()); - table.add ("Name"); - table.add ("Type"); - table.add ("Label"); - table.add ("Allowed Values"); - table.add ("Default"); - table.add ("Usage Count"); - setHeaderUnderline (table); + table.width(Context::getContext().getWidth()); + table.add("Name"); + table.add("Type"); + table.add("Label"); + table.add("Allowed Values"); + table.add("Default"); + table.add("Usage Count"); + setHeaderUnderline(table); - for (auto& uda : udas) - { - std::string type = Context::getContext ().config.get ("uda." + uda + ".type"); - std::string label = Context::getContext ().config.get ("uda." + uda + ".label"); - std::string values = Context::getContext ().config.get ("uda." + uda + ".values"); - std::string defval = Context::getContext ().config.get ("uda." + uda + ".default"); - if (label == "") - label = uda; + for (auto& uda : udas) { + std::string type = Context::getContext().config.get("uda." + uda + ".type"); + std::string label = Context::getContext().config.get("uda." + uda + ".label"); + std::string values = Context::getContext().config.get("uda." + uda + ".values"); + std::string defval = Context::getContext().config.get("uda." + uda + ".default"); + if (label == "") label = uda; // Count UDA usage by UDA. int count = 0; for (auto& i : filtered) - if (i.has (uda)) - ++count; + if (i.has(uda)) ++count; - int row = table.addRow (); - table.set (row, 0, uda); - table.set (row, 1, type); - table.set (row, 2, label); - table.set (row, 3, values); - table.set (row, 4, defval); - table.set (row, 5, count); + int row = table.addRow(); + table.set(row, 0, uda); + table.set(row, 1, type); + table.set(row, 2, label); + table.set(row, 3, values); + table.set(row, 4, defval); + table.set(row, 5, count); } - out << optionalBlankLine () - << table.render () - << optionalBlankLine () - << (udas.size () == 1 - ? format ("{1} UDA defined", udas.size ()) - : format ("{1} UDAs defined", udas.size ())) + out << optionalBlankLine() << table.render() << optionalBlankLine() + << (udas.size() == 1 ? format("{1} UDA defined", udas.size()) + : format("{1} UDAs defined", udas.size())) << '\n'; - } - else - { + } else { out << "No UDAs defined.\n"; rc = 1; } // Orphans are task attributes that are not represented in context.columns. - std::map orphans; - for (auto& i : filtered) - { - for (auto& att : i.getUDAOrphans ()) - orphans[att]++; + std::map orphans; + for (auto& i : filtered) { + for (auto& att : i.getUDAOrphans()) orphans[att]++; } - if (orphans.size ()) - { + if (orphans.size()) { // Display the orphans and their counts. Table orphanTable; - orphanTable.width (Context::getContext ().getWidth ()); - orphanTable.add ("Orphan UDA"); - orphanTable.add ("Usage Count"); - setHeaderUnderline (orphanTable); + orphanTable.width(Context::getContext().getWidth()); + orphanTable.add("Orphan UDA"); + orphanTable.add("Usage Count"); + setHeaderUnderline(orphanTable); - for (auto& o : orphans) - { - int row = orphanTable.addRow (); - orphanTable.set (row, 0, o.first); - orphanTable.set (row, 1, o.second); + for (auto& o : orphans) { + int row = orphanTable.addRow(); + orphanTable.set(row, 0, o.first); + orphanTable.set(row, 1, o.second); } - out << optionalBlankLine () - << orphanTable.render () - << optionalBlankLine () - << (udas.size () == 1 - ? format ("{1} Orphan UDA", orphans.size ()) - : format ("{1} Orphan UDAs", orphans.size ())) + out << optionalBlankLine() << orphanTable.render() << optionalBlankLine() + << (udas.size() == 1 ? format("{1} Orphan UDA", orphans.size()) + : format("{1} Orphan UDAs", orphans.size())) << '\n'; } - output = out.str (); + output = out.str(); return rc; } /////////////////////////////////////////////////////////////////////////////// -CmdCompletionUDAs::CmdCompletionUDAs () -{ - _keyword = "_udas"; - _usage = "task _udas"; - _description = "Shows the defined UDAs for completion purposes"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdCompletionUDAs::CmdCompletionUDAs() { + _keyword = "_udas"; + _usage = "task _udas"; + _description = "Shows the defined UDAs for completion purposes"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionUDAs::execute (std::string& output) -{ - std::vector udas; - for (auto& name : Context::getContext ().config) - { - if (name.first.substr (0, 4) == "uda." && - name.first.find (".type") != std::string::npos) - { - auto period = name.first.find ('.', 4); - if (period != std::string::npos) - udas.push_back (name.first.substr (4, period - 4)); +int CmdCompletionUDAs::execute(std::string& output) { + std::vector udas; + for (auto& name : Context::getContext().config) { + if (name.first.substr(0, 4) == "uda." && name.first.find(".type") != std::string::npos) { + auto period = name.first.find('.', 4); + if (period != std::string::npos) udas.push_back(name.first.substr(4, period - 4)); } } - if (udas.size ()) - { - std::sort (udas.begin (), udas.end ()); - output = join ("\n", udas) + '\n'; + if (udas.size()) { + std::sort(udas.begin(), udas.end()); + output = join("\n", udas) + '\n'; } return 0; diff --git a/src/commands/CmdUDAs.h b/src/commands/CmdUDAs.h index ac2fb3bc0..185036ecd 100644 --- a/src/commands/CmdUDAs.h +++ b/src/commands/CmdUDAs.h @@ -27,21 +27,20 @@ #ifndef INCLUDED_CMDUDAS #define INCLUDED_CMDUDAS -#include #include -class CmdUDAs : public Command -{ -public: - CmdUDAs (); - int execute (std::string&); +#include + +class CmdUDAs : public Command { + public: + CmdUDAs(); + int execute(std::string&); }; -class CmdCompletionUDAs : public Command -{ -public: - CmdCompletionUDAs (); - int execute (std::string&); +class CmdCompletionUDAs : public Command { + public: + CmdCompletionUDAs(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdUndo.cpp b/src/commands/CmdUndo.cpp index cb3ec26c4..b462ae590 100644 --- a/src/commands/CmdUndo.cpp +++ b/src/commands/CmdUndo.cpp @@ -31,25 +31,23 @@ #include //////////////////////////////////////////////////////////////////////////////// -CmdUndo::CmdUndo () -{ - _keyword = "undo"; - _usage = "task undo"; - _description = "Reverts the most recent change to a task"; - _read_only = false; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdUndo::CmdUndo() { + _keyword = "undo"; + _usage = "task undo"; + _description = "Reverts the most recent change to a task"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::operation; + _category = Command::Category::operation; } //////////////////////////////////////////////////////////////////////////////// -int CmdUndo::execute (std::string&) -{ - Context::getContext ().tdb2.revert (); +int CmdUndo::execute(std::string&) { + Context::getContext().tdb2.revert(); return 0; } diff --git a/src/commands/CmdUndo.h b/src/commands/CmdUndo.h index ca2a0dce1..40993311f 100644 --- a/src/commands/CmdUndo.h +++ b/src/commands/CmdUndo.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDUNDO #define INCLUDED_CMDUNDO -#include #include -class CmdUndo : public Command -{ -public: - CmdUndo (); - int execute (std::string&); +#include + +class CmdUndo : public Command { + public: + CmdUndo(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdUnique.cpp b/src/commands/CmdUnique.cpp index ca67b3075..6833afb24 100644 --- a/src/commands/CmdUnique.cpp +++ b/src/commands/CmdUnique.cpp @@ -28,71 +28,63 @@ // cmake.h include header must come first #include -#include -#include #include #include #include +#include +#include + //////////////////////////////////////////////////////////////////////////////// -CmdUnique::CmdUnique () -{ - _keyword = "_unique"; - _usage = "task _unique "; - _description = "Generates lists of unique attribute values"; - _read_only = true; - _displays_id = true; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdUnique::CmdUnique() { + _keyword = "_unique"; + _usage = "task _unique "; + _description = "Generates lists of unique attribute values"; + _read_only = true; + _displays_id = true; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = true; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdUnique::execute (std::string& output) -{ +int CmdUnique::execute(std::string& output) { // Apply filter. Filter filter; - filter.disableSafety (); - std::vector filtered; - filter.subset (filtered); + filter.disableSafety(); + std::vector filtered; + filter.subset(filtered); // Find . - std::string attribute {}; + std::string attribute{}; // Just the first arg. - auto words = Context::getContext ().cli2.getWords (); - if (words.size () == 0) - throw std::string ("An attribute must be specified. See 'task _columns'."); + auto words = Context::getContext().cli2.getWords(); + if (words.size() == 0) throw std::string("An attribute must be specified. See 'task _columns'."); attribute = words[0]; std::string canonical; - if (! Context::getContext ().cli2.canonicalize (canonical, "attribute", attribute)) - throw std::string ("You must specify an attribute or UDA."); + if (!Context::getContext().cli2.canonicalize(canonical, "attribute", attribute)) + throw std::string("You must specify an attribute or UDA."); // Find the unique set of matching tasks. - std::set values; - for (auto& task : filtered) - { - if (task.has (canonical)) - { - values.insert (task.get (canonical)); - } - else if (canonical == "id" && - task.getStatus () != Task::deleted && - task.getStatus () != Task::completed) - { - values.insert (format (task.id)); + std::set values; + for (auto& task : filtered) { + if (task.has(canonical)) { + values.insert(task.get(canonical)); + } else if (canonical == "id" && task.getStatus() != Task::deleted && + task.getStatus() != Task::completed) { + values.insert(format(task.id)); } } // Generate list of values. - for (auto& value : values) - output += value + '\n'; + for (auto& value : values) output += value + '\n'; - Context::getContext ().headers.clear (); + Context::getContext().headers.clear(); return 0; } diff --git a/src/commands/CmdUnique.h b/src/commands/CmdUnique.h index cbe4b9d61..cad5f8405 100644 --- a/src/commands/CmdUnique.h +++ b/src/commands/CmdUnique.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDUNIQUE #define INCLUDED_CMDUNIQUE -#include #include -class CmdUnique : public Command -{ -public: - CmdUnique (); - int execute (std::string&); +#include + +class CmdUnique : public Command { + public: + CmdUnique(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdUrgency.cpp b/src/commands/CmdUrgency.cpp index 6260d8d2c..4d2b4a31f 100644 --- a/src/commands/CmdUrgency.cpp +++ b/src/commands/CmdUrgency.cpp @@ -28,55 +28,51 @@ // cmake.h include header must come first #include -#include -#include #include #include #include -#include #include +#include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -CmdUrgency::CmdUrgency () -{ - _keyword = "_urgency"; - _usage = "task _urgency"; - _description = "Displays the urgency measure of a task"; - _read_only = true; - _displays_id = false; - _needs_gc = true; - _uses_context = false; - _accepts_filter = true; +CmdUrgency::CmdUrgency() { + _keyword = "_urgency"; + _usage = "task _urgency"; + _description = "Displays the urgency measure of a task"; + _read_only = true; + _displays_id = false; + _needs_gc = true; + _uses_context = false; + _accepts_filter = true; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdUrgency::execute (std::string& output) -{ +int CmdUrgency::execute(std::string& output) { // Apply filter. Filter filter; - std::vector filtered; - filter.subset (filtered); + std::vector filtered; + filter.subset(filtered); - if (filtered.size () == 0) - { - Context::getContext ().footnote ("No tasks specified."); + if (filtered.size() == 0) { + Context::getContext().footnote("No tasks specified."); return 1; } // Display urgency for the selected tasks. std::stringstream out; - for (auto& task : filtered) - { - out << format ("task {1} urgency {2}", - task.identifier (), - Lexer::trim (format (task.urgency (), 6, 3))) + for (auto& task : filtered) { + out << format("task {1} urgency {2}", task.identifier(), + Lexer::trim(format(task.urgency(), 6, 3))) << '\n'; } - output = out.str (); + output = out.str(); return 0; } diff --git a/src/commands/CmdUrgency.h b/src/commands/CmdUrgency.h index aeaad047d..b2be3256e 100644 --- a/src/commands/CmdUrgency.h +++ b/src/commands/CmdUrgency.h @@ -27,14 +27,14 @@ #ifndef INCLUDED_CMDURGENCY #define INCLUDED_CMDURGENCY -#include #include -class CmdUrgency : public Command -{ -public: - CmdUrgency (); - int execute (std::string&); +#include + +class CmdUrgency : public Command { + public: + CmdUrgency(); + int execute(std::string&); }; #endif diff --git a/src/commands/CmdVersion.cpp b/src/commands/CmdVersion.cpp index 1042071d6..fd82bda98 100644 --- a/src/commands/CmdVersion.cpp +++ b/src/commands/CmdVersion.cpp @@ -28,97 +28,90 @@ // cmake.h include header must come first #include -#include -#include -#include #include +#include #include +#include + +#include #ifdef HAVE_COMMIT #include #endif -#include #include +#include //////////////////////////////////////////////////////////////////////////////// -CmdVersion::CmdVersion () -{ - _keyword = "version"; - _usage = "task version"; - _description = "Shows the Taskwarrior version number"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdVersion::CmdVersion() { + _keyword = "version"; + _usage = "task version"; + _description = "Shows the Taskwarrior version number"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::misc; + _category = Command::Category::misc; } //////////////////////////////////////////////////////////////////////////////// -int CmdVersion::execute (std::string& output) -{ +int CmdVersion::execute(std::string& output) { std::stringstream out; // Create a table for the disclaimer. - int width = Context::getContext ().getWidth (); + int width = Context::getContext().getWidth(); Table disclaimer; - disclaimer.width (width); - disclaimer.add (""); - disclaimer.set (disclaimer.addRow (), 0, "Taskwarrior may be copied only under the terms of the MIT license, which may be found in the Taskwarrior source kit."); + disclaimer.width(width); + disclaimer.add(""); + disclaimer.set(disclaimer.addRow(), 0, + "Taskwarrior may be copied only under the terms of the MIT license, which may be " + "found in the Taskwarrior source kit."); // Create a table for the URL. Table link; - link.width (width); - link.add (""); - link.set (link.addRow (), 0, "Documentation for Taskwarrior can be found using 'man task', 'man taskrc', 'man task-color', 'man task-sync' or at https://taskwarrior.org"); + link.width(width); + link.add(""); + link.set(link.addRow(), 0, + "Documentation for Taskwarrior can be found using 'man task', 'man taskrc', 'man " + "task-color', 'man task-sync' or at https://taskwarrior.org"); Datetime now; Color bold; - if (Context::getContext ().color ()) - bold = Color ("bold"); + if (Context::getContext().color()) bold = Color("bold"); out << '\n' - << format ("{1} {2} built for ", bold.colorize (PACKAGE), bold.colorize (VERSION)) - << osName () + << format("{1} {2} built for ", bold.colorize(PACKAGE), bold.colorize(VERSION)) << osName() << '\n' - << "Copyright (C) 2006 - " << now.year () << " T. Babej, P. Beckingham, F. Hernandez." + << "Copyright (C) 2006 - " << now.year() << " T. Babej, P. Beckingham, F. Hernandez." << '\n' << '\n' - << '\n' - << disclaimer.render () - << '\n' - << link.render () - << '\n'; + << disclaimer.render() << '\n' + << link.render() << '\n'; - output = out.str (); + output = out.str(); return 0; } //////////////////////////////////////////////////////////////////////////////// -CmdCompletionVersion::CmdCompletionVersion () -{ - _keyword = "_version"; - _usage = "task _version"; - _description = "Shows only the Taskwarrior version number"; - _read_only = true; - _displays_id = false; - _needs_gc = false; - _uses_context = false; - _accepts_filter = false; +CmdCompletionVersion::CmdCompletionVersion() { + _keyword = "_version"; + _usage = "task _version"; + _description = "Shows only the Taskwarrior version number"; + _read_only = true; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; _accepts_modifications = false; _accepts_miscellaneous = false; - _category = Command::Category::internal; + _category = Command::Category::internal; } //////////////////////////////////////////////////////////////////////////////// -int CmdCompletionVersion::execute (std::string& output) -{ +int CmdCompletionVersion::execute(std::string& output) { #ifdef HAVE_COMMIT - output = std::string (VERSION) - + std::string (" (") - + std::string (COMMIT) - + std::string (")"); + output = std::string(VERSION) + std::string(" (") + std::string(COMMIT) + std::string(")"); #else output = VERSION; #endif diff --git a/src/commands/CmdVersion.h b/src/commands/CmdVersion.h index 9a1ef26e2..1c7e6233e 100644 --- a/src/commands/CmdVersion.h +++ b/src/commands/CmdVersion.h @@ -27,21 +27,20 @@ #ifndef INCLUDED_CMDVERSION #define INCLUDED_CMDVERSION -#include #include -class CmdVersion : public Command -{ -public: - CmdVersion (); - int execute (std::string&); +#include + +class CmdVersion : public Command { + public: + CmdVersion(); + int execute(std::string&); }; -class CmdCompletionVersion : public Command -{ -public: - CmdCompletionVersion (); - int execute (std::string&); +class CmdCompletionVersion : public Command { + public: + CmdCompletionVersion(); + int execute(std::string&); }; #endif diff --git a/src/commands/Command.cpp b/src/commands/Command.cpp index ba028b4a9..0dc215b01 100644 --- a/src/commands/Command.cpp +++ b/src/commands/Command.cpp @@ -27,15 +27,6 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include -#include -#include -#include - #include #include #include @@ -57,6 +48,15 @@ #include #include #include +#include +#include +#include +#include +#include +#include + +#include +#include #ifdef HAVE_EXECUTE #include #endif @@ -88,222 +88,251 @@ #include #include #include - -#include -#include #include +#include +#include //////////////////////////////////////////////////////////////////////////////// -void Command::factory (std::map & all) -{ +void Command::factory(std::map& all) { Command* c; - c = new CmdAdd (); all[c->keyword ()] = c; - c = new CmdAnnotate (); all[c->keyword ()] = c; - c = new CmdAppend (); all[c->keyword ()] = c; - c = new CmdBurndownDaily (); all[c->keyword ()] = c; - c = new CmdBurndownMonthly (); all[c->keyword ()] = c; - c = new CmdBurndownWeekly (); all[c->keyword ()] = c; - c = new CmdCalc (); all[c->keyword ()] = c; - c = new CmdCalendar (); all[c->keyword ()] = c; - c = new CmdColor (); all[c->keyword ()] = c; - c = new CmdColumns (); all[c->keyword ()] = c; - c = new CmdCommands (); all[c->keyword ()] = c; - c = new CmdCompletionAliases (); all[c->keyword ()] = c; - c = new CmdCompletionColumns (); all[c->keyword ()] = c; - c = new CmdCompletionCommands (); all[c->keyword ()] = c; - c = new CmdCompletionConfig (); all[c->keyword ()] = c; - c = new CmdCompletionContext (); all[c->keyword ()] = c; - c = new CmdCompletionIds (); all[c->keyword ()] = c; - c = new CmdCompletionUDAs (); all[c->keyword ()] = c; - c = new CmdCompletionUuids (); all[c->keyword ()] = c; - c = new CmdCompletionProjects (); all[c->keyword ()] = c; - c = new CmdCompletionTags (); all[c->keyword ()] = c; - c = new CmdCompletionVersion (); all[c->keyword ()] = c; - c = new CmdConfig (); all[c->keyword ()] = c; - c = new CmdContext (); all[c->keyword ()] = c; - c = new CmdCount (); all[c->keyword ()] = c; - c = new CmdDelete (); all[c->keyword ()] = c; - c = new CmdDenotate (); all[c->keyword ()] = c; - c = new CmdDiagnostics (); all[c->keyword ()] = c; - c = new CmdDone (); all[c->keyword ()] = c; - c = new CmdDuplicate (); all[c->keyword ()] = c; - c = new CmdEdit (); all[c->keyword ()] = c; + c = new CmdAdd(); + all[c->keyword()] = c; + c = new CmdAnnotate(); + all[c->keyword()] = c; + c = new CmdAppend(); + all[c->keyword()] = c; + c = new CmdBurndownDaily(); + all[c->keyword()] = c; + c = new CmdBurndownMonthly(); + all[c->keyword()] = c; + c = new CmdBurndownWeekly(); + all[c->keyword()] = c; + c = new CmdCalc(); + all[c->keyword()] = c; + c = new CmdCalendar(); + all[c->keyword()] = c; + c = new CmdColor(); + all[c->keyword()] = c; + c = new CmdColumns(); + all[c->keyword()] = c; + c = new CmdCommands(); + all[c->keyword()] = c; + c = new CmdCompletionAliases(); + all[c->keyword()] = c; + c = new CmdCompletionColumns(); + all[c->keyword()] = c; + c = new CmdCompletionCommands(); + all[c->keyword()] = c; + c = new CmdCompletionConfig(); + all[c->keyword()] = c; + c = new CmdCompletionContext(); + all[c->keyword()] = c; + c = new CmdCompletionIds(); + all[c->keyword()] = c; + c = new CmdCompletionUDAs(); + all[c->keyword()] = c; + c = new CmdCompletionUuids(); + all[c->keyword()] = c; + c = new CmdCompletionProjects(); + all[c->keyword()] = c; + c = new CmdCompletionTags(); + all[c->keyword()] = c; + c = new CmdCompletionVersion(); + all[c->keyword()] = c; + c = new CmdConfig(); + all[c->keyword()] = c; + c = new CmdContext(); + all[c->keyword()] = c; + c = new CmdCount(); + all[c->keyword()] = c; + c = new CmdDelete(); + all[c->keyword()] = c; + c = new CmdDenotate(); + all[c->keyword()] = c; + c = new CmdDiagnostics(); + all[c->keyword()] = c; + c = new CmdDone(); + all[c->keyword()] = c; + c = new CmdDuplicate(); + all[c->keyword()] = c; + c = new CmdEdit(); + all[c->keyword()] = c; #ifdef HAVE_EXECUTE - c = new CmdExec (); all[c->keyword ()] = c; + c = new CmdExec(); + all[c->keyword()] = c; #endif - c = new CmdExport (); all[c->keyword ()] = c; - c = new CmdGet (); all[c->keyword ()] = c; - c = new CmdGHistoryDaily (); all[c->keyword ()] = c; - c = new CmdGHistoryWeekly (); all[c->keyword ()] = c; - c = new CmdGHistoryMonthly (); all[c->keyword ()] = c; - c = new CmdGHistoryAnnual (); all[c->keyword ()] = c; - c = new CmdHelp (); all[c->keyword ()] = c; - c = new CmdHistoryDaily (); all[c->keyword ()] = c; - c = new CmdHistoryWeekly (); all[c->keyword ()] = c; - c = new CmdHistoryMonthly (); all[c->keyword ()] = c; - c = new CmdHistoryAnnual (); all[c->keyword ()] = c; - c = new CmdIDs (); all[c->keyword ()] = c; - c = new CmdImport (); all[c->keyword ()] = c; - c = new CmdInfo (); all[c->keyword ()] = c; - c = new CmdLog (); all[c->keyword ()] = c; - c = new CmdLogo (); all[c->keyword ()] = c; - c = new CmdModify (); all[c->keyword ()] = c; - c = new CmdNews (); all[c->keyword ()] = c; - c = new CmdPrepend (); all[c->keyword ()] = c; - c = new CmdProjects (); all[c->keyword ()] = c; - c = new CmdPurge (); all[c->keyword ()] = c; - c = new CmdReports (); all[c->keyword ()] = c; - c = new CmdShow (); all[c->keyword ()] = c; - c = new CmdShowRaw (); all[c->keyword ()] = c; - c = new CmdStart (); all[c->keyword ()] = c; - c = new CmdStats (); all[c->keyword ()] = c; - c = new CmdStop (); all[c->keyword ()] = c; - c = new CmdSummary (); all[c->keyword ()] = c; - c = new CmdSync (); all[c->keyword ()] = c; - c = new CmdTags (); all[c->keyword ()] = c; - c = new CmdTimesheet (); all[c->keyword ()] = c; - c = new CmdUDAs (); all[c->keyword ()] = c; - c = new CmdUndo (); all[c->keyword ()] = c; - c = new CmdUnique (); all[c->keyword ()] = c; - c = new CmdUrgency (); all[c->keyword ()] = c; - c = new CmdUUIDs (); all[c->keyword ()] = c; - c = new CmdVersion (); all[c->keyword ()] = c; - c = new CmdZshAttributes (); all[c->keyword ()] = c; - c = new CmdZshCommands (); all[c->keyword ()] = c; - c = new CmdZshCompletionIds (); all[c->keyword ()] = c; - c = new CmdZshCompletionUuids (); all[c->keyword ()] = c; + c = new CmdExport(); + all[c->keyword()] = c; + c = new CmdGet(); + all[c->keyword()] = c; + c = new CmdGHistoryDaily(); + all[c->keyword()] = c; + c = new CmdGHistoryWeekly(); + all[c->keyword()] = c; + c = new CmdGHistoryMonthly(); + all[c->keyword()] = c; + c = new CmdGHistoryAnnual(); + all[c->keyword()] = c; + c = new CmdHelp(); + all[c->keyword()] = c; + c = new CmdHistoryDaily(); + all[c->keyword()] = c; + c = new CmdHistoryWeekly(); + all[c->keyword()] = c; + c = new CmdHistoryMonthly(); + all[c->keyword()] = c; + c = new CmdHistoryAnnual(); + all[c->keyword()] = c; + c = new CmdIDs(); + all[c->keyword()] = c; + c = new CmdImport(); + all[c->keyword()] = c; + c = new CmdInfo(); + all[c->keyword()] = c; + c = new CmdLog(); + all[c->keyword()] = c; + c = new CmdLogo(); + all[c->keyword()] = c; + c = new CmdModify(); + all[c->keyword()] = c; + c = new CmdNews(); + all[c->keyword()] = c; + c = new CmdPrepend(); + all[c->keyword()] = c; + c = new CmdProjects(); + all[c->keyword()] = c; + c = new CmdPurge(); + all[c->keyword()] = c; + c = new CmdReports(); + all[c->keyword()] = c; + c = new CmdShow(); + all[c->keyword()] = c; + c = new CmdShowRaw(); + all[c->keyword()] = c; + c = new CmdStart(); + all[c->keyword()] = c; + c = new CmdStats(); + all[c->keyword()] = c; + c = new CmdStop(); + all[c->keyword()] = c; + c = new CmdSummary(); + all[c->keyword()] = c; + c = new CmdSync(); + all[c->keyword()] = c; + c = new CmdTags(); + all[c->keyword()] = c; + c = new CmdTimesheet(); + all[c->keyword()] = c; + c = new CmdUDAs(); + all[c->keyword()] = c; + c = new CmdUndo(); + all[c->keyword()] = c; + c = new CmdUnique(); + all[c->keyword()] = c; + c = new CmdUrgency(); + all[c->keyword()] = c; + c = new CmdUUIDs(); + all[c->keyword()] = c; + c = new CmdVersion(); + all[c->keyword()] = c; + c = new CmdZshAttributes(); + all[c->keyword()] = c; + c = new CmdZshCommands(); + all[c->keyword()] = c; + c = new CmdZshCompletionIds(); + all[c->keyword()] = c; + c = new CmdZshCompletionUuids(); + all[c->keyword()] = c; // Instantiate a command object for each custom report. - std::vector reports; - for (auto &i : Context::getContext ().config) - { - if (i.first.substr (0, 7) == "report.") - { - std::string report = i.first.substr (7); - auto columns = report.find (".columns"); - if (columns != std::string::npos) - reports.push_back (report.substr (0, columns)); + std::vector reports; + for (auto& i : Context::getContext().config) { + if (i.first.substr(0, 7) == "report.") { + std::string report = i.first.substr(7); + auto columns = report.find(".columns"); + if (columns != std::string::npos) reports.push_back(report.substr(0, columns)); } } - for (auto &report : reports) - { + for (auto& report : reports) { // Make sure a custom report does not clash with a built-in command. - if (all.find (report) != all.end ()) - throw format ("Custom report '{1}' conflicts with built-in task command.", report); + if (all.find(report) != all.end()) + throw format("Custom report '{1}' conflicts with built-in task command.", report); - c = new CmdCustom ( - report, - "task " + report, - Context::getContext ().config.get ("report." + report + ".description")); + c = new CmdCustom(report, "task " + report, + Context::getContext().config.get("report." + report + ".description")); - all[c->keyword ()] = c; + all[c->keyword()] = c; } } //////////////////////////////////////////////////////////////////////////////// -const std::map Command::categoryNames = -{ - // These strings are intentionally not l10n'd: they are used as identifiers. - {Command::Category::unassigned, "unassigned"} // should never happen - ,{Command::Category::metadata, "metadata"} - ,{Command::Category::report, "report"} - ,{Command::Category::operation, "operation"} - ,{Command::Category::context, "context"} - ,{Command::Category::graphs, "graphs" } - ,{Command::Category::config, "config" } - ,{Command::Category::migration, "migration"} - ,{Command::Category::misc, "misc" } - ,{Command::Category::internal, "internal"} - ,{Command::Category::UNDOCUMENTED, "undocumented"} -}; +const std::map Command::categoryNames = { + // These strings are intentionally not l10n'd: they are used as identifiers. + {Command::Category::unassigned, "unassigned"} // should never happen + , + {Command::Category::metadata, "metadata"}, + {Command::Category::report, "report"}, + {Command::Category::operation, "operation"}, + {Command::Category::context, "context"}, + {Command::Category::graphs, "graphs"}, + {Command::Category::config, "config"}, + {Command::Category::migration, "migration"}, + {Command::Category::misc, "misc"}, + {Command::Category::internal, "internal"}, + {Command::Category::UNDOCUMENTED, "undocumented"}}; //////////////////////////////////////////////////////////////////////////////// -Command::Command () -: _keyword ("") -, _usage ("") -, _description ("") -, _read_only (true) -, _displays_id (true) -, _needs_confirm (false) -, _needs_gc (true) -, _uses_context (false) -, _accepts_filter (false) -, _accepts_modifications (false) -, _accepts_miscellaneous (false) -, _category(Category::unassigned) -, _permission_quit (false) -, _permission_all (false) -, _first_iteration (true) -{ -} +Command::Command() + : _keyword(""), + _usage(""), + _description(""), + _read_only(true), + _displays_id(true), + _needs_confirm(false), + _needs_gc(true), + _uses_context(false), + _accepts_filter(false), + _accepts_modifications(false), + _accepts_miscellaneous(false), + _category(Category::unassigned), + _permission_quit(false), + _permission_all(false), + _first_iteration(true) {} //////////////////////////////////////////////////////////////////////////////// -std::string Command::keyword () const -{ - return _keyword; -} +std::string Command::keyword() const { return _keyword; } //////////////////////////////////////////////////////////////////////////////// -std::string Command::usage () const -{ - return _usage; -} +std::string Command::usage() const { return _usage; } //////////////////////////////////////////////////////////////////////////////// -std::string Command::description () const -{ - return _description; -} +std::string Command::description() const { return _description; } //////////////////////////////////////////////////////////////////////////////// -bool Command::read_only () const -{ - return _read_only; -} +bool Command::read_only() const { return _read_only; } //////////////////////////////////////////////////////////////////////////////// -bool Command::displays_id () const -{ - return _displays_id; -} +bool Command::displays_id() const { return _displays_id; } //////////////////////////////////////////////////////////////////////////////// -bool Command::needs_gc () const -{ - return _needs_gc; -} +bool Command::needs_gc() const { return _needs_gc; } //////////////////////////////////////////////////////////////////////////////// -bool Command::uses_context () const -{ - return _uses_context; -} +bool Command::uses_context() const { return _uses_context; } //////////////////////////////////////////////////////////////////////////////// -bool Command::accepts_filter () const -{ - return _accepts_filter; -} +bool Command::accepts_filter() const { return _accepts_filter; } //////////////////////////////////////////////////////////////////////////////// -bool Command::accepts_modifications () const -{ - return _accepts_modifications; -} +bool Command::accepts_modifications() const { return _accepts_modifications; } //////////////////////////////////////////////////////////////////////////////// -bool Command::accepts_miscellaneous () const -{ - return _accepts_miscellaneous; -} +bool Command::accepts_miscellaneous() const { return _accepts_miscellaneous; } //////////////////////////////////////////////////////////////////////////////// -Command::Category Command::category () const -{ - return _category; -} +Command::Category Command::category() const { return _category; } //////////////////////////////////////////////////////////////////////////////// // Returns true or false indicating whether to proceed with a write command, on @@ -314,51 +343,44 @@ Command::Category Command::category () const // rc.bulk // rc.confirmation // this->_read_only -bool Command::permission ( - const std::string& question, - unsigned int quantity) -{ +bool Command::permission(const std::string& question, unsigned int quantity) { // Read-only commands do not need to seek permission. Write commands are // granted permission automatically if the 'all' selection was made in an // earlier call. Or if the 'all' option has already been made. - if (_read_only || - _permission_all) - return true; + if (_read_only || _permission_all) return true; // If the 'quit' selection has already been made. - if (_permission_quit) - return false; + if (_permission_quit) return false; // What remains are write commands that have not yet selected 'all' or 'quit'. // Describe the task. - bool confirmation = Context::getContext ().config.getBoolean ("confirmation"); - unsigned int bulk = Context::getContext ().config.getInteger ("bulk"); + bool confirmation = Context::getContext().config.getBoolean("confirmation"); + unsigned int bulk = Context::getContext().config.getInteger("bulk"); // Quantity 1 modifications have optional confirmation, and only (y/n). - if (quantity == 1) - { - if (!_needs_confirm || - !confirmation) - return true; + if (quantity == 1) { + if (!_needs_confirm || !confirmation) return true; - bool answer = confirm (question); + bool answer = confirm(question); return answer; } // 1 < Quantity < bulk modifications have optional confirmation, in the (y/n/a/q) // style. Bulk = 0 denotes infinite bulk. - if ((bulk == 0 || quantity < bulk) && (!_needs_confirm || !confirmation)) - return true; + if ((bulk == 0 || quantity < bulk) && (!_needs_confirm || !confirmation)) return true; - if (Context::getContext ().verbose ("blank") && !_first_iteration) - std::cout << '\n'; - int answer = confirm4 (question); + if (Context::getContext().verbose("blank") && !_first_iteration) std::cout << '\n'; + int answer = confirm4(question); _first_iteration = false; - switch (answer) - { - case 1: return true; // yes - case 2: _permission_all = true; return true; // all - case 3: _permission_quit = true; return false; // quit + switch (answer) { + case 1: + return true; // yes + case 2: + _permission_all = true; + return true; // all + case 3: + _permission_quit = true; + return false; // quit } return false; // This line keeps the compiler happy. diff --git a/src/commands/Command.h b/src/commands/Command.h index d93dd90c3..43d6ced89 100644 --- a/src/commands/Command.h +++ b/src/commands/Command.h @@ -27,16 +27,15 @@ #ifndef INCLUDED_COMMAND #define INCLUDED_COMMAND -#include -#include -#include #include -class Command -{ -public: - enum class Category - { +#include +#include +#include + +class Command { + public: + enum class Category { unassigned, // In presentation ("usefulness") order: frequently-used categories first. metadata, @@ -52,46 +51,46 @@ public: // Whenever you extend this enum, update categoryNames. }; - Command (); - virtual ~Command () = default; + Command(); + virtual ~Command() = default; - static void factory (std::map &); + static void factory(std::map&); - std::string keyword () const; - std::string usage () const; - std::string description () const; - bool read_only () const; - bool displays_id () const; - bool needs_gc () const; - virtual bool uses_context () const; - bool accepts_filter () const; - bool accepts_modifications () const; - bool accepts_miscellaneous () const; - Category category () const; - virtual int execute (std::string&) = 0; + std::string keyword() const; + std::string usage() const; + std::string description() const; + bool read_only() const; + bool displays_id() const; + bool needs_gc() const; + virtual bool uses_context() const; + bool accepts_filter() const; + bool accepts_modifications() const; + bool accepts_miscellaneous() const; + Category category() const; + virtual int execute(std::string&) = 0; -protected: - bool permission (const std::string&, unsigned int); - static const std::map categoryNames; + protected: + bool permission(const std::string&, unsigned int); + static const std::map categoryNames; -protected: + protected: std::string _keyword; std::string _usage; std::string _description; - bool _read_only; - bool _displays_id; - bool _needs_confirm; - bool _needs_gc; - bool _uses_context; - bool _accepts_filter; - bool _accepts_modifications; - bool _accepts_miscellaneous; - Category _category; + bool _read_only; + bool _displays_id; + bool _needs_confirm; + bool _needs_gc; + bool _uses_context; + bool _accepts_filter; + bool _accepts_modifications; + bool _accepts_miscellaneous; + Category _category; // Permission support - bool _permission_quit; - bool _permission_all; - bool _first_iteration; + bool _permission_quit; + bool _permission_all; + bool _first_iteration; }; #endif diff --git a/src/dependency.cpp b/src/dependency.cpp index ab17c3d08..28b592470 100644 --- a/src/dependency.cpp +++ b/src/dependency.cpp @@ -27,62 +27,56 @@ #include // cmake.h include header must come first +#include +#include +#include +#include + #include #include #include #include -#include -#include -#include -#include -#define STRING_DEPEND_BLOCKED "Task {1} is blocked by:" +#define STRING_DEPEND_BLOCKED "Task {1} is blocked by:" //////////////////////////////////////////////////////////////////////////////// // Returns true if the supplied task adds a cycle to the dependency chain. -bool dependencyIsCircular (const Task& task) -{ +bool dependencyIsCircular(const Task& task) { // A new task has no UUID assigned yet, and therefore cannot be part of any // dependency chain. - if (task.has ("uuid")) - { - auto task_uuid = task.get ("uuid"); + if (task.has("uuid")) { + auto task_uuid = task.get("uuid"); - std::stack s; - s.push (task); + std::stack s; + s.push(task); - std::unordered_set visited; - visited.insert (task_uuid); + std::unordered_set visited; + visited.insert(task_uuid); - while (! s.empty ()) - { - Task& current = s.top (); - auto deps_current = current.getDependencyUUIDs (); + while (!s.empty()) { + Task& current = s.top(); + auto deps_current = current.getDependencyUUIDs(); // This is a basic depth first search that always terminates given the // fact that we do not visit any task twice - for (const auto& dep : deps_current) - { - if (Context::getContext ().tdb2.get (dep, current)) - { - auto current_uuid = current.get ("uuid"); + for (const auto& dep : deps_current) { + if (Context::getContext().tdb2.get(dep, current)) { + auto current_uuid = current.get("uuid"); - if (task_uuid == current_uuid) - { + if (task_uuid == current_uuid) { // Cycle found, initial task reached for the second time! return true; } - if (visited.find (current_uuid) == visited.end ()) - { + if (visited.find(current_uuid) == visited.end()) { // Push the task to the stack, if it has not been processed yet - s.push (current); - visited.insert (current_uuid); + s.push(current); + visited.insert(current_uuid); } } } - s.pop (); + s.pop(); } } @@ -118,76 +112,61 @@ bool dependencyIsCircular (const Task& task) // 1 dep:3,5 // 4 dep:3,5 // -void dependencyChainOnComplete (Task& task) -{ - auto blocking = task.getDependencyTasks (); +void dependencyChainOnComplete(Task& task) { + auto blocking = task.getDependencyTasks(); // If the task is anything but the tail end of a dependency chain. - if (blocking.size ()) - { - auto blocked = task.getBlockedTasks (); + if (blocking.size()) { + auto blocked = task.getBlockedTasks(); // Nag about broken chain. - if (Context::getContext ().config.getBoolean ("dependency.reminder")) - { - std::cout << format (STRING_DEPEND_BLOCKED, task.identifier ()) - << '\n'; + if (Context::getContext().config.getBoolean("dependency.reminder")) { + std::cout << format(STRING_DEPEND_BLOCKED, task.identifier()) << '\n'; for (const auto& b : blocking) - std::cout << " " << b.id << ' ' << b.get ("description") << '\n'; + std::cout << " " << b.id << ' ' << b.get("description") << '\n'; } // If there are both blocking and blocked tasks, the chain is broken. - if (blocked.size ()) - { - if (Context::getContext ().config.getBoolean ("dependency.reminder")) - { + if (blocked.size()) { + if (Context::getContext().config.getBoolean("dependency.reminder")) { std::cout << "and is blocking:\n"; for (const auto& b : blocked) - std::cout << " " << b.id << ' ' << b.get ("description") << '\n'; + std::cout << " " << b.id << ' ' << b.get("description") << '\n'; } - if (!Context::getContext ().config.getBoolean ("dependency.confirmation") || - confirm ("Would you like the dependency chain fixed?")) - { + if (!Context::getContext().config.getBoolean("dependency.confirmation") || + confirm("Would you like the dependency chain fixed?")) { // Repair the chain - everything in blocked should now depend on // everything in blocking, instead of task.id. - for (auto& left : blocked) - { - left.removeDependency (task.id); + for (auto& left : blocked) { + left.removeDependency(task.id); - for (const auto& right : blocking) - left.addDependency (right.id); + for (const auto& right : blocking) left.addDependency(right.id); } // Now update TDB2, now that the updates have all occurred. - for (auto& left : blocked) - Context::getContext ().tdb2.modify (left); + for (auto& left : blocked) Context::getContext().tdb2.modify(left); - for (auto& right : blocking) - Context::getContext ().tdb2.modify (right); + for (auto& right : blocking) Context::getContext().tdb2.modify(right); } } } } //////////////////////////////////////////////////////////////////////////////// -void dependencyChainOnStart (Task& task) -{ - if (Context::getContext ().config.getBoolean ("dependency.reminder")) - { - auto blocking = task.getDependencyTasks (); +void dependencyChainOnStart(Task& task) { + if (Context::getContext().config.getBoolean("dependency.reminder")) { + auto blocking = task.getDependencyTasks(); // If the task is anything but the tail end of a dependency chain, nag about // broken chain. - if (blocking.size ()) - { - std::cout << format (STRING_DEPEND_BLOCKED, task.identifier ()) - << '\n'; + if (blocking.size()) { + std::cout << format(STRING_DEPEND_BLOCKED, task.identifier()) << '\n'; for (const auto& b : blocking) - std::cout << " " << b.id << ' ' << b.get ("description") << '\n'; + std::cout << " " << b.id << ' ' << b.get("description") << '\n'; } } } diff --git a/src/feedback.cpp b/src/feedback.cpp index 4905b06d0..60d89a686 100644 --- a/src/feedback.cpp +++ b/src/feedback.cpp @@ -27,38 +27,34 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include -#include -#include #include #include #include #include +#include +#include #include #include -#include +#include -static void countTasks (const std::vector &, const std::string&, int&, int&); +#include +#include +#include +#include +#include + +static void countTasks(const std::vector&, const std::string&, int&, int&); //////////////////////////////////////////////////////////////////////////////// -std::string renderAttribute (const std::string& name, const std::string& value, const std::string& format /* = "" */) -{ - if (Context::getContext ().columns.find (name) != Context::getContext ().columns.end ()) - { - Column* col = Context::getContext ().columns[name]; - if (col && - col->type () == "date" && - value != "") - { - Datetime d ((time_t)strtoll (value.c_str (), nullptr, 10)); - if (format == "") - return d.toString (Context::getContext ().config.get ("dateformat")); +std::string renderAttribute(const std::string& name, const std::string& value, + const std::string& format /* = "" */) { + if (Context::getContext().columns.find(name) != Context::getContext().columns.end()) { + Column* col = Context::getContext().columns[name]; + if (col && col->type() == "date" && value != "") { + Datetime d((time_t)strtoll(value.c_str(), nullptr, 10)); + if (format == "") return d.toString(Context::getContext().config.get("dateformat")); - return d.toString (format); + return d.toString(format); } } @@ -68,10 +64,8 @@ std::string renderAttribute (const std::string& name, const std::string& value, //////////////////////////////////////////////////////////////////////////////// // Implements: // -void feedback_affected (const std::string& effect) -{ - if (Context::getContext ().verbose ("affected")) - std::cout << effect << "\n"; +void feedback_affected(const std::string& effect) { + if (Context::getContext().verbose("affected")) std::cout << effect << "\n"; } //////////////////////////////////////////////////////////////////////////////// @@ -80,11 +74,8 @@ void feedback_affected (const std::string& effect) // // The 'effect' string should contain: // {1} Quantity -void feedback_affected (const std::string& effect, int quantity) -{ - if (Context::getContext ().verbose ("affected")) - std::cout << format (effect, quantity) - << "\n"; +void feedback_affected(const std::string& effect, int quantity) { + if (Context::getContext().verbose("affected")) std::cout << format(effect, quantity) << "\n"; } //////////////////////////////////////////////////////////////////////////////// @@ -94,76 +85,49 @@ void feedback_affected (const std::string& effect, int quantity) // The 'effect' string should contain: // {1} ID // {2} Description -void feedback_affected (const std::string& effect, const Task& task) -{ - if (Context::getContext ().verbose ("affected")) - { - std::cout << format (effect, - task.identifier (true), - task.get ("description")) - << "\n"; +void feedback_affected(const std::string& effect, const Task& task) { + if (Context::getContext().verbose("affected")) { + std::cout << format(effect, task.identifier(true), task.get("description")) << "\n"; } } //////////////////////////////////////////////////////////////////////////////// // Implements feedback and error when adding a reserved tag name. -void feedback_reserved_tags (const std::string& tag) -{ +void feedback_reserved_tags(const std::string& tag) { // Note: This list must match that in Task::hasTag. // Note: This list must match that in CmdInfo::execute. - if (tag == "ACTIVE" || - tag == "ANNOTATED" || - tag == "BLOCKED" || - tag == "BLOCKING" || - tag == "CHILD" || // Deprecated 2.6.0 - tag == "COMPLETED" || - tag == "DELETED" || - tag == "DUE" || - tag == "DUETODAY" || - tag == "INSTANCE" || - tag == "LATEST" || - tag == "MONTH" || - tag == "ORPHAN" || - tag == "OVERDUE" || - tag == "PARENT" || // Deprecated 2.6.0 - tag == "PENDING" || - tag == "PRIORITY" || - tag == "PROJECT" || - tag == "QUARTER" || - tag == "READY" || - tag == "SCHEDULED" || - tag == "TAGGED" || - tag == "TEMPLATE" || - tag == "TODAY" || - tag == "TOMORROW" || - tag == "UDA" || - tag == "UNBLOCKED" || - tag == "UNTIL" || - tag == "WAITING" || - tag == "WEEK" || - tag == "YEAR" || - tag == "YESTERDAY") - { - throw format ("Virtual tags (including '{1}') are reserved and may not be added or removed.", tag); + if (tag == "ACTIVE" || tag == "ANNOTATED" || tag == "BLOCKED" || tag == "BLOCKING" || + tag == "CHILD" || // Deprecated 2.6.0 + tag == "COMPLETED" || tag == "DELETED" || tag == "DUE" || tag == "DUETODAY" || + tag == "INSTANCE" || tag == "LATEST" || tag == "MONTH" || tag == "ORPHAN" || + tag == "OVERDUE" || tag == "PARENT" || // Deprecated 2.6.0 + tag == "PENDING" || tag == "PRIORITY" || tag == "PROJECT" || tag == "QUARTER" || + tag == "READY" || tag == "SCHEDULED" || tag == "TAGGED" || tag == "TEMPLATE" || + tag == "TODAY" || tag == "TOMORROW" || tag == "UDA" || tag == "UNBLOCKED" || tag == "UNTIL" || + tag == "WAITING" || tag == "WEEK" || tag == "YEAR" || tag == "YESTERDAY") { + throw format("Virtual tags (including '{1}') are reserved and may not be added or removed.", + tag); } } //////////////////////////////////////////////////////////////////////////////// // Implements feedback when adding special tags to a task. -void feedback_special_tags (const Task& task, const std::string& tag) -{ - if (Context::getContext ().verbose ("special")) - { +void feedback_special_tags(const Task& task, const std::string& tag) { + if (Context::getContext().verbose("special")) { std::string msg; - if (tag == "nocolor") msg = "The 'nocolor' special tag will disable color rules for this task."; - else if (tag == "nonag") msg = "The 'nonag' special tag will prevent nagging when this task is modified."; - else if (tag == "nocal") msg = "The 'nocal' special tag will keep this task off the 'calendar' report."; - else if (tag == "next") msg = "The 'next' special tag will boost the urgency of this task so it appears on the 'next' report."; + if (tag == "nocolor") + msg = "The 'nocolor' special tag will disable color rules for this task."; + else if (tag == "nonag") + msg = "The 'nonag' special tag will prevent nagging when this task is modified."; + else if (tag == "nocal") + msg = "The 'nocal' special tag will keep this task off the 'calendar' report."; + else if (tag == "next") + msg = + "The 'next' special tag will boost the urgency of this task so it appears on the 'next' " + "report."; - if (msg.length ()) - { - std::cout << format (msg, task.identifier ()) - << "\n"; + if (msg.length()) { + std::cout << format(msg, task.identifier()) << "\n"; } } } @@ -175,31 +139,20 @@ void feedback_special_tags (const Task& task, const std::string& tag) // // Implements: // Unblocked '' -void feedback_unblocked (const Task& task) -{ - if (Context::getContext ().verbose ("affected")) - { +void feedback_unblocked(const Task& task) { + if (Context::getContext().verbose("affected")) { // Get a list of tasks that depended on this task. - auto blocked = task.getBlockedTasks (); + auto blocked = task.getBlockedTasks(); // Scan all the tasks that were blocked by this task - for (auto& i : blocked) - { - auto blocking = i.getDependencyTasks (); - if (blocking.size () == 0) - { + for (auto& i : blocked) { + auto blocking = i.getDependencyTasks(); + if (blocking.size() == 0) { if (i.id) - std::cout << format ("Unblocked {1} '{2}'.", - i.id, - i.get ("description")) - << "\n"; - else - { - std::string uuid = i.get ("uuid"); - std::cout << format ("Unblocked {1} '{2}'.", - i.get ("uuid"), - i.get ("description")) - << "\n"; + std::cout << format("Unblocked {1} '{2}'.", i.id, i.get("description")) << "\n"; + else { + std::string uuid = i.get("uuid"); + std::cout << format("Unblocked {1} '{2}'.", i.get("uuid"), i.get("description")) << "\n"; } } } @@ -207,39 +160,35 @@ void feedback_unblocked (const Task& task) } /////////////////////////////////////////////////////////////////////////////// -void feedback_backlog () -{ +void feedback_backlog() { // If non-local sync is not set up, do not provide this feedback. - if (Context::getContext ().config.get ("sync.encryption_secret") == "") { + if (Context::getContext().config.get("sync.encryption_secret") == "") { return; } - if (Context::getContext ().verbose ("sync")) - { - int count = Context::getContext ().tdb2.num_local_changes (); + if (Context::getContext().verbose("sync")) { + int count = Context::getContext().tdb2.num_local_changes(); if (count) - Context::getContext ().footnote (format (count > 1 ? "There are {1} local changes. Sync required." - : "There is {1} local change. Sync required.", count)); + Context::getContext().footnote(format(count > 1 + ? "There are {1} local changes. Sync required." + : "There is {1} local change. Sync required.", + count)); } } /////////////////////////////////////////////////////////////////////////////// -std::string onProjectChange (Task& task, bool scope /* = true */) -{ +std::string onProjectChange(Task& task, bool scope /* = true */) { std::stringstream msg; - std::string project = task.get ("project"); + std::string project = task.get("project"); - if (project != "") - { - if (scope) - msg << format ("The project '{1}' has changed.", project) - << " "; + if (project != "") { + if (scope) msg << format("The project '{1}' has changed.", project) << " "; // Count pending and done tasks, for this project. int count_pending = 0; int count_done = 0; - std::vector all = Context::getContext ().tdb2.all_tasks (); - countTasks (all, project, count_pending, count_done); + std::vector all = Context::getContext().tdb2.all_tasks(); + countTasks(all, project, count_pending, count_done); // count_done count_pending percentage // ---------- ------------- ---------- @@ -255,72 +204,59 @@ std::string onProjectChange (Task& task, bool scope /* = true */) else percentage = (count_done * 100 / (count_done + count_pending)); - msg << format ("Project '{1}' is {2}% complete", project, percentage) - << ' '; + msg << format("Project '{1}' is {2}% complete", project, percentage) << ' '; if (count_pending == 1 && count_done == 0) - msg << format ("({1} task remaining).", count_pending); + msg << format("({1} task remaining).", count_pending); else - msg << format ("({1} of {2} tasks remaining).", count_pending, count_pending + count_done); + msg << format("({1} of {2} tasks remaining).", count_pending, count_pending + count_done); } - return msg.str (); + return msg.str(); } /////////////////////////////////////////////////////////////////////////////// -std::string onProjectChange (Task& task1, Task& task2) -{ - if (task1.get ("project") == task2.get ("project")) - return onProjectChange (task1, false); +std::string onProjectChange(Task& task1, Task& task2) { + if (task1.get("project") == task2.get("project")) return onProjectChange(task1, false); - std::string messages1 = onProjectChange (task1); - std::string messages2 = onProjectChange (task2); + std::string messages1 = onProjectChange(task1); + std::string messages2 = onProjectChange(task2); - if (messages1.length () && messages2.length ()) - return messages1 + '\n' + messages2; + if (messages1.length() && messages2.length()) return messages1 + '\n' + messages2; return messages1 + messages2; } /////////////////////////////////////////////////////////////////////////////// -std::string onExpiration (Task& task) -{ +std::string onExpiration(Task& task) { std::stringstream msg; - if (Context::getContext ().verbose ("affected")) - msg << format ("Task {1} '{2}' expired and was deleted.", - task.identifier (true), - task.get ("description")); + if (Context::getContext().verbose("affected")) + msg << format("Task {1} '{2}' expired and was deleted.", task.identifier(true), + task.get("description")); - return msg.str (); + return msg.str(); } /////////////////////////////////////////////////////////////////////////////// -static void countTasks ( - const std::vector & all, - const std::string& project, - int& count_pending, - int& count_done) -{ - for (auto& it : all) - { - if (it.get ("project") == project) - { - switch (it.getStatus ()) - { - case Task::pending: - case Task::waiting: - ++count_pending; - break; +static void countTasks(const std::vector& all, const std::string& project, int& count_pending, + int& count_done) { + for (auto& it : all) { + if (it.get("project") == project) { + switch (it.getStatus()) { + case Task::pending: + case Task::waiting: + ++count_pending; + break; - case Task::completed: - ++count_done; - break; + case Task::completed: + ++count_done; + break; - case Task::deleted: - case Task::recurring: - default: - break; + case Task::deleted: + case Task::recurring: + default: + break; } } } diff --git a/src/legacy.cpp b/src/legacy.cpp index 965e1d227..16b1f3292 100644 --- a/src/legacy.cpp +++ b/src/legacy.cpp @@ -27,16 +27,16 @@ #include // cmake.h include header must come first -#include -#include #include #include +#include +#include + #define STRING_LEGACY_PRIORITY "Legacy attribute found. Please change '{1}' to '{2}'." //////////////////////////////////////////////////////////////////////////////// -void legacyColumnMap (std::string& name) -{ +void legacyColumnMap(std::string& name) { // 2014-01-26: priority_long --> priority.long Mapping removed // 2014-01-26: entry_time --> entry Mapping removed // 2014-01-26: start_time --> start Mapping removed @@ -51,20 +51,18 @@ void legacyColumnMap (std::string& name) // 2014-01-26: description_only --> description.desc Mapping removed // One-time initialization, on demand. - static std::map legacyMap {{"priority.", "priority"}}; + static std::map legacyMap{{"priority.", "priority"}}; // If a legacy column was used, complain about it, but modify it anyway. - auto found = legacyMap.find (name); - if (found != legacyMap.end ()) - { - Context::getContext ().footnote (format (STRING_LEGACY_PRIORITY, name, found->second)); + auto found = legacyMap.find(name); + if (found != legacyMap.end()) { + Context::getContext().footnote(format(STRING_LEGACY_PRIORITY, name, found->second)); name = found->second; } } //////////////////////////////////////////////////////////////////////////////// -void legacySortColumnMap (std::string& name) -{ +void legacySortColumnMap(std::string& name) { // 2014-01-26: priority_long --> priority Mapping removed // 2014-01-26: entry_time --> entry Mapping removed // 2014-01-26: start_time --> start Mapping removed @@ -79,97 +77,83 @@ void legacySortColumnMap (std::string& name) // 2014-01-26: description_only --> description Mapping removed // One-time initialization, on demand. - static std::map legacyMap {{"priority.", "priority"}}; + static std::map legacyMap{{"priority.", "priority"}}; // If a legacy column was used, complain about it, but modify it anyway. - auto found = legacyMap.find (name); - if (found != legacyMap.end ()) - { - Context::getContext ().footnote (format (STRING_LEGACY_PRIORITY, name, found->second)); + auto found = legacyMap.find(name); + if (found != legacyMap.end()) { + Context::getContext().footnote(format(STRING_LEGACY_PRIORITY, name, found->second)); name = found->second; } } //////////////////////////////////////////////////////////////////////////////// -std::string legacyCheckForDeprecatedVariables () -{ - std::vector deprecated; - for (auto& it : Context::getContext ().config) - { +std::string legacyCheckForDeprecatedVariables() { + std::vector deprecated; + for (auto& it : Context::getContext().config) { // 2014-07-04: report.*.limit removed. // 2016-02-24: alias._query removed. // Deprecated in 2.5.0. // report.*.annotations - if (it.first.length () > 19 && - it.first.substr (0, 7) == "report." && - it.first.substr (it.first.length () - 12) == ".annotations") - deprecated.push_back (it.first); + if (it.first.length() > 19 && it.first.substr(0, 7) == "report." && + it.first.substr(it.first.length() - 12) == ".annotations") + deprecated.push_back(it.first); // Deprecated in 2.5.0. - if (it.first == "next" || - it.first == "annotations" || - it.first == "export.ical.class") - deprecated.push_back (it.first); + if (it.first == "next" || it.first == "annotations" || it.first == "export.ical.class") + deprecated.push_back(it.first); // Deprecated in 2.5.0. - if (it.first == "urgency.inherit.coefficient") - deprecated.push_back (it.first); + if (it.first == "urgency.inherit.coefficient") deprecated.push_back(it.first); } std::stringstream out; - if (deprecated.size ()) - { + if (deprecated.size()) { out << "Your .taskrc file contains variables that are deprecated:\n"; - for (const auto& dep : deprecated) - out << " " << dep << "\n"; + for (const auto& dep : deprecated) out << " " << dep << "\n"; out << "\n"; } - return out.str (); + return out.str(); } //////////////////////////////////////////////////////////////////////////////// -std::string legacyCheckForDeprecatedColumns () -{ - std::vector deprecated; - for (auto& it : Context::getContext ().config) - { - if (it.first.find ("report") == 0) - { +std::string legacyCheckForDeprecatedColumns() { + std::vector deprecated; + for (auto& it : Context::getContext().config) { + if (it.first.find("report") == 0) { // Deprecated in 2.0.0 - std::string value = Context::getContext ().config.get (it.first); - if (value.find ("entry_time") != std::string::npos || - value.find ("start_time") != std::string::npos || - value.find ("end_time") != std::string::npos) - deprecated.push_back (it.first); + std::string value = Context::getContext().config.get(it.first); + if (value.find("entry_time") != std::string::npos || + value.find("start_time") != std::string::npos || + value.find("end_time") != std::string::npos) + deprecated.push_back(it.first); } } std::stringstream out; out << "\n"; - if (deprecated.size ()) - { - out << "Your .taskrc file contains reports with deprecated columns. Please check for entry_time, start_time or end_time in:\n"; + if (deprecated.size()) { + out << "Your .taskrc file contains reports with deprecated columns. Please check for " + "entry_time, start_time or end_time in:\n"; for (const auto& dep : deprecated) - out << " " << dep << "=" << Context::getContext ().config.get (dep) << "\n"; + out << " " << dep << "=" << Context::getContext().config.get(dep) << "\n"; out << "\n"; } - return out.str (); + return out.str(); } //////////////////////////////////////////////////////////////////////////////// -void legacyAttributeMap (std::string& name) -{ +void legacyAttributeMap(std::string& name) { // TW-1274, 2.4.0 - if (name == "modification") - name = "modified"; + if (name == "modification") name = "modified"; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/lex.cpp b/src/lex.cpp index 366962bc7..6e675eef5 100644 --- a/src/lex.cpp +++ b/src/lex.cpp @@ -1,22 +1,21 @@ //////////////////////////////////////////////////////////////////////////////// -#include -#include #include +#include + +#include Context context; -int main (int argc, char** argv) -{ - for (auto i = 1; i < argc; i++) - { +int main(int argc, char** argv) { + for (auto i = 1; i < argc; i++) { std::cout << "argument '" << argv[i] << "'\n"; - Lexer l (argv[i]); + Lexer l(argv[i]); std::string token; Lexer::Type type; - while (l.token (token, type)) - std::cout << " token '" << token << "' " << Lexer::typeToString (type) << "\n"; + while (l.token(token, type)) + std::cout << " token '" << token << "' " << Lexer::typeToString(type) << "\n"; } } diff --git a/src/main.cpp b/src/main.cpp index d360876d0..f64daeee7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,53 +27,44 @@ #include // cmake.h include header must come first +#include + +#include #include #include -#include -#include #include //////////////////////////////////////////////////////////////////////////////// -int main (int argc, const char** argv) -{ - int status {0}; +int main(int argc, const char** argv) { + int status{0}; Context globalContext; - Context::setContext (&globalContext); + Context::setContext(&globalContext); // Lightweight version checking that doesn't require initialization or any I/O. - if (argc == 2 && !strcmp (argv[1], "--version")) - { + if (argc == 2 && !strcmp(argv[1], "--version")) { std::cout << VERSION << "\n"; - } - else - { - try - { - status = Context::getContext ().initialize (argc, argv); - if (status == 0) - status = Context::getContext ().run (); + } else { + try { + status = Context::getContext().initialize(argc, argv); + if (status == 0) status = Context::getContext().run(); } - catch (const std::string& error) - { + catch (const std::string& error) { std::cerr << error << "\n"; status = -1; } - catch (std::bad_alloc& error) - { - std::cerr << "Error: Memory allocation failed: " << error.what () << "\n"; + catch (std::bad_alloc& error) { + std::cerr << "Error: Memory allocation failed: " << error.what() << "\n"; status = -3; } - catch (const std::regex_error& e) - { + catch (const std::regex_error& e) { std::cout << "regex_error caught: " << e.what() << '\n'; } - catch (...) - { + catch (...) { std::cerr << "Unknown error. Please report.\n"; status = -2; } diff --git a/src/main.h b/src/main.h index 3b29a74f1..9d15a09fa 100644 --- a/src/main.h +++ b/src/main.h @@ -27,66 +27,69 @@ #ifndef INCLUDED_MAIN #define INCLUDED_MAIN -#include -#include -#include -#include -#include -#include +#include #include #include -#include +#include + +#include +#include +#include +#include +#include // recur.cpp -void handleRecurrence (); -void handleUntil (); -Datetime getNextRecurrence (Datetime&, std::string&); -bool generateDueDates (Task&, std::vector &); -void updateRecurrenceMask (Task&); +void handleRecurrence(); +void handleUntil(); +Datetime getNextRecurrence(Datetime&, std::string&); +bool generateDueDates(Task&, std::vector&); +void updateRecurrenceMask(Task&); // recur2.cpp -void handleRecurrence2 (); +void handleRecurrence2(); // nag.cpp -void nag (std::vector &); +void nag(std::vector&); // 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&); +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&); +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&); +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 &, std::vector &, const std::string&); -void sort_projects (std::list >& sorted, std::map & allProjects); -void sort_projects (std::list >& sorted, std::map & allProjects); +void sort_tasks(std::vector&, std::vector&, const std::string&); +void sort_projects(std::list>& sorted, + std::map& allProjects); +void sort_projects(std::list>& sorted, + std::map& allProjects); // legacy.cpp -void legacyColumnMap (std::string&); -void legacySortColumnMap (std::string&); -std::string legacyCheckForDeprecatedVariables (); -std::string legacyCheckForDeprecatedColumns (); -void legacyAttributeMap (std::string&); +void legacyColumnMap(std::string&); +void legacySortColumnMap(std::string&); +std::string legacyCheckForDeprecatedVariables(); +std::string legacyCheckForDeprecatedColumns(); +void legacyAttributeMap(std::string&); #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/src/nag.cpp b/src/nag.cpp index 55085a8b2..057567582 100644 --- a/src/nag.cpp +++ b/src/nag.cpp @@ -24,11 +24,13 @@ // //////////////////////////////////////////////////////////////////////////////// -#include #include + +#include // cmake.h include header must come first #include + #include #include #include @@ -36,23 +38,17 @@ //////////////////////////////////////////////////////////////////////////////// // Generates a nag message when there are READY tasks of a higher urgency. -void nag (std::vector & tasks) -{ - auto msg = Context::getContext ().config.get ("nag"); - if (msg == "") - return; +void nag(std::vector& tasks) { + auto msg = Context::getContext().config.get("nag"); + if (msg == "") return; - auto pending = Context::getContext ().tdb2.pending_tasks (); + auto pending = Context::getContext().tdb2.pending_tasks(); for (auto& t1 : tasks) { - if (t1.hasTag ("nonag")) - continue; + if (t1.hasTag("nonag")) continue; for (auto& t2 : pending) { - if (t1.get ("uuid") != t2.get ("uuid") && - t2.hasTag ("READY") && - t1.urgency () < t2.urgency ()) - { - Context::getContext ().footnote (msg); + if (t1.get("uuid") != t2.get("uuid") && t2.hasTag("READY") && t1.urgency() < t2.urgency()) { + Context::getContext().footnote(msg); return; } } diff --git a/src/recur.cpp b/src/recur.cpp index 463534ecf..f5c9f3979 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -27,109 +27,100 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include #include #include +#include #include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include //////////////////////////////////////////////////////////////////////////////// // Scans all tasks, and for any recurring tasks, determines whether any new // child tasks need to be generated to fill gaps. -void handleRecurrence () -{ +void handleRecurrence() { // Recurrence can be disabled. // Note: This is currently a workaround for TD-44, TW-1520. - if (! Context::getContext ().config.getBoolean ("recurrence")) - return; + if (!Context::getContext().config.getBoolean("recurrence")) return; - auto tasks = Context::getContext ().tdb2.pending_tasks (); + auto tasks = Context::getContext().tdb2.pending_tasks(); Datetime now; // Look at all tasks and find any recurring ones. - for (auto& t : tasks) - { - if (t.getStatus () == Task::recurring) - { + for (auto& t : tasks) { + if (t.getStatus() == Task::recurring) { // Generate a list of due dates for this recurring task, regardless of // the mask. - std::vector due; - if (! generateDueDates (t, due)) - { + std::vector due; + if (!generateDueDates(t, due)) { // Determine the end date. - t.setStatus (Task::deleted); - Context::getContext ().tdb2.modify (t); - Context::getContext ().footnote (onExpiration (t)); + t.setStatus(Task::deleted); + Context::getContext().tdb2.modify(t); + Context::getContext().footnote(onExpiration(t)); continue; } // Get the mask from the parent task. - auto mask = t.get ("mask"); + auto mask = t.get("mask"); // Iterate over the due dates, and check each against the mask. auto changed = false; unsigned int i = 0; - for (auto& d : due) - { - if (mask.length () <= i) - { + for (auto& d : due) { + if (mask.length() <= i) { changed = true; - Task rec (t); // Clone the parent. - rec.setStatus (Task::pending); // Change the status. - rec.set ("uuid", uuid ()); // New UUID. - rec.set ("parent", t.get ("uuid")); // Remember mom. - rec.setAsNow ("entry"); // New entry date. - rec.set ("due", format (d.toEpoch ())); + Task rec(t); // Clone the parent. + rec.setStatus(Task::pending); // Change the status. + rec.set("uuid", uuid()); // New UUID. + rec.set("parent", t.get("uuid")); // Remember mom. + rec.setAsNow("entry"); // New entry date. + rec.set("due", format(d.toEpoch())); - if (t.has ("wait")) - { - Datetime old_wait (t.get_date ("wait")); - Datetime old_due (t.get_date ("due")); - Datetime due (d); - rec.set ("wait", format ((due + (old_wait - old_due)).toEpoch ())); - rec.setStatus (Task::waiting); + if (t.has("wait")) { + Datetime old_wait(t.get_date("wait")); + Datetime old_due(t.get_date("due")); + Datetime due(d); + rec.set("wait", format((due + (old_wait - old_due)).toEpoch())); + rec.setStatus(Task::waiting); mask += 'W'; - } - else - { + } else { mask += '-'; - rec.setStatus (Task::pending); + rec.setStatus(Task::pending); } - rec.set ("imask", i); - rec.remove ("mask"); // Remove the mask of the parent. + rec.set("imask", i); + rec.remove("mask"); // Remove the mask of the parent. // Add the new task to the DB. - Context::getContext ().tdb2.add (rec); + Context::getContext().tdb2.add(rec); } ++i; } // Only modify the parent if necessary. - if (changed) - { - t.set ("mask", mask); - Context::getContext ().tdb2.modify (t); + if (changed) { + t.set("mask", mask); + Context::getContext().tdb2.modify(t); - if (Context::getContext ().verbose ("recur")) - Context::getContext ().footnote (format ("Creating recurring task instance '{1}'", t.get ("description"))); + if (Context::getContext().verbose("recur")) + Context::getContext().footnote( + format("Creating recurring task instance '{1}'", t.get("description"))); } } } @@ -140,292 +131,242 @@ void handleRecurrence () // period (recur). Then generate a set of corresponding dates. // // Returns false if the parent recurring task is depleted. -bool generateDueDates (Task& parent, std::vector & allDue) -{ +bool generateDueDates(Task& parent, std::vector& allDue) { // Determine due date, recur period and until date. - Datetime due (parent.get_date ("due")); - if (due._date == 0) - return false; + Datetime due(parent.get_date("due")); + if (due._date == 0) return false; - std::string recur = parent.get ("recur"); + std::string recur = parent.get("recur"); bool specificEnd = false; Datetime until; - if (parent.get ("until") != "") - { - until = Datetime (parent.get ("until")); + if (parent.get("until") != "") { + until = Datetime(parent.get("until")); specificEnd = true; } - auto recurrence_limit = Context::getContext ().config.getInteger ("recurrence.limit"); + auto recurrence_limit = Context::getContext().config.getInteger("recurrence.limit"); int recurrence_counter = 0; Datetime now; - for (Datetime i = due; ; i = getNextRecurrence (i, recur)) - { - allDue.push_back (i); + for (Datetime i = due;; i = getNextRecurrence(i, recur)) { + allDue.push_back(i); - if (specificEnd && i > until) - { + if (specificEnd && i > until) { // If i > until, it means there are no more tasks to generate, and if the // parent mask contains all + or X, then there never will be another task // to generate, and this parent task may be safely reaped. - auto mask = parent.get ("mask"); - if (mask.length () == allDue.size () && - mask.find ('-') == std::string::npos) - return false; + auto mask = parent.get("mask"); + if (mask.length() == allDue.size() && mask.find('-') == std::string::npos) return false; return true; } - if (i > now) - ++recurrence_counter; + if (i > now) ++recurrence_counter; - if (recurrence_counter >= recurrence_limit) - return true; + if (recurrence_counter >= recurrence_limit) return true; } return true; } //////////////////////////////////////////////////////////////////////////////// -Datetime getNextRecurrence (Datetime& current, std::string& period) -{ - auto m = current.month (); - auto d = current.day (); - auto y = current.year (); - auto ho = current.hour (); - auto mi = current.minute (); - auto se = current.second (); +Datetime getNextRecurrence(Datetime& current, std::string& period) { + auto m = current.month(); + auto d = current.day(); + auto y = current.year(); + auto ho = current.hour(); + auto mi = current.minute(); + auto se = current.second(); // Some periods are difficult, because they can be vague. - if (period == "monthly" || - period == "P1M") - { - if (++m > 12) - { - m -= 12; - ++y; + if (period == "monthly" || period == "P1M") { + if (++m > 12) { + m -= 12; + ++y; } - while (! Datetime::valid (y, m, d)) - --d; + while (!Datetime::valid(y, m, d)) --d; - return Datetime (y, m, d, ho, mi, se); + return Datetime(y, m, d, ho, mi, se); } - else if (period == "weekdays") - { - auto dow = current.dayOfWeek (); + else if (period == "weekdays") { + auto dow = current.dayOfWeek(); int days; - if (dow == 5) days = 3; - else if (dow == 6) days = 2; - else days = 1; + if (dow == 5) + days = 3; + else if (dow == 6) + days = 2; + else + days = 1; return current + (days * 86400); } - else if (unicodeLatinDigit (period[0]) && - period[period.length () - 1] == 'm') - { - int increment = strtol (period.substr (0, period.length () - 1).c_str (), nullptr, 10); + else if (unicodeLatinDigit(period[0]) && period[period.length() - 1] == 'm') { + int increment = strtol(period.substr(0, period.length() - 1).c_str(), nullptr, 10); if (increment <= 0) - throw format ("Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, increment); + throw format("Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, + increment); m += increment; - while (m > 12) - { - m -= 12; - ++y; + while (m > 12) { + m -= 12; + ++y; } - while (! Datetime::valid (y, m, d)) - --d; + while (!Datetime::valid(y, m, d)) --d; - return Datetime (y, m, d, ho, mi, se); + return Datetime(y, m, d, ho, mi, se); } - else if (period[0] == 'P' && - Lexer::isAllDigits (period.substr (1, period.length () - 2)) && - period[period.length () - 1] == 'M') - { - int increment = strtol (period.substr (1, period.length () - 2).c_str (), nullptr, 10); + else if (period[0] == 'P' && Lexer::isAllDigits(period.substr(1, period.length() - 2)) && + period[period.length() - 1] == 'M') { + int increment = strtol(period.substr(1, period.length() - 2).c_str(), nullptr, 10); if (increment <= 0) - throw format ("Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, increment); + throw format("Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, + increment); m += increment; - while (m > 12) - { - m -= 12; - ++y; + while (m > 12) { + m -= 12; + ++y; } - while (! Datetime::valid (y, m, d)) - --d; + while (!Datetime::valid(y, m, d)) --d; - return Datetime (y, m, d); + return Datetime(y, m, d); } - else if (period == "quarterly" || - period == "P3M") - { + else if (period == "quarterly" || period == "P3M") { m += 3; - if (m > 12) - { - m -= 12; - ++y; + if (m > 12) { + m -= 12; + ++y; } - while (! Datetime::valid (y, m, d)) - --d; + while (!Datetime::valid(y, m, d)) --d; - return Datetime (y, m, d, ho, mi, se); + return Datetime(y, m, d, ho, mi, se); } - else if (unicodeLatinDigit (period[0]) && period[period.length () - 1] == 'q') - { - int increment = strtol (period.substr (0, period.length () - 1).c_str (), nullptr, 10); + else if (unicodeLatinDigit(period[0]) && period[period.length() - 1] == 'q') { + int increment = strtol(period.substr(0, period.length() - 1).c_str(), nullptr, 10); if (increment <= 0) - throw format ("Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, increment); + throw format("Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, + increment); m += 3 * increment; - while (m > 12) - { - m -= 12; - ++y; + while (m > 12) { + m -= 12; + ++y; } - while (! Datetime::valid (y, m, d)) - --d; + while (!Datetime::valid(y, m, d)) --d; - return Datetime (y, m, d, ho, mi, se); + return Datetime(y, m, d, ho, mi, se); } - else if (period == "semiannual" || - period == "P6M") - { + else if (period == "semiannual" || period == "P6M") { m += 6; - if (m > 12) - { - m -= 12; - ++y; + if (m > 12) { + m -= 12; + ++y; } - while (! Datetime::valid (y, m, d)) - --d; + while (!Datetime::valid(y, m, d)) --d; - return Datetime (y, m, d, ho, mi, se); + return Datetime(y, m, d, ho, mi, se); } - else if (period == "bimonthly" || - period == "P2M") - { + else if (period == "bimonthly" || period == "P2M") { m += 2; - if (m > 12) - { - m -= 12; - ++y; + if (m > 12) { + m -= 12; + ++y; } - while (! Datetime::valid (y, m, d)) - --d; + while (!Datetime::valid(y, m, d)) --d; - return Datetime (y, m, d, ho, mi, se); + return Datetime(y, m, d, ho, mi, se); } - else if (period == "biannual" || - period == "biyearly" || - period == "P2Y") - { + else if (period == "biannual" || period == "biyearly" || period == "P2Y") { y += 2; - return Datetime (y, m, d, ho, mi, se); + return Datetime(y, m, d, ho, mi, se); } - else if (period == "annual" || - period == "yearly" || - period == "P1Y") - { + else if (period == "annual" || period == "yearly" || period == "P1Y") { y += 1; // If the due data just happens to be 2/29 in a leap year, then simply // incrementing y is going to create an invalid date. - if (m == 2 && d == 29) - d = 28; + if (m == 2 && d == 29) d = 28; - return Datetime (y, m, d, ho, mi, se); + return Datetime(y, m, d, ho, mi, se); } // Add the period to current, and we're done. std::string::size_type idx = 0; Duration p; - if (! p.parse (period, idx)) - throw std::string (format ("The recurrence value '{1}' is not valid.", period)); + if (!p.parse(period, idx)) + throw std::string(format("The recurrence value '{1}' is not valid.", period)); - return current + p.toTime_t (); + return current + p.toTime_t(); } //////////////////////////////////////////////////////////////////////////////// // When the status of a recurring child task changes, the parent task must // update it's mask. -void updateRecurrenceMask (Task& task) -{ - auto uuid = task.get ("parent"); +void updateRecurrenceMask(Task& task) { + auto uuid = task.get("parent"); Task parent; - if (uuid != "" && - Context::getContext ().tdb2.get (uuid, parent)) - { - unsigned int index = strtol (task.get ("imask").c_str (), nullptr, 10); - auto mask = parent.get ("mask"); - if (mask.length () > index) - { - mask[index] = (task.getStatus () == Task::pending) ? '-' - : (task.getStatus () == Task::completed) ? '+' - : (task.getStatus () == Task::deleted) ? 'X' - : (task.getStatus () == Task::waiting) ? 'W' - : '?'; - } - else - { + if (uuid != "" && Context::getContext().tdb2.get(uuid, parent)) { + unsigned int index = strtol(task.get("imask").c_str(), nullptr, 10); + auto mask = parent.get("mask"); + if (mask.length() > index) { + mask[index] = (task.getStatus() == Task::pending) ? '-' + : (task.getStatus() == Task::completed) ? '+' + : (task.getStatus() == Task::deleted) ? 'X' + : (task.getStatus() == Task::waiting) ? 'W' + : '?'; + } else { std::string mask; - for (unsigned int i = 0; i < index; ++i) - mask += "?"; + for (unsigned int i = 0; i < index; ++i) mask += "?"; - mask += (task.getStatus () == Task::pending) ? '-' - : (task.getStatus () == Task::completed) ? '+' - : (task.getStatus () == Task::deleted) ? 'X' - : (task.getStatus () == Task::waiting) ? 'W' - : '?'; + mask += (task.getStatus() == Task::pending) ? '-' + : (task.getStatus() == Task::completed) ? '+' + : (task.getStatus() == Task::deleted) ? 'X' + : (task.getStatus() == Task::waiting) ? 'W' + : '?'; } - parent.set ("mask", mask); - Context::getContext ().tdb2.modify (parent); + parent.set("mask", mask); + Context::getContext().tdb2.modify(parent); } } //////////////////////////////////////////////////////////////////////////////// // Delete expired tasks. -void handleUntil () -{ +void handleUntil() { Datetime now; - auto tasks = Context::getContext ().tdb2.pending_tasks (); - for (auto& t : tasks) - { + auto tasks = Context::getContext().tdb2.pending_tasks(); + for (auto& t : tasks) { // TODO What about expiring template tasks? - if (t.getStatus () == Task::pending && - t.has ("until")) - { - auto until = Datetime (t.get_date ("until")); - if (until < now) - { - Context::getContext ().debug (format ("handleUntil: recurrence expired until {1} < now {2}", until.toISOLocalExtended (), now.toISOLocalExtended ())); - t.setStatus (Task::deleted); - Context::getContext ().tdb2.modify(t); - Context::getContext ().footnote (onExpiration (t)); + if (t.getStatus() == Task::pending && t.has("until")) { + auto until = Datetime(t.get_date("until")); + if (until < now) { + Context::getContext().debug(format("handleUntil: recurrence expired until {1} < now {2}", + until.toISOLocalExtended(), now.toISOLocalExtended())); + t.setStatus(Task::deleted); + Context::getContext().tdb2.modify(t); + Context::getContext().footnote(onExpiration(t)); } } } diff --git a/src/rules.cpp b/src/rules.cpp index 4edf81d5f..924644233 100644 --- a/src/rules.cpp +++ b/src/rules.cpp @@ -27,175 +27,145 @@ #include // cmake.h include header must come first -#include #include #include -#include #include +#include +#include -static std::map gsColor; -static std::vector gsPrecedence; +static std::map gsColor; +static std::vector gsPrecedence; static Datetime now; //////////////////////////////////////////////////////////////////////////////// -void initializeColorRules () -{ +void initializeColorRules() { // If color is not enable/supported, short circuit. - if (! Context::getContext ().color ()) - return; + if (!Context::getContext().color()) return; - try - { - gsColor.clear (); - gsPrecedence.clear (); + try { + gsColor.clear(); + gsPrecedence.clear(); // Load all the configuration values, filter to only the ones that begin with // "color.", then store name/value in gsColor, and name in rules. - std::vector rules; - for (const auto& v : Context::getContext ().config) - { - if (! v.first.compare (0, 6, "color.", 6)) - { - Color c (v.second); + std::vector rules; + for (const auto& v : Context::getContext().config) { + if (!v.first.compare(0, 6, "color.", 6)) { + Color c(v.second); gsColor[v.first] = c; - rules.push_back (v.first); + rules.push_back(v.first); } } // Load the rule.precedence.color list, split it, then autocomplete against // the 'rules' vector loaded above. - std::vector results; - auto precedence = split (Context::getContext ().config.get ("rule.precedence.color"), ','); + std::vector results; + auto precedence = split(Context::getContext().config.get("rule.precedence.color"), ','); - for (const auto& p : precedence) - { + for (const auto& p : precedence) { // Add the leading "color." string. std::string rule = "color." + p; - autoComplete (rule, rules, results, 3); // Hard-coded 3. + autoComplete(rule, rules, results, 3); // Hard-coded 3. - for (auto& r : results) - gsPrecedence.push_back (r); + for (auto& r : results) gsPrecedence.push_back(r); } } - catch (const std::string& e) - { - Context::getContext ().error (e); + catch (const std::string& e) { + Context::getContext().error(e); } } //////////////////////////////////////////////////////////////////////////////// -static void applyColor (const Color& base, Color& c, bool merge) -{ +static void applyColor(const Color& base, Color& c, bool merge) { if (merge) - c.blend (base); + c.blend(base); else c = base; } //////////////////////////////////////////////////////////////////////////////// -static void colorizeBlocked (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.is_blocked) - applyColor (base, c, merge); +static void colorizeBlocked(Task& task, const Color& base, Color& c, bool merge) { + if (task.is_blocked) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeBlocking (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.is_blocking) - applyColor (base, c, merge); +static void colorizeBlocking(Task& task, const Color& base, Color& c, bool merge) { + if (task.is_blocking) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeTagged (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.getTagCount ()) - applyColor (base, c, merge); +static void colorizeTagged(Task& task, const Color& base, Color& c, bool merge) { + if (task.getTagCount()) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeActive (Task& task, const Color& base, Color& c, bool merge) -{ +static void colorizeActive(Task& task, const Color& base, Color& c, bool merge) { // TODO: Not consistent with the implementation of the +ACTIVE virtual tag - if (task.has ("start") && - !task.has ("end")) - applyColor (base, c, merge); + if (task.has("start") && !task.has("end")) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeScheduled (Task& task, const Color& base, Color& c, bool merge) -{ +static void colorizeScheduled(Task& task, const Color& base, Color& c, bool merge) { // TODO: Not consistent with the implementation of the +SCHEDULED virtual tag - if (task.has ("scheduled") && - Datetime (task.get_date ("scheduled")) <= now) - applyColor (base, c, merge); + if (task.has("scheduled") && Datetime(task.get_date("scheduled")) <= now) + applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeUntil (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.has ("until")) - applyColor (base, c, merge); +static void colorizeUntil(Task& task, const Color& base, Color& c, bool merge) { + if (task.has("until")) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeTag (Task& task, const std::string& rule, const Color& base, Color& c, bool merge) -{ - if (task.hasTag (rule.substr (10))) - applyColor (base, c, merge); +static void colorizeTag(Task& task, const std::string& rule, const Color& base, Color& c, + bool merge) { + if (task.hasTag(rule.substr(10))) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeProject (Task& task, const std::string& rule, const Color& base, Color& c, bool merge) -{ +static void colorizeProject(Task& task, const std::string& rule, const Color& base, Color& c, + bool merge) { // Observe the case sensitivity setting. - bool sensitive = Context::getContext ().config.getBoolean ("search.case.sensitive"); + bool sensitive = Context::getContext().config.getBoolean("search.case.sensitive"); - auto project = task.get ("project"); - auto rule_trunc = rule.substr (14); + auto project = task.get("project"); + auto rule_trunc = rule.substr(14); // Match project names leftmost. - if (rule_trunc.length () <= project.length ()) - if (compare (rule_trunc, project.substr (0, rule_trunc.length ()), sensitive)) - applyColor (base, c, merge); + if (rule_trunc.length() <= project.length()) + if (compare(rule_trunc, project.substr(0, rule_trunc.length()), sensitive)) + applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeProjectNone (Task& task, const Color& base, Color& c, bool merge) -{ - if(!task.has ("project")) - applyColor (base, c, merge); +static void colorizeProjectNone(Task& task, const Color& base, Color& c, bool merge) { + if (!task.has("project")) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeTagNone (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.getTagCount () == 0) - applyColor (base, c, merge); +static void colorizeTagNone(Task& task, const Color& base, Color& c, bool merge) { + if (task.getTagCount() == 0) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeKeyword (Task& task, const std::string& rule, const Color& base, Color& c, bool merge) -{ +static void colorizeKeyword(Task& task, const std::string& rule, const Color& base, Color& c, + bool merge) { // Observe the case sensitivity setting. - auto sensitive = Context::getContext ().config.getBoolean ("search.case.sensitive"); + auto sensitive = Context::getContext().config.getBoolean("search.case.sensitive"); // The easiest thing to check is the description, because it is just one // attribute. - if (find (task.get ("description"), rule.substr (14), sensitive) != std::string::npos) - applyColor (base, c, merge); + if (find(task.get("description"), rule.substr(14), sensitive) != std::string::npos) + applyColor(base, c, merge); // Failing the description check, look at all annotations, returning on the // first match. - else - { - for (const auto& att : task.getAnnotations ()) - { - if (find (att.second, rule.substr (14), sensitive) != std::string::npos) - { - applyColor (base, c, merge); + else { + for (const auto& att : task.getAnnotations()) { + if (find(att.second, rule.substr(14), sensitive) != std::string::npos) { + applyColor(base, c, merge); return; } } @@ -203,146 +173,132 @@ static void colorizeKeyword (Task& task, const std::string& rule, const Color& b } //////////////////////////////////////////////////////////////////////////////// -static void colorizeUDA (Task& task, const std::string& rule, const Color& base, Color& c, bool merge) -{ +static void colorizeUDA(Task& task, const std::string& rule, const Color& base, Color& c, + bool merge) { // Is the rule color.uda.name.value or color.uda.name? - auto pos = rule.find ('.', 10); - if (pos == std::string::npos) - { - if (task.has (rule.substr (10))) - applyColor (base, c, merge); - } - else - { - auto uda = rule.substr (10, pos - 10); - auto val = rule.substr (pos + 1); - if ((val == "none" && ! task.has (uda)) || - task.get (uda) == val) - applyColor (base, c, merge); + auto pos = rule.find('.', 10); + if (pos == std::string::npos) { + if (task.has(rule.substr(10))) applyColor(base, c, merge); + } else { + auto uda = rule.substr(10, pos - 10); + auto val = rule.substr(pos + 1); + if ((val == "none" && !task.has(uda)) || task.get(uda) == val) applyColor(base, c, merge); } } //////////////////////////////////////////////////////////////////////////////// -static void colorizeDue (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.is_due ()) - applyColor (base, c, merge); +static void colorizeDue(Task& task, const Color& base, Color& c, bool merge) { + if (task.is_due()) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeDueToday (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.is_duetoday ()) - applyColor (base, c, merge); +static void colorizeDueToday(Task& task, const Color& base, Color& c, bool merge) { + if (task.is_duetoday()) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeOverdue (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.is_overdue ()) - applyColor (base, c, merge); +static void colorizeOverdue(Task& task, const Color& base, Color& c, bool merge) { + if (task.is_overdue()) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeRecurring (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.has ("recur")) - applyColor (base, c, merge); +static void colorizeRecurring(Task& task, const Color& base, Color& c, bool merge) { + if (task.has("recur")) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeCompleted (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.getStatus () == Task::completed) - applyColor (base, c, merge); +static void colorizeCompleted(Task& task, const Color& base, Color& c, bool merge) { + if (task.getStatus() == Task::completed) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -static void colorizeDeleted (Task& task, const Color& base, Color& c, bool merge) -{ - if (task.getStatus () == Task::deleted) - applyColor (base, c, merge); +static void colorizeDeleted(Task& task, const Color& base, Color& c, bool merge) { + if (task.getStatus() == Task::deleted) applyColor(base, c, merge); } //////////////////////////////////////////////////////////////////////////////// -void autoColorize (Task& task, Color& c) -{ +void autoColorize(Task& task, Color& c) { // The special tag 'nocolor' overrides all auto and specific colorization. - if (! Context::getContext ().color () || - task.hasTag ("nocolor")) - { - c = Color (); + if (!Context::getContext().color() || task.hasTag("nocolor")) { + c = Color(); return; } - auto merge = Context::getContext ().config.getBoolean ("rule.color.merge"); + auto merge = Context::getContext().config.getBoolean("rule.color.merge"); // Note: c already contains colors specifically assigned via command. // Note: These rules form a hierarchy - the last rule is King, hence the // reverse iterator. - for (auto r = gsPrecedence.rbegin (); r != gsPrecedence.rend (); ++r) - { + for (auto r = gsPrecedence.rbegin(); r != gsPrecedence.rend(); ++r) { Color base = gsColor[*r]; - if (base.nontrivial ()) - { - if (*r == "color.blocked") colorizeBlocked (task, base, c, merge); - else if (*r == "color.blocking") colorizeBlocking (task, base, c, merge); - else if (*r == "color.tagged") colorizeTagged (task, base, c, merge); - else if (*r == "color.active") colorizeActive (task, base, c, merge); - else if (*r == "color.scheduled") colorizeScheduled (task, base, c, merge); - else if (*r == "color.until") colorizeUntil (task, base, c, merge); - else if (*r == "color.project.none") colorizeProjectNone (task, base, c, merge); - else if (*r == "color.tag.none") colorizeTagNone (task, base, c, merge); - else if (*r == "color.due") colorizeDue (task, base, c, merge); - else if (*r == "color.due.today") colorizeDueToday (task, base, c, merge); - else if (*r == "color.overdue") colorizeOverdue (task, base, c, merge); - else if (*r == "color.recurring") colorizeRecurring (task, base, c, merge); - else if (*r == "color.completed") colorizeCompleted (task, base, c, merge); - else if (*r == "color.deleted") colorizeDeleted (task, base, c, merge); + if (base.nontrivial()) { + if (*r == "color.blocked") + colorizeBlocked(task, base, c, merge); + else if (*r == "color.blocking") + colorizeBlocking(task, base, c, merge); + else if (*r == "color.tagged") + colorizeTagged(task, base, c, merge); + else if (*r == "color.active") + colorizeActive(task, base, c, merge); + else if (*r == "color.scheduled") + colorizeScheduled(task, base, c, merge); + else if (*r == "color.until") + colorizeUntil(task, base, c, merge); + else if (*r == "color.project.none") + colorizeProjectNone(task, base, c, merge); + else if (*r == "color.tag.none") + colorizeTagNone(task, base, c, merge); + else if (*r == "color.due") + colorizeDue(task, base, c, merge); + else if (*r == "color.due.today") + colorizeDueToday(task, base, c, merge); + else if (*r == "color.overdue") + colorizeOverdue(task, base, c, merge); + else if (*r == "color.recurring") + colorizeRecurring(task, base, c, merge); + else if (*r == "color.completed") + colorizeCompleted(task, base, c, merge); + else if (*r == "color.deleted") + colorizeDeleted(task, base, c, merge); // Wildcards - else if (! r->compare (0, 10, "color.tag.", 10)) colorizeTag (task, *r, base, c, merge); - else if (! r->compare (0, 14, "color.project.", 14)) colorizeProject (task, *r, base, c, merge); - else if (! r->compare (0, 14, "color.keyword.", 14)) colorizeKeyword (task, *r, base, c, merge); - else if (! r->compare (0, 10, "color.uda.", 10)) colorizeUDA (task, *r, base, c, merge); + else if (!r->compare(0, 10, "color.tag.", 10)) + colorizeTag(task, *r, base, c, merge); + else if (!r->compare(0, 14, "color.project.", 14)) + colorizeProject(task, *r, base, c, merge); + else if (!r->compare(0, 14, "color.keyword.", 14)) + colorizeKeyword(task, *r, base, c, merge); + else if (!r->compare(0, 10, "color.uda.", 10)) + colorizeUDA(task, *r, base, c, merge); } } - } //////////////////////////////////////////////////////////////////////////////// -std::string colorizeHeader (const std::string& input) -{ - if (gsColor["color.header"].nontrivial ()) - return gsColor["color.header"].colorize (input); +std::string colorizeHeader(const std::string& input) { + if (gsColor["color.header"].nontrivial()) return gsColor["color.header"].colorize(input); return input; } //////////////////////////////////////////////////////////////////////////////// -std::string colorizeFootnote (const std::string& input) -{ - if (gsColor["color.footnote"].nontrivial ()) - return gsColor["color.footnote"].colorize (input); +std::string colorizeFootnote(const std::string& input) { + if (gsColor["color.footnote"].nontrivial()) return gsColor["color.footnote"].colorize(input); return input; } //////////////////////////////////////////////////////////////////////////////// -std::string colorizeError (const std::string& input) -{ - if (gsColor["color.error"].nontrivial ()) - return gsColor["color.error"].colorize (input); +std::string colorizeError(const std::string& input) { + if (gsColor["color.error"].nontrivial()) return gsColor["color.error"].colorize(input); return input; } //////////////////////////////////////////////////////////////////////////////// -std::string colorizeDebug (const std::string& input) -{ - if (gsColor["color.debug"].nontrivial ()) - return gsColor["color.debug"].colorize (input); +std::string colorizeDebug(const std::string& input) { + if (gsColor["color.debug"].nontrivial()) return gsColor["color.debug"].colorize(input); return input; } diff --git a/src/sort.cpp b/src/sort.cpp index 25f690fe4..4b0bee350 100644 --- a/src/sort.cpp +++ b/src/sort.cpp @@ -27,93 +27,77 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include -#include #include #include #include -#include -#include #include +#include +#include +#include -static std::vector * global_data = nullptr; -static std::vector global_keys; -static bool sort_compare (int, int); +#include +#include +#include +#include +#include + +static std::vector* global_data = nullptr; +static std::vector global_keys; +static bool sort_compare(int, int); //////////////////////////////////////////////////////////////////////////////// -void sort_tasks ( - std::vector & data, - std::vector & order, - const std::string& keys) -{ +void sort_tasks(std::vector& data, std::vector& order, const std::string& keys) { Timer timer; global_data = &data; // Split the key defs. - global_keys = split (keys, ','); + global_keys = split(keys, ','); // Only sort if necessary. - if (order.size ()) - std::stable_sort (order.begin (), order.end (), sort_compare); + if (order.size()) std::stable_sort(order.begin(), order.end(), sort_compare); - Context::getContext ().time_sort_us += timer.total_us (); + Context::getContext().time_sort_us += timer.total_us(); } -void sort_projects ( - std::list >& sorted, - std::map & allProjects) -{ - for (auto& project : allProjects) - { - const std::vector parents = extractParents (project.first); - if (parents.size ()) - { +void sort_projects(std::list>& sorted, + std::map& allProjects) { + for (auto& project : allProjects) { + const std::vector parents = extractParents(project.first); + if (parents.size()) { // if parents exist: store iterator position of last parent - std::list >::iterator parent_pos; - for (auto& parent : parents) - { - parent_pos = std::find_if (sorted.begin (), sorted.end (), - [&parent](const std::pair & item) { return item.first == parent; }); + std::list>::iterator parent_pos; + for (auto& parent : parents) { + parent_pos = std::find_if( + sorted.begin(), sorted.end(), + [&parent](const std::pair& item) { return item.first == parent; }); // if parent does not exist yet: insert into sorted view - if (parent_pos == sorted.end ()) - sorted.emplace_back (parent, 1); + if (parent_pos == sorted.end()) sorted.emplace_back(parent, 1); } // insert new element below latest parent - sorted.insert ((parent_pos == sorted.end ()) ? parent_pos : ++parent_pos, project); - } - else - { + sorted.insert((parent_pos == sorted.end()) ? parent_pos : ++parent_pos, project); + } else { // if has no parents: simply push to end of list - sorted.push_back (project); + sorted.push_back(project); } } } -void sort_projects ( - std::list >& sorted, - std::map & allProjects) -{ - std::map allProjectsInt; - for (auto& p : allProjects) - allProjectsInt[p.first] = (int) p.second; +void sort_projects(std::list>& sorted, + std::map& allProjects) { + std::map allProjectsInt; + for (auto& p : allProjects) allProjectsInt[p.first] = (int)p.second; - sort_projects (sorted, allProjectsInt); + sort_projects(sorted, allProjectsInt); } - //////////////////////////////////////////////////////////////////////////////// // Re-implementation, using direct Task access instead of data copies that // require re-parsing. // // Essentially a static implementation of a dynamic operator<. -static bool sort_compare (int left, int right) -{ +static bool sort_compare(int left, int right) { std::string field; bool ascending; bool breakIndicator; @@ -123,205 +107,148 @@ static bool sort_compare (int left, int right) float left_real; float right_real; - for (auto& k : global_keys) - { - Context::getContext ().decomposeSortField (k, field, ascending, breakIndicator); + for (auto& k : global_keys) { + Context::getContext().decomposeSortField(k, field, ascending, breakIndicator); // Urgency. - if (field == "urgency") - { - left_real = (*global_data)[left].urgency (); - right_real = (*global_data)[right].urgency (); + if (field == "urgency") { + left_real = (*global_data)[left].urgency(); + right_real = (*global_data)[right].urgency(); - if (left_real == right_real) - continue; + if (left_real == right_real) continue; - return ascending ? (left_real < right_real) - : (left_real > right_real); + return ascending ? (left_real < right_real) : (left_real > right_real); } // Number. - else if (field == "id") - { - left_number = (*global_data)[left].id; + else if (field == "id") { + left_number = (*global_data)[left].id; right_number = (*global_data)[right].id; - if (left_number == right_number) - continue; + if (left_number == right_number) continue; - return ascending ? (left_number < right_number) - : (left_number > right_number); + return ascending ? (left_number < right_number) : (left_number > right_number); } // String. - else if (field == "description" || - field == "project" || - field == "status" || - field == "tags" || - field == "uuid" || - field == "parent" || - field == "imask" || - field == "mask") - { - auto left_string = (*global_data)[left].get_ref (field); - auto right_string = (*global_data)[right].get_ref (field); + else if (field == "description" || field == "project" || field == "status" || field == "tags" || + field == "uuid" || field == "parent" || field == "imask" || field == "mask") { + auto left_string = (*global_data)[left].get_ref(field); + auto right_string = (*global_data)[right].get_ref(field); - if (left_string == right_string) - continue; + if (left_string == right_string) continue; - return ascending ? (left_string < right_string) - : (left_string > right_string); + return ascending ? (left_string < right_string) : (left_string > right_string); } // Due Date. - else if (field == "due" || - field == "end" || - field == "entry" || - field == "start" || - field == "until" || - field == "wait" || - field == "modified" || - field == "scheduled") - { - auto left_string = (*global_data)[left].get_ref (field); - auto right_string = (*global_data)[right].get_ref (field); + else if (field == "due" || field == "end" || field == "entry" || field == "start" || + field == "until" || field == "wait" || field == "modified" || field == "scheduled") { + auto left_string = (*global_data)[left].get_ref(field); + auto right_string = (*global_data)[right].get_ref(field); - if (left_string != "" && right_string == "") - return true; + if (left_string != "" && right_string == "") return true; - if (left_string == "" && right_string != "") - return false; + if (left_string == "" && right_string != "") return false; - if (left_string == right_string) - continue; + if (left_string == right_string) continue; - return ascending ? (left_string < right_string) - : (left_string > right_string); + return ascending ? (left_string < right_string) : (left_string > right_string); } // Depends string. - else if (field == "depends") - { + else if (field == "depends") { // Raw data is an un-sorted list of UUIDs. We just need a stable // sort, so we sort them lexically. - auto left_deps = (*global_data)[left].getDependencyUUIDs (); + auto left_deps = (*global_data)[left].getDependencyUUIDs(); std::sort(left_deps.begin(), left_deps.end()); - auto right_deps = (*global_data)[right].getDependencyUUIDs (); + auto right_deps = (*global_data)[right].getDependencyUUIDs(); std::sort(right_deps.begin(), right_deps.end()); - if (left_deps == right_deps) - continue; + if (left_deps == right_deps) continue; - if (left_deps.size () == 0 && right_deps.size () > 0) - return ascending; + if (left_deps.size() == 0 && right_deps.size() > 0) return ascending; - if (left_deps.size () > 0 && right_deps.size () == 0) - return !ascending; + if (left_deps.size() > 0 && right_deps.size() == 0) return !ascending; // Sort on the first dependency. - left_number = Context::getContext ().tdb2.id (left_deps[0]); - right_number = Context::getContext ().tdb2.id (right_deps[0]); + left_number = Context::getContext().tdb2.id(left_deps[0]); + right_number = Context::getContext().tdb2.id(right_deps[0]); - if (left_number == right_number) - continue; + if (left_number == right_number) continue; - return ascending ? (left_number < right_number) - : (left_number > right_number); + return ascending ? (left_number < right_number) : (left_number > right_number); } // Duration. - else if (field == "recur") - { - auto left_string = (*global_data)[left].get_ref (field); - auto right_string = (*global_data)[right].get_ref (field); + else if (field == "recur") { + auto left_string = (*global_data)[left].get_ref(field); + auto right_string = (*global_data)[right].get_ref(field); - if (left_string == right_string) - continue; + if (left_string == right_string) continue; - Duration left_duration (left_string); - Duration right_duration (right_string); - return ascending ? (left_duration < right_duration) - : (left_duration > right_duration); + Duration left_duration(left_string); + Duration right_duration(right_string); + return ascending ? (left_duration < right_duration) : (left_duration > right_duration); } // UDAs. - else if ((column = Context::getContext ().columns[field]) != nullptr) - { - std::string type = column->type (); - if (type == "numeric") - { - auto left_real = strtof (((*global_data)[left].get_ref (field)).c_str (), nullptr); - auto right_real = strtof (((*global_data)[right].get_ref (field)).c_str (), nullptr); + else if ((column = Context::getContext().columns[field]) != nullptr) { + std::string type = column->type(); + if (type == "numeric") { + auto left_real = strtof(((*global_data)[left].get_ref(field)).c_str(), nullptr); + auto right_real = strtof(((*global_data)[right].get_ref(field)).c_str(), nullptr); - if (left_real == right_real) - continue; + if (left_real == right_real) continue; - return ascending ? (left_real < right_real) - : (left_real > right_real); - } - else if (type == "string") - { - auto left_string = (*global_data)[left].get_ref (field); - auto right_string = (*global_data)[right].get_ref (field); + return ascending ? (left_real < right_real) : (left_real > right_real); + } else if (type == "string") { + auto left_string = (*global_data)[left].get_ref(field); + auto right_string = (*global_data)[right].get_ref(field); - if (left_string == right_string) - continue; + if (left_string == right_string) continue; // UDAs of the type string can have custom sort orders, which need to be considered. - auto order = Task::customOrder.find (field); - if (order != Task::customOrder.end ()) - { + auto order = Task::customOrder.find(field); + if (order != Task::customOrder.end()) { // Guaranteed to be found, because of ColUDA::validate (). - auto posLeft = std::find (order->second.begin (), order->second.end (), left_string); - auto posRight = std::find (order->second.begin (), order->second.end (), right_string); + auto posLeft = std::find(order->second.begin(), order->second.end(), left_string); + auto posRight = std::find(order->second.begin(), order->second.end(), right_string); return ascending ? (posLeft < posRight) : (posLeft > posRight); - } - else - { + } else { // Empty values are unconditionally last, if no custom order was specified. if (left_string == "") return false; else if (right_string == "") return true; - return ascending ? (left_string < right_string) - : (left_string > right_string); + return ascending ? (left_string < right_string) : (left_string > right_string); } } - else if (type == "date") - { - auto left_string = (*global_data)[left].get_ref (field); - auto right_string = (*global_data)[right].get_ref (field); + else if (type == "date") { + auto left_string = (*global_data)[left].get_ref(field); + auto right_string = (*global_data)[right].get_ref(field); - if (left_string != "" && right_string == "") - return true; + if (left_string != "" && right_string == "") return true; - if (left_string == "" && right_string != "") - return false; + if (left_string == "" && right_string != "") return false; - if (left_string == right_string) - continue; + if (left_string == right_string) continue; - return ascending ? (left_string < right_string) - : (left_string > right_string); + return ascending ? (left_string < right_string) : (left_string > right_string); + } else if (type == "duration") { + auto left_string = (*global_data)[left].get_ref(field); + auto right_string = (*global_data)[right].get_ref(field); + + if (left_string == right_string) continue; + + Duration left_duration(left_string); + Duration right_duration(right_string); + return ascending ? (left_duration < right_duration) : (left_duration > right_duration); } - else if (type == "duration") - { - auto left_string = (*global_data)[left].get_ref (field); - auto right_string = (*global_data)[right].get_ref (field); - - if (left_string == right_string) - continue; - - Duration left_duration (left_string); - Duration right_duration (right_string); - return ascending ? (left_duration < right_duration) - : (left_duration > right_duration); - } - } - else - throw format ("The '{1}' column is not a valid sort field.", field); + } else + throw format("The '{1}' column is not a valid sort field.", field); } return false; diff --git a/src/tc/Replica.cpp b/src/tc/Replica.cpp index e1a2fb1ce..a90ac2e0b 100644 --- a/src/tc/Replica.cpp +++ b/src/tc/Replica.cpp @@ -28,20 +28,19 @@ // cmake.h include header must come first #include + +#include + #include "tc/Replica.h" -#include "tc/Task.h" #include "tc/Server.h" +#include "tc/Task.h" #include "tc/WorkingSet.h" #include "tc/util.h" -#include using namespace tc::ffi; //////////////////////////////////////////////////////////////////////////////// -tc::ReplicaGuard::ReplicaGuard (Replica &replica, Task &task) : - replica(replica), - task(task) -{ +tc::ReplicaGuard::ReplicaGuard(Replica &replica, Task &task) : replica(replica), task(task) { // "steal" the reference from the Replica and store it locally, so that any // attempt to use the Replica will fail tcreplica = replica.inner.release(); @@ -49,228 +48,196 @@ tc::ReplicaGuard::ReplicaGuard (Replica &replica, Task &task) : } //////////////////////////////////////////////////////////////////////////////// -tc::ReplicaGuard::~ReplicaGuard () -{ +tc::ReplicaGuard::~ReplicaGuard() { task.to_immut(); // return the reference to the Replica. replica.inner.reset(tcreplica); } //////////////////////////////////////////////////////////////////////////////// -tc::Replica::Replica () -{ - inner = unique_tcreplica_ptr ( - tc_replica_new_in_memory (), - [](TCReplica* rep) { tc_replica_free (rep); }); +tc::Replica::Replica() { + inner = unique_tcreplica_ptr(tc_replica_new_in_memory(), + [](TCReplica *rep) { tc_replica_free(rep); }); } //////////////////////////////////////////////////////////////////////////////// -tc::Replica::Replica (Replica &&other) noexcept -{ +tc::Replica::Replica(Replica &&other) noexcept { // move inner from other - inner = unique_tcreplica_ptr ( - other.inner.release (), - [](TCReplica* rep) { tc_replica_free (rep); }); + inner = unique_tcreplica_ptr(other.inner.release(), [](TCReplica *rep) { tc_replica_free(rep); }); } //////////////////////////////////////////////////////////////////////////////// -tc::Replica& tc::Replica::operator= (Replica &&other) noexcept -{ +tc::Replica &tc::Replica::operator=(Replica &&other) noexcept { if (this != &other) { // move inner from other - inner = unique_tcreplica_ptr ( - other.inner.release (), - [](TCReplica* rep) { tc_replica_free (rep); }); + inner = + unique_tcreplica_ptr(other.inner.release(), [](TCReplica *rep) { tc_replica_free(rep); }); } return *this; } //////////////////////////////////////////////////////////////////////////////// -tc::Replica::Replica (const std::string& dir, bool create_if_missing) -{ - TCString path = tc_string_borrow (dir.c_str ()); +tc::Replica::Replica(const std::string &dir, bool create_if_missing) { + TCString path = tc_string_borrow(dir.c_str()); TCString error; - auto tcreplica = tc_replica_new_on_disk (path, create_if_missing, &error); + auto tcreplica = tc_replica_new_on_disk(path, create_if_missing, &error); if (!tcreplica) { - auto errmsg = format ("Could not create replica at {1}: {2}", dir, tc_string_content (&error)); - tc_string_free (&error); + auto errmsg = format("Could not create replica at {1}: {2}", dir, tc_string_content(&error)); + tc_string_free(&error); throw errmsg; } - inner = unique_tcreplica_ptr ( - tcreplica, - [](TCReplica* rep) { tc_replica_free (rep); }); + inner = unique_tcreplica_ptr(tcreplica, [](TCReplica *rep) { tc_replica_free(rep); }); } //////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet tc::Replica::working_set () -{ - TCWorkingSet *tcws = tc_replica_working_set (&*inner); +tc::WorkingSet tc::Replica::working_set() { + TCWorkingSet *tcws = tc_replica_working_set(&*inner); if (!tcws) { - throw replica_error (); + throw replica_error(); } - return WorkingSet {tcws}; + return WorkingSet{tcws}; } //////////////////////////////////////////////////////////////////////////////// -std::optional tc::Replica::get_task (const std::string &uuid) -{ - TCTask *tctask = tc_replica_get_task (&*inner, uuid2tc (uuid)); +std::optional tc::Replica::get_task(const std::string &uuid) { + TCTask *tctask = tc_replica_get_task(&*inner, uuid2tc(uuid)); if (!tctask) { - auto error = tc_replica_error (&*inner); + auto error = tc_replica_error(&*inner); if (error.ptr) { - throw replica_error (error); + throw replica_error(error); } else { return std::nullopt; } } - return std::make_optional (Task (tctask)); + return std::make_optional(Task(tctask)); } //////////////////////////////////////////////////////////////////////////////// -tc::Task tc::Replica::new_task (tc::Status status, const std::string &description) -{ - TCTask *tctask = tc_replica_new_task (&*inner, (tc::ffi::TCStatus)status, string2tc (description)); +tc::Task tc::Replica::new_task(tc::Status status, const std::string &description) { + TCTask *tctask = tc_replica_new_task(&*inner, (tc::ffi::TCStatus)status, string2tc(description)); if (!tctask) { - throw replica_error (); + throw replica_error(); } - return Task (tctask); + return Task(tctask); } //////////////////////////////////////////////////////////////////////////////// -tc::Task tc::Replica::import_task_with_uuid (const std::string &uuid) -{ - TCTask *tctask = tc_replica_import_task_with_uuid (&*inner, uuid2tc (uuid)); +tc::Task tc::Replica::import_task_with_uuid(const std::string &uuid) { + TCTask *tctask = tc_replica_import_task_with_uuid(&*inner, uuid2tc(uuid)); if (!tctask) { - throw replica_error (); + throw replica_error(); } - return Task (tctask); + return Task(tctask); } //////////////////////////////////////////////////////////////////////////////// -void tc::Replica::delete_task (const std::string &uuid) -{ - auto res = tc_replica_delete_task (&*inner, uuid2tc (uuid)); +void tc::Replica::delete_task(const std::string &uuid) { + auto res = tc_replica_delete_task(&*inner, uuid2tc(uuid)); if (res != TC_RESULT_OK) { - throw replica_error (); + throw replica_error(); } } //////////////////////////////////////////////////////////////////////////////// -void tc::Replica::expire_tasks () -{ - auto res = tc_replica_expire_tasks (&*inner); +void tc::Replica::expire_tasks() { + auto res = tc_replica_expire_tasks(&*inner); if (res != TC_RESULT_OK) { - throw replica_error (); + throw replica_error(); } } //////////////////////////////////////////////////////////////////////////////// -void tc::Replica::sync (Server server, bool avoid_snapshots) -{ +void tc::Replica::sync(Server server, bool avoid_snapshots) { // The server remains owned by this function, per tc_replica_sync docs. - auto res = tc_replica_sync (&*inner, server.inner.get(), avoid_snapshots); + auto res = tc_replica_sync(&*inner, server.inner.get(), avoid_snapshots); if (res != TC_RESULT_OK) { - throw replica_error (); + throw replica_error(); } } //////////////////////////////////////////////////////////////////////////////// -TCReplicaOpList tc::Replica::get_undo_ops () -{ - return tc_replica_get_undo_ops(&*inner); -} +TCReplicaOpList tc::Replica::get_undo_ops() { return tc_replica_get_undo_ops(&*inner); } //////////////////////////////////////////////////////////////////////////////// -void tc::Replica::commit_undo_ops (TCReplicaOpList tc_undo_ops, int32_t *undone_out) -{ - auto res = tc_replica_commit_undo_ops (&*inner, tc_undo_ops, undone_out); +void tc::Replica::commit_undo_ops(TCReplicaOpList tc_undo_ops, int32_t *undone_out) { + auto res = tc_replica_commit_undo_ops(&*inner, tc_undo_ops, undone_out); if (res != TC_RESULT_OK) { - throw replica_error (); + throw replica_error(); } } //////////////////////////////////////////////////////////////////////////////// -void tc::Replica::free_replica_ops (TCReplicaOpList tc_undo_ops) -{ +void tc::Replica::free_replica_ops(TCReplicaOpList tc_undo_ops) { tc_replica_op_list_free(&tc_undo_ops); } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_uuid(TCReplicaOp &tc_replica_op) const -{ +std::string tc::Replica::get_op_uuid(TCReplicaOp &tc_replica_op) const { TCString uuid = tc_replica_op_get_uuid(&tc_replica_op); return tc2string(uuid); } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_property(TCReplicaOp &tc_replica_op) const -{ +std::string tc::Replica::get_op_property(TCReplicaOp &tc_replica_op) const { TCString property = tc_replica_op_get_property(&tc_replica_op); return tc2string(property); } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_value(TCReplicaOp &tc_replica_op) const -{ +std::string tc::Replica::get_op_value(TCReplicaOp &tc_replica_op) const { TCString value = tc_replica_op_get_value(&tc_replica_op); return tc2string(value); } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_old_value(TCReplicaOp &tc_replica_op) const -{ +std::string tc::Replica::get_op_old_value(TCReplicaOp &tc_replica_op) const { TCString old_value = tc_replica_op_get_old_value(&tc_replica_op); return tc2string(old_value); } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_timestamp(TCReplicaOp &tc_replica_op) const -{ +std::string tc::Replica::get_op_timestamp(TCReplicaOp &tc_replica_op) const { TCString timestamp = tc_replica_op_get_timestamp(&tc_replica_op); return tc2string(timestamp); } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_old_task_description(TCReplicaOp &tc_replica_op) const -{ +std::string tc::Replica::get_op_old_task_description(TCReplicaOp &tc_replica_op) const { TCString description = tc_replica_op_get_old_task_description(&tc_replica_op); return tc2string(description); } //////////////////////////////////////////////////////////////////////////////// -int64_t tc::Replica::num_local_operations () -{ - auto num = tc_replica_num_local_operations (&*inner); +int64_t tc::Replica::num_local_operations() { + auto num = tc_replica_num_local_operations(&*inner); if (num < 0) { - throw replica_error (); + throw replica_error(); } return num; } //////////////////////////////////////////////////////////////////////////////// -int64_t tc::Replica::num_undo_points () -{ - auto num = tc_replica_num_undo_points (&*inner); +int64_t tc::Replica::num_undo_points() { + auto num = tc_replica_num_undo_points(&*inner); if (num < 0) { - throw replica_error (); + throw replica_error(); } return num; } //////////////////////////////////////////////////////////////////////////////// -std::vector tc::Replica::all_tasks () -{ - TCTaskList tasks = tc_replica_all_tasks (&*inner); +std::vector tc::Replica::all_tasks() { + TCTaskList tasks = tc_replica_all_tasks(&*inner); if (!tasks.items) { - throw replica_error (); + throw replica_error(); } - std::vector all; - all.reserve (tasks.len); + std::vector all; + all.reserve(tasks.len); for (size_t i = 0; i < tasks.len; i++) { - auto tctask = tc_task_list_take (&tasks, i); + auto tctask = tc_task_list_take(&tasks, i); if (tctask) { - all.push_back (Task (tctask)); + all.push_back(Task(tctask)); } } @@ -278,33 +245,28 @@ std::vector tc::Replica::all_tasks () } //////////////////////////////////////////////////////////////////////////////// -void tc::Replica::rebuild_working_set (bool force) -{ - auto res = tc_replica_rebuild_working_set (&*inner, force); +void tc::Replica::rebuild_working_set(bool force) { + auto res = tc_replica_rebuild_working_set(&*inner, force); if (res != TC_RESULT_OK) { - throw replica_error (); + throw replica_error(); } } //////////////////////////////////////////////////////////////////////////////// -tc::ReplicaGuard tc::Replica::mutate_task (tc::Task &task) { - return ReplicaGuard(*this, task); -} +tc::ReplicaGuard tc::Replica::mutate_task(tc::Task &task) { return ReplicaGuard(*this, task); } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::replica_error () { - return replica_error (tc_replica_error (&*inner)); -} +std::string tc::Replica::replica_error() { return replica_error(tc_replica_error(&*inner)); } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::replica_error (TCString error) { +std::string tc::Replica::replica_error(TCString error) { std::string errmsg; if (!error.ptr) { - errmsg = std::string ("Unknown TaskChampion error"); + errmsg = std::string("Unknown TaskChampion error"); } else { - errmsg = std::string (tc_string_content (&error)); + errmsg = std::string(tc_string_content(&error)); } - tc_string_free (&error); + tc_string_free(&error); return errmsg; } diff --git a/src/tc/Replica.h b/src/tc/Replica.h index 4b0ff3eda..8dd3ef188 100644 --- a/src/tc/Replica.h +++ b/src/tc/Replica.h @@ -27,102 +27,101 @@ #ifndef INCLUDED_TC_REPLICA #define INCLUDED_TC_REPLICA -#include #include #include #include +#include #include -#include "tc/ffi.h" + #include "tc/Task.h" +#include "tc/ffi.h" namespace tc { - class Task; - class WorkingSet; - class Server; +class Task; +class WorkingSet; +class Server; - // a unique_ptr to a TCReplica which will automatically free the value when - // it goes out of scope. - using unique_tcreplica_ptr = std::unique_ptr< - tc::ffi::TCReplica, - std::function>; +// a unique_ptr to a TCReplica which will automatically free the value when +// it goes out of scope. +using unique_tcreplica_ptr = + std::unique_ptr>; - // ReplicaGuard uses RAII to ensure that a Replica is not accessed while it - // is mutably borrowed (specifically, to make a task mutable). - class ReplicaGuard { - protected: - friend class Replica; - explicit ReplicaGuard (Replica &, Task &); +// ReplicaGuard uses RAII to ensure that a Replica is not accessed while it +// is mutably borrowed (specifically, to make a task mutable). +class ReplicaGuard { + protected: + friend class Replica; + explicit ReplicaGuard(Replica &, Task &); - public: - ~ReplicaGuard(); + public: + ~ReplicaGuard(); - // No moving or copying allowed - ReplicaGuard (const ReplicaGuard &) = delete; - ReplicaGuard &operator=(const ReplicaGuard &) = delete; - ReplicaGuard (ReplicaGuard &&) = delete; - ReplicaGuard &operator=(Replica &&) = delete; + // No moving or copying allowed + ReplicaGuard(const ReplicaGuard &) = delete; + ReplicaGuard &operator=(const ReplicaGuard &) = delete; + ReplicaGuard(ReplicaGuard &&) = delete; + ReplicaGuard &operator=(Replica &&) = delete; - private: - Replica &replica; - tc::ffi::TCReplica *tcreplica; - Task &task; - }; + private: + Replica &replica; + tc::ffi::TCReplica *tcreplica; + Task &task; +}; - // Replica wraps the TCReplica type, managing its memory, errors, and so on. - // - // Except as noted, method names match the suffix to `tc_replica_..`. - class Replica - { - public: - Replica (); // tc_replica_new_in_memory - Replica (const std::string& dir, bool create_if_missing); // tc_replica_new_on_disk +// Replica wraps the TCReplica type, managing its memory, errors, and so on. +// +// Except as noted, method names match the suffix to `tc_replica_..`. +class Replica { + public: + Replica(); // tc_replica_new_in_memory + Replica(const std::string &dir, bool create_if_missing); // tc_replica_new_on_disk - // This object "owns" inner, so copy is not allowed. - Replica (const Replica &) = delete; - Replica &operator=(const Replica &) = delete; + // This object "owns" inner, so copy is not allowed. + Replica(const Replica &) = delete; + Replica &operator=(const Replica &) = delete; - // Explicit move constructor and assignment - Replica (Replica &&) noexcept; - Replica &operator=(Replica &&) noexcept; + // Explicit move constructor and assignment + Replica(Replica &&) noexcept; + Replica &operator=(Replica &&) noexcept; - std::vector all_tasks (); -// TODO: struct TCUuidList tc_replica_all_task_uuids(struct TCReplica *rep); - tc::WorkingSet working_set (); - std::optional get_task (const std::string &uuid); - tc::Task new_task (Status status, const std::string &description); - tc::Task import_task_with_uuid (const std::string &uuid); - void delete_task (const std::string &uuid); -// TODO: struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid); - void expire_tasks(); - void sync(Server server, bool avoid_snapshots); - tc::ffi::TCReplicaOpList get_undo_ops (); - void commit_undo_ops (tc::ffi::TCReplicaOpList tc_undo_ops, int32_t *undone_out); - void free_replica_ops (tc::ffi::TCReplicaOpList tc_undo_ops); - std::string get_op_uuid(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_property(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_value(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_old_value(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_timestamp(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_old_task_description(tc::ffi::TCReplicaOp &tc_replica_op) const; - int64_t num_local_operations (); - int64_t num_undo_points (); -// TODO: TCResult tc_replica_add_undo_point(struct TCReplica *rep, bool force); - void rebuild_working_set (bool force); + std::vector all_tasks(); + // TODO: struct TCUuidList tc_replica_all_task_uuids(struct TCReplica *rep); + tc::WorkingSet working_set(); + std::optional get_task(const std::string &uuid); + tc::Task new_task(Status status, const std::string &description); + tc::Task import_task_with_uuid(const std::string &uuid); + void delete_task(const std::string &uuid); + // TODO: struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid + // tcuuid); + void expire_tasks(); + void sync(Server server, bool avoid_snapshots); + tc::ffi::TCReplicaOpList get_undo_ops(); + void commit_undo_ops(tc::ffi::TCReplicaOpList tc_undo_ops, int32_t *undone_out); + void free_replica_ops(tc::ffi::TCReplicaOpList tc_undo_ops); + std::string get_op_uuid(tc::ffi::TCReplicaOp &tc_replica_op) const; + std::string get_op_property(tc::ffi::TCReplicaOp &tc_replica_op) const; + std::string get_op_value(tc::ffi::TCReplicaOp &tc_replica_op) const; + std::string get_op_old_value(tc::ffi::TCReplicaOp &tc_replica_op) const; + std::string get_op_timestamp(tc::ffi::TCReplicaOp &tc_replica_op) const; + std::string get_op_old_task_description(tc::ffi::TCReplicaOp &tc_replica_op) const; + int64_t num_local_operations(); + int64_t num_undo_points(); + // TODO: TCResult tc_replica_add_undo_point(struct TCReplica *rep, bool force); + void rebuild_working_set(bool force); - ReplicaGuard mutate_task(tc::Task &); - void immut_task(tc::Task &); + ReplicaGuard mutate_task(tc::Task &); + void immut_task(tc::Task &); - protected: - friend class ReplicaGuard; - unique_tcreplica_ptr inner; - - // construct an error message from tc_replica_error, or from the given - // string retrieved from tc_replica_error. - std::string replica_error (); - std::string replica_error (tc::ffi::TCString string); - }; -} + protected: + friend class ReplicaGuard; + unique_tcreplica_ptr inner; + // construct an error message from tc_replica_error, or from the given + // string retrieved from tc_replica_error. + std::string replica_error(); + std::string replica_error(tc::ffi::TCString string); +}; +} // namespace tc #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Server.cpp b/src/tc/Server.cpp index 5e43d1c2e..e5adef9dd 100644 --- a/src/tc/Server.cpp +++ b/src/tc/Server.cpp @@ -28,95 +28,80 @@ // cmake.h include header must come first #include + #include "tc/Server.h" #include "tc/util.h" using namespace tc::ffi; //////////////////////////////////////////////////////////////////////////////// -tc::Server -tc::Server::new_local (const std::string &server_dir) -{ - TCString tc_server_dir = tc_string_borrow (server_dir.c_str ()); +tc::Server tc::Server::new_local(const std::string &server_dir) { + TCString tc_server_dir = tc_string_borrow(server_dir.c_str()); TCString error; - auto tcserver = tc_server_new_local (tc_server_dir, &error); + auto tcserver = tc_server_new_local(tc_server_dir, &error); if (!tcserver) { - std::string errmsg = format ("Could not configure local server at {1}: {2}", - server_dir, tc_string_content (&error)); - tc_string_free (&error); + std::string errmsg = format("Could not configure local server at {1}: {2}", server_dir, + tc_string_content(&error)); + tc_string_free(&error); throw errmsg; } - return Server (unique_tcserver_ptr ( - tcserver, - [](TCServer* rep) { tc_server_free (rep); })); + return Server(unique_tcserver_ptr(tcserver, [](TCServer *rep) { tc_server_free(rep); })); } //////////////////////////////////////////////////////////////////////////////// -tc::Server -tc::Server::new_sync (const std::string &url, const std::string &client_id, const std::string &encryption_secret) -{ - TCString tc_url = tc_string_borrow (url.c_str ()); - TCString tc_client_id = tc_string_borrow (client_id.c_str ()); - TCString tc_encryption_secret = tc_string_borrow (encryption_secret.c_str ()); +tc::Server tc::Server::new_sync(const std::string &url, const std::string &client_id, + const std::string &encryption_secret) { + TCString tc_url = tc_string_borrow(url.c_str()); + TCString tc_client_id = tc_string_borrow(client_id.c_str()); + TCString tc_encryption_secret = tc_string_borrow(encryption_secret.c_str()); TCUuid tc_client_uuid; if (tc_uuid_from_str(tc_client_id, &tc_client_uuid) != TC_RESULT_OK) { tc_string_free(&tc_url); tc_string_free(&tc_encryption_secret); - throw format ("client_id '{1}' is not a valid UUID", client_id); + throw format("client_id '{1}' is not a valid UUID", client_id); } TCString error; - auto tcserver = tc_server_new_sync (tc_url, tc_client_uuid, tc_encryption_secret, &error); + auto tcserver = tc_server_new_sync(tc_url, tc_client_uuid, tc_encryption_secret, &error); if (!tcserver) { - std::string errmsg = format ("Could not configure connection to server at {1}: {2}", - url, tc_string_content (&error)); - tc_string_free (&error); + std::string errmsg = format("Could not configure connection to server at {1}: {2}", url, + tc_string_content(&error)); + tc_string_free(&error); throw errmsg; } - return Server (unique_tcserver_ptr ( - tcserver, - [](TCServer* rep) { tc_server_free (rep); })); + return Server(unique_tcserver_ptr(tcserver, [](TCServer *rep) { tc_server_free(rep); })); } //////////////////////////////////////////////////////////////////////////////// -tc::Server -tc::Server::new_gcp (const std::string &bucket, const std::string &credential_path, const std::string &encryption_secret) -{ - TCString tc_bucket = tc_string_borrow (bucket.c_str ()); - TCString tc_encryption_secret = tc_string_borrow (encryption_secret.c_str ()); - TCString tc_credential_path = tc_string_borrow (credential_path.c_str ()); +tc::Server tc::Server::new_gcp(const std::string &bucket, const std::string &credential_path, + const std::string &encryption_secret) { + TCString tc_bucket = tc_string_borrow(bucket.c_str()); + TCString tc_encryption_secret = tc_string_borrow(encryption_secret.c_str()); + TCString tc_credential_path = tc_string_borrow(credential_path.c_str()); TCString error; - auto tcserver = tc_server_new_gcp (tc_bucket, tc_credential_path, tc_encryption_secret, &error); + auto tcserver = tc_server_new_gcp(tc_bucket, tc_credential_path, tc_encryption_secret, &error); if (!tcserver) { - std::string errmsg = format ("Could not configure connection to GCP bucket {1}: {2}", - bucket, tc_string_content (&error)); - tc_string_free (&error); + std::string errmsg = format("Could not configure connection to GCP bucket {1}: {2}", bucket, + tc_string_content(&error)); + tc_string_free(&error); throw errmsg; } - return Server (unique_tcserver_ptr ( - tcserver, - [](TCServer* rep) { tc_server_free (rep); })); + return Server(unique_tcserver_ptr(tcserver, [](TCServer *rep) { tc_server_free(rep); })); } //////////////////////////////////////////////////////////////////////////////// -tc::Server::Server (tc::Server &&other) noexcept -{ +tc::Server::Server(tc::Server &&other) noexcept { // move inner from other - inner = unique_tcserver_ptr ( - other.inner.release (), - [](TCServer* rep) { tc_server_free (rep); }); + inner = unique_tcserver_ptr(other.inner.release(), [](TCServer *rep) { tc_server_free(rep); }); } //////////////////////////////////////////////////////////////////////////////// -tc::Server& tc::Server::operator= (tc::Server &&other) noexcept -{ +tc::Server &tc::Server::operator=(tc::Server &&other) noexcept { if (this != &other) { // move inner from other - inner = unique_tcserver_ptr ( - other.inner.release (), - [](TCServer* rep) { tc_server_free (rep); }); + inner = unique_tcserver_ptr(other.inner.release(), [](TCServer *rep) { tc_server_free(rep); }); } return *this; } diff --git a/src/tc/Server.h b/src/tc/Server.h index bc5266b4d..489d73425 100644 --- a/src/tc/Server.h +++ b/src/tc/Server.h @@ -27,59 +27,59 @@ #ifndef INCLUDED_TC_SERVER #define INCLUDED_TC_SERVER -#include #include #include #include +#include #include + #include "tc/ffi.h" namespace tc { - // a unique_ptr to a TCServer which will automatically free the value when - // it goes out of scope. - using unique_tcserver_ptr = std::unique_ptr< - tc::ffi::TCServer, - std::function>; +// a unique_ptr to a TCServer which will automatically free the value when +// it goes out of scope. +using unique_tcserver_ptr = + std::unique_ptr>; - // Server wraps the TCServer type, managing its memory, errors, and so on. - // - // Except as noted, method names match the suffix to `tc_server_..`. - class Server - { - public: - // Construct a null server - Server () = default; +// Server wraps the TCServer type, managing its memory, errors, and so on. +// +// Except as noted, method names match the suffix to `tc_server_..`. +class Server { + public: + // Construct a null server + Server() = default; - // Construct a local server (tc_server_new_local). - static Server new_local (const std::string& server_dir); + // Construct a local server (tc_server_new_local). + static Server new_local(const std::string &server_dir); - // Construct a remote server (tc_server_new_sync). - static Server new_sync (const std::string &url, const std::string &client_id, const std::string &encryption_secret); + // Construct a remote server (tc_server_new_sync). + static Server new_sync(const std::string &url, const std::string &client_id, + const std::string &encryption_secret); - // Construct a GCP server (tc_server_new_gcp). - static Server new_gcp (const std::string &bucket, const std::string &credential_path, const std::string &encryption_secret); + // Construct a GCP server (tc_server_new_gcp). + static Server new_gcp(const std::string &bucket, const std::string &credential_path, + const std::string &encryption_secret); - // This object "owns" inner, so copy is not allowed. - Server (const Server &) = delete; - Server &operator=(const Server &) = delete; + // This object "owns" inner, so copy is not allowed. + Server(const Server &) = delete; + Server &operator=(const Server &) = delete; - // Explicit move constructor and assignment - Server (Server &&) noexcept; - Server &operator=(Server &&) noexcept; + // Explicit move constructor and assignment + Server(Server &&) noexcept; + Server &operator=(Server &&) noexcept; - protected: - Server (unique_tcserver_ptr inner) : inner(std::move(inner)) {}; + protected: + Server(unique_tcserver_ptr inner) : inner(std::move(inner)) {}; - unique_tcserver_ptr inner; + unique_tcserver_ptr inner; - // Replica accesses the inner pointer to call tc_replica_sync - friend class Replica; - - // construct an error message from the given string. - std::string server_error (tc::ffi::TCString string); - }; -} + // Replica accesses the inner pointer to call tc_replica_sync + friend class Replica; + // construct an error message from the given string. + std::string server_error(tc::ffi::TCString string); +}; +} // namespace tc #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Task.cpp b/src/tc/Task.cpp index 7b3a4e217..5b5e5fd9a 100644 --- a/src/tc/Task.cpp +++ b/src/tc/Task.cpp @@ -28,72 +28,55 @@ // cmake.h include header must come first #include + #include "tc/Task.h" #include "tc/util.h" using namespace tc::ffi; //////////////////////////////////////////////////////////////////////////////// -tc::Task::Task (TCTask *tctask) -{ - inner = unique_tctask_ptr( - tctask, - [](TCTask* task) { tc_task_free(task); }); +tc::Task::Task(TCTask* tctask) { + inner = unique_tctask_ptr(tctask, [](TCTask* task) { tc_task_free(task); }); } //////////////////////////////////////////////////////////////////////////////// -tc::Task::Task (Task &&other) noexcept -{ +tc::Task::Task(Task&& other) noexcept { // move inner from other - inner = unique_tctask_ptr( - other.inner.release(), - [](TCTask* task) { tc_task_free(task); }); + inner = unique_tctask_ptr(other.inner.release(), [](TCTask* task) { tc_task_free(task); }); } //////////////////////////////////////////////////////////////////////////////// -tc::Task& tc::Task::operator= (Task &&other) noexcept -{ +tc::Task& tc::Task::operator=(Task&& other) noexcept { if (this != &other) { // move inner from other - inner = unique_tctask_ptr( - other.inner.release(), - [](TCTask* task) { tc_task_free(task); }); + inner = unique_tctask_ptr(other.inner.release(), [](TCTask* task) { tc_task_free(task); }); } return *this; } //////////////////////////////////////////////////////////////////////////////// -void tc::Task::to_mut (TCReplica *replica) -{ - tc_task_to_mut(&*inner, replica); -} +void tc::Task::to_mut(TCReplica* replica) { tc_task_to_mut(&*inner, replica); } //////////////////////////////////////////////////////////////////////////////// -void tc::Task::to_immut () -{ - tc_task_to_immut(&*inner); -} +void tc::Task::to_immut() { tc_task_to_immut(&*inner); } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Task::get_uuid () const -{ +std::string tc::Task::get_uuid() const { auto uuid = tc_task_get_uuid(&*inner); return tc2uuid(uuid); } //////////////////////////////////////////////////////////////////////////////// -tc::Status tc::Task::get_status () const -{ +tc::Status tc::Task::get_status() const { auto status = tc_task_get_status(&*inner); return tc::Status(status); } //////////////////////////////////////////////////////////////////////////////// -std::map tc::Task::get_taskmap () const -{ - TCKVList kv = tc_task_get_taskmap (&*inner); +std::map tc::Task::get_taskmap() const { + TCKVList kv = tc_task_get_taskmap(&*inner); if (!kv.items) { - throw task_error (); + throw task_error(); } std::map taskmap; @@ -107,89 +90,72 @@ std::map tc::Task::get_taskmap () const } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Task::get_description () const -{ +std::string tc::Task::get_description() const { auto desc = tc_task_get_description(&*inner); return tc2string(desc); } //////////////////////////////////////////////////////////////////////////////// -std::optional tc::Task::get_value (std::string property) const -{ - auto maybe_desc = tc_task_get_value (&*inner, string2tc(property)); +std::optional tc::Task::get_value(std::string property) const { + auto maybe_desc = tc_task_get_value(&*inner, string2tc(property)); if (maybe_desc.ptr == NULL) { return std::nullopt; } return std::make_optional(tc2string(maybe_desc)); } -bool tc::Task::is_waiting () const -{ - return tc_task_is_waiting (&*inner); -} +bool tc::Task::is_waiting() const { return tc_task_is_waiting(&*inner); } //////////////////////////////////////////////////////////////////////////////// -bool tc::Task::is_active () const -{ - return tc_task_is_active (&*inner); -} +bool tc::Task::is_active() const { return tc_task_is_active(&*inner); } //////////////////////////////////////////////////////////////////////////////// -bool tc::Task::is_blocked () const -{ - return tc_task_is_blocked (&*inner); -} +bool tc::Task::is_blocked() const { return tc_task_is_blocked(&*inner); } //////////////////////////////////////////////////////////////////////////////// -bool tc::Task::is_blocking () const -{ - return tc_task_is_blocking (&*inner); -} +bool tc::Task::is_blocking() const { return tc_task_is_blocking(&*inner); } //////////////////////////////////////////////////////////////////////////////// -void tc::Task::set_status (tc::Status status) -{ - TCResult res = tc_task_set_status (&*inner, (TCStatus)status); +void tc::Task::set_status(tc::Status status) { + TCResult res = tc_task_set_status(&*inner, (TCStatus)status); if (res != TC_RESULT_OK) { - throw task_error (); + throw task_error(); } } //////////////////////////////////////////////////////////////////////////////// -void tc::Task::set_value (std::string property, std::optional value) -{ +void tc::Task::set_value(std::string property, std::optional value) { TCResult res; if (value.has_value()) { - res = tc_task_set_value (&*inner, string2tc(property), string2tc(value.value())); + res = tc_task_set_value(&*inner, string2tc(property), string2tc(value.value())); } else { TCString nullstr; nullstr.ptr = NULL; - res = tc_task_set_value (&*inner, string2tc(property), nullstr); + res = tc_task_set_value(&*inner, string2tc(property), nullstr); } if (res != TC_RESULT_OK) { - throw task_error (); + throw task_error(); } } //////////////////////////////////////////////////////////////////////////////// -void tc::Task::set_modified (time_t modified) -{ - TCResult res = tc_task_set_modified (&*inner, modified); +void tc::Task::set_modified(time_t modified) { + TCResult res = tc_task_set_modified(&*inner, modified); if (res != TC_RESULT_OK) { - throw task_error (); + throw task_error(); } } //////////////////////////////////////////////////////////////////////////////// -std::string tc::Task::task_error () const { - TCString error = tc_task_error (&*inner); +std::string tc::Task::task_error() const { + TCString error = tc_task_error(&*inner); std::string errmsg; if (!error.ptr) { - errmsg = std::string ("Unknown TaskChampion error"); + errmsg = std::string("Unknown TaskChampion error"); } else { - errmsg = std::string (tc_string_content (&error)); + errmsg = std::string(tc_string_content(&error)); } - tc_string_free (&error); + tc_string_free(&error); return errmsg; } diff --git a/src/tc/Task.h b/src/tc/Task.h index 2710768d3..d087dc810 100644 --- a/src/tc/Task.h +++ b/src/tc/Task.h @@ -27,101 +27,101 @@ #ifndef INCLUDED_TC_TASK #define INCLUDED_TC_TASK -#include #include -#include #include +#include #include +#include + #include "tc/ffi.h" namespace tc { - class Replica; - class ReplicaGuard; +class Replica; +class ReplicaGuard; - enum Status { - Pending = tc::ffi::TC_STATUS_PENDING, - Completed = tc::ffi::TC_STATUS_COMPLETED, - Deleted = tc::ffi::TC_STATUS_DELETED, - Recurring = tc::ffi::TC_STATUS_RECURRING, - Unknown = tc::ffi::TC_STATUS_UNKNOWN, - }; +enum Status { + Pending = tc::ffi::TC_STATUS_PENDING, + Completed = tc::ffi::TC_STATUS_COMPLETED, + Deleted = tc::ffi::TC_STATUS_DELETED, + Recurring = tc::ffi::TC_STATUS_RECURRING, + Unknown = tc::ffi::TC_STATUS_UNKNOWN, +}; - // a unique_ptr to a TCReplica which will automatically free the value when - // it goes out of scope. - using unique_tctask_ptr = std::unique_ptr< - tc::ffi::TCTask, - std::function>; +// a unique_ptr to a TCReplica which will automatically free the value when +// it goes out of scope. +using unique_tctask_ptr = std::unique_ptr>; +// Task wraps the TCTask type, managing its memory, errors, and so on. +// +// Except as noted, method names match the suffix to `tc_task_..`. +class Task { + protected: + // Tasks may only be created and made mutable/immutable + // by tc::Replica + friend class tc::Replica; + explicit Task(tc::ffi::TCTask *); - // Task wraps the TCTask type, managing its memory, errors, and so on. - // - // Except as noted, method names match the suffix to `tc_task_..`. - class Task - { - protected: - // Tasks may only be created and made mutable/immutable - // by tc::Replica - friend class tc::Replica; - explicit Task (tc::ffi::TCTask *); + // RplicaGuard handles mut/immut + friend class tc::ReplicaGuard; + void to_mut(tc::ffi::TCReplica *); + void to_immut(); - // RplicaGuard handles mut/immut - friend class tc::ReplicaGuard; - void to_mut(tc::ffi::TCReplica *); - void to_immut(); + public: + // This object "owns" inner, so copy is not allowed. + Task(const Task &) = delete; + Task &operator=(const Task &) = delete; - public: - // This object "owns" inner, so copy is not allowed. - Task (const Task &) = delete; - Task &operator=(const Task &) = delete; + // Explicit move constructor and assignment + Task(Task &&) noexcept; + Task &operator=(Task &&) noexcept; - // Explicit move constructor and assignment - Task (Task &&) noexcept; - Task &operator=(Task &&) noexcept; + std::string get_uuid() const; + Status get_status() const; + std::map get_taskmap() const; + std::string get_description() const; + std::optional get_value(std::string property) const; + // TODO: time_t tc_task_get_entry(struct TCTask *task); + // TODO: time_t tc_task_get_wait(struct TCTask *task); + // TODO: time_t tc_task_get_modified(struct TCTask *task); + bool is_waiting() const; + bool is_active() const; + bool is_blocked() const; + bool is_blocking() const; + // TODO: bool tc_task_has_tag(struct TCTask *task, struct TCString tag); + // TODO: struct TCStringList tc_task_get_tags(struct TCTask *task); + // TODO: struct TCAnnotationList tc_task_get_annotations(struct TCTask *task); + // TODO: struct TCString tc_task_get_uda(struct TCTask *task, struct TCString ns, struct TCString + // key); + // TODO: struct TCString tc_task_get_legacy_uda(struct TCTask *task, struct TCString key); + // TODO: struct TCUdaList tc_task_get_udas(struct TCTask *task); + // TODO: struct TCUdaList tc_task_get_legacy_udas(struct TCTask *task); + void set_status(Status status); + // TODO: TCResult tc_task_set_description(struct TCTask *task, struct TCString description); + void set_value(std::string property, std::optional value); + // TODO: TCResult tc_task_set_entry(struct TCTask *task, time_t entry); + // TODO: TCResult tc_task_set_wait(struct TCTask *task, time_t wait); + void set_modified(time_t modified); + // TODO: TCResult tc_task_start(struct TCTask *task); + // TODO: TCResult tc_task_stop(struct TCTask *task); + // TODO: TCResult tc_task_done(struct TCTask *task); + // TODO: TCResult tc_task_delete(struct TCTask *task); + // TODO: TCResult tc_task_add_tag(struct TCTask *task, struct TCString tag); + // TODO: TCResult tc_task_remove_tag(struct TCTask *task, struct TCString tag); + // TODO: TCResult tc_task_add_annotation(struct TCTask *task, struct TCAnnotation *annotation); + // TODO: TCResult tc_task_remove_annotation(struct TCTask *task, int64_t entry); + // TODO: TCResult tc_task_set_uda(struct TCTask *task, + // TODO: TCResult tc_task_remove_uda(struct TCTask *task, struct TCString ns, struct TCString + // key); + // TODO: TCResult tc_task_set_legacy_uda(struct TCTask *task, struct TCString key, struct TCString + // value); + // TODO: TCResult tc_task_remove_legacy_uda(struct TCTask *task, struct TCString key); - std::string get_uuid () const; - Status get_status () const; - std::map get_taskmap() const; - std::string get_description() const; - std::optional get_value(std::string property) const; -// TODO: time_t tc_task_get_entry(struct TCTask *task); -// TODO: time_t tc_task_get_wait(struct TCTask *task); -// TODO: time_t tc_task_get_modified(struct TCTask *task); - bool is_waiting() const; - bool is_active() const; - bool is_blocked() const; - bool is_blocking() const; -// TODO: bool tc_task_has_tag(struct TCTask *task, struct TCString tag); -// TODO: struct TCStringList tc_task_get_tags(struct TCTask *task); -// TODO: struct TCAnnotationList tc_task_get_annotations(struct TCTask *task); -// TODO: struct TCString tc_task_get_uda(struct TCTask *task, struct TCString ns, struct TCString key); -// TODO: struct TCString tc_task_get_legacy_uda(struct TCTask *task, struct TCString key); -// TODO: struct TCUdaList tc_task_get_udas(struct TCTask *task); -// TODO: struct TCUdaList tc_task_get_legacy_udas(struct TCTask *task); - void set_status(Status status); -// TODO: TCResult tc_task_set_description(struct TCTask *task, struct TCString description); - void set_value(std::string property, std::optional value); -// TODO: TCResult tc_task_set_entry(struct TCTask *task, time_t entry); -// TODO: TCResult tc_task_set_wait(struct TCTask *task, time_t wait); - void set_modified(time_t modified); -// TODO: TCResult tc_task_start(struct TCTask *task); -// TODO: TCResult tc_task_stop(struct TCTask *task); -// TODO: TCResult tc_task_done(struct TCTask *task); -// TODO: TCResult tc_task_delete(struct TCTask *task); -// TODO: TCResult tc_task_add_tag(struct TCTask *task, struct TCString tag); -// TODO: TCResult tc_task_remove_tag(struct TCTask *task, struct TCString tag); -// TODO: TCResult tc_task_add_annotation(struct TCTask *task, struct TCAnnotation *annotation); -// TODO: TCResult tc_task_remove_annotation(struct TCTask *task, int64_t entry); -// TODO: TCResult tc_task_set_uda(struct TCTask *task, -// TODO: TCResult tc_task_remove_uda(struct TCTask *task, struct TCString ns, struct TCString key); -// TODO: TCResult tc_task_set_legacy_uda(struct TCTask *task, struct TCString key, struct TCString value); -// TODO: TCResult tc_task_remove_legacy_uda(struct TCTask *task, struct TCString key); + private: + unique_tctask_ptr inner; - private: - unique_tctask_ptr inner; - - std::string task_error () const; // tc_task_error - }; -} + std::string task_error() const; // tc_task_error +}; +} // namespace tc // TODO: struct TCTask *tc_task_list_take(struct TCTaskList *tasks, size_t index); // TODO: void tc_task_list_free(struct TCTaskList *tasks); diff --git a/src/tc/WorkingSet.cpp b/src/tc/WorkingSet.cpp index b089f347c..1adeb8f9d 100644 --- a/src/tc/WorkingSet.cpp +++ b/src/tc/WorkingSet.cpp @@ -28,70 +28,57 @@ // cmake.h include header must come first #include -#include "tc/WorkingSet.h" + #include "tc/Task.h" +#include "tc/WorkingSet.h" #include "tc/util.h" using namespace tc::ffi; //////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet::WorkingSet (WorkingSet &&other) noexcept -{ +tc::WorkingSet::WorkingSet(WorkingSet&& other) noexcept { // move inner from other - inner = unique_tcws_ptr ( - other.inner.release (), - [](TCWorkingSet* ws) { tc_working_set_free (ws); }); + inner = unique_tcws_ptr(other.inner.release(), [](TCWorkingSet* ws) { tc_working_set_free(ws); }); } //////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet& tc::WorkingSet::operator= (WorkingSet &&other) noexcept -{ +tc::WorkingSet& tc::WorkingSet::operator=(WorkingSet&& other) noexcept { if (this != &other) { // move inner from other - inner = unique_tcws_ptr ( - other.inner.release (), - [](TCWorkingSet* ws) { tc_working_set_free (ws); }); + inner = + unique_tcws_ptr(other.inner.release(), [](TCWorkingSet* ws) { tc_working_set_free(ws); }); } return *this; } //////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet::WorkingSet (tc::ffi::TCWorkingSet* tcws) -{ - inner = unique_tcws_ptr ( - tcws, - [](TCWorkingSet* ws) { tc_working_set_free (ws); }); +tc::WorkingSet::WorkingSet(tc::ffi::TCWorkingSet* tcws) { + inner = unique_tcws_ptr(tcws, [](TCWorkingSet* ws) { tc_working_set_free(ws); }); } //////////////////////////////////////////////////////////////////////////////// -size_t tc::WorkingSet::len () const noexcept -{ - return tc_working_set_len (&*inner); +size_t tc::WorkingSet::len() const noexcept { return tc_working_set_len(&*inner); } + +//////////////////////////////////////////////////////////////////////////////// +size_t tc::WorkingSet::largest_index() const noexcept { + return tc_working_set_largest_index(&*inner); } //////////////////////////////////////////////////////////////////////////////// -size_t tc::WorkingSet::largest_index () const noexcept -{ - return tc_working_set_largest_index (&*inner); -} - -//////////////////////////////////////////////////////////////////////////////// -std::optional tc::WorkingSet::by_index (size_t index) const noexcept -{ +std::optional tc::WorkingSet::by_index(size_t index) const noexcept { TCUuid uuid; - if (tc_working_set_by_index (&*inner, index, &uuid)) { - return std::make_optional (tc2uuid (uuid)); + if (tc_working_set_by_index(&*inner, index, &uuid)) { + return std::make_optional(tc2uuid(uuid)); } else { return std::nullopt; } } //////////////////////////////////////////////////////////////////////////////// -std::optional tc::WorkingSet::by_uuid (const std::string &uuid) const noexcept -{ - auto index = tc_working_set_by_uuid (&*inner, uuid2tc (uuid)); +std::optional tc::WorkingSet::by_uuid(const std::string& uuid) const noexcept { + auto index = tc_working_set_by_uuid(&*inner, uuid2tc(uuid)); if (index > 0) { - return std::make_optional (index); + return std::make_optional(index); } else { return std::nullopt; } diff --git a/src/tc/WorkingSet.h b/src/tc/WorkingSet.h index 7b6c63423..e9a3a5d5c 100644 --- a/src/tc/WorkingSet.h +++ b/src/tc/WorkingSet.h @@ -27,49 +27,48 @@ #ifndef INCLUDED_TC_WORKINGSET #define INCLUDED_TC_WORKINGSET -#include #include #include #include -#include "tc/ffi.h" +#include + #include "tc/Task.h" +#include "tc/ffi.h" namespace tc { - class Task; +class Task; - // a unique_ptr to a TCWorkingSet which will automatically free the value when - // it goes out of scope. - using unique_tcws_ptr = std::unique_ptr< - tc::ffi::TCWorkingSet, - std::function>; +// a unique_ptr to a TCWorkingSet which will automatically free the value when +// it goes out of scope. +using unique_tcws_ptr = + std::unique_ptr>; - // WorkingSet wraps the TCWorkingSet type, managing its memory, errors, and so on. - // - // Except as noted, method names match the suffix to `tc_working_set_..`. - class WorkingSet - { - protected: - friend class tc::Replica; - WorkingSet (tc::ffi::TCWorkingSet*); // via tc_replica_working_set +// WorkingSet wraps the TCWorkingSet type, managing its memory, errors, and so on. +// +// Except as noted, method names match the suffix to `tc_working_set_..`. +class WorkingSet { + protected: + friend class tc::Replica; + WorkingSet(tc::ffi::TCWorkingSet *); // via tc_replica_working_set - public: - // This object "owns" inner, so copy is not allowed. - WorkingSet (const WorkingSet &) = delete; - WorkingSet &operator=(const WorkingSet &) = delete; + public: + // This object "owns" inner, so copy is not allowed. + WorkingSet(const WorkingSet &) = delete; + WorkingSet &operator=(const WorkingSet &) = delete; - // Explicit move constructor and assignment - WorkingSet (WorkingSet &&) noexcept; - WorkingSet &operator=(WorkingSet &&) noexcept; + // Explicit move constructor and assignment + WorkingSet(WorkingSet &&) noexcept; + WorkingSet &operator=(WorkingSet &&) noexcept; - size_t len () const noexcept; // tc_working_set_len - size_t largest_index () const noexcept; // tc_working_set_largest_index - std::optional by_index (size_t index) const noexcept; // tc_working_set_by_index - std::optional by_uuid (const std::string &index) const noexcept; // tc_working_set_by_uuid + size_t len() const noexcept; // tc_working_set_len + size_t largest_index() const noexcept; // tc_working_set_largest_index + std::optional by_index(size_t index) const noexcept; // tc_working_set_by_index + std::optional by_uuid(const std::string &index) const noexcept; // tc_working_set_by_uuid - private: - unique_tcws_ptr inner; - }; -} + private: + unique_tcws_ptr inner; +}; +} // namespace tc #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/lib/taskchampion.h b/src/tc/lib/taskchampion.h index f0571d469..3d0cf5e9a 100644 --- a/src/tc/lib/taskchampion.h +++ b/src/tc/lib/taskchampion.h @@ -77,7 +77,7 @@ #define EXTERN_C extern "C" #else #define EXTERN_C -#endif // __cplusplus +#endif // __cplusplus // ***** TCResult ***** // @@ -85,15 +85,15 @@ // the associated object's `tc_.._error` method will return an error message. enum TCResult #ifdef __cplusplus - : int32_t -#endif // __cplusplus + : int32_t +#endif // __cplusplus { TC_RESULT_ERROR = -1, TC_RESULT_OK = 0, }; #ifndef __cplusplus typedef int32_t TCResult; -#endif // __cplusplus +#endif // __cplusplus // ***** TCString ***** // @@ -135,10 +135,10 @@ typedef int32_t TCResult; // // TCString is not threadsafe. typedef struct TCString { - void *ptr; // opaque, but may be checked for NULL - size_t _u1; // reserved - size_t _u2; // reserved - uint8_t _u3; // reserved + void *ptr; // opaque, but may be checked for NULL + size_t _u1; // reserved + size_t _u2; // reserved + uint8_t _u3; // reserved } TCString; // Create a new TCString referencing the given C string. The C string must remain valid and @@ -214,8 +214,8 @@ typedef struct TCStringList { struct TCString *items; } TCStringList; -// Free a TCStringList instance. The instance, and all TCStringList it contains, must not be used after -// this call. +// Free a TCStringList instance. The instance, and all TCStringList it contains, must not be used +// after this call. // // When this call returns, the `items` pointer will be NULL, signalling an invalid TCStringList. EXTERN_C void tc_string_list_free(struct TCStringList *tcstrings); @@ -310,8 +310,8 @@ typedef struct TCAnnotationList { struct TCAnnotation *items; } TCAnnotationList; -// Free a TCAnnotationList instance. The instance, and all TCAnnotations it contains, must not be used after -// this call. +// Free a TCAnnotationList instance. The instance, and all TCAnnotations it contains, must not be +// used after this call. // // When this call returns, the `items` pointer will be NULL, signalling an invalid TCAnnotationList. EXTERN_C void tc_annotation_list_free(struct TCAnnotationList *tcanns); @@ -390,10 +390,10 @@ EXTERN_C void tc_kv_list_free(struct TCKVList *tckvs); // The status of a task, as defined by the task data model. #ifdef __cplusplus typedef enum TCStatus : int32_t { -#else // __cplusplus +#else // __cplusplus typedef int32_t TCStatus; enum TCStatus { -#endif // __cplusplus +#endif // __cplusplus TC_STATUS_PENDING = 0, TC_STATUS_COMPLETED = 1, TC_STATUS_DELETED = 2, @@ -403,9 +403,9 @@ enum TCStatus { TC_STATUS_UNKNOWN = -1, #ifdef __cplusplus } TCStatus; -#else // __cplusplus +#else // __cplusplus }; -#endif // __cplusplus +#endif // __cplusplus // ***** TCServer ***** // @@ -424,7 +424,8 @@ typedef struct TCServer TCServer; // returned. The caller must free this string. // // The server must be freed after it is used - tc_replica_sync does not automatically free it. -EXTERN_C struct TCServer *tc_server_new_local(struct TCString server_dir, struct TCString *error_out); +EXTERN_C struct TCServer *tc_server_new_local(struct TCString server_dir, + struct TCString *error_out); // Create a new TCServer that connects to a remote server. See the TaskChampion docs for the // description of the arguments. @@ -433,10 +434,9 @@ EXTERN_C struct TCServer *tc_server_new_local(struct TCString server_dir, struct // returned. The caller must free this string. // // The server must be freed after it is used - tc_replica_sync does not automatically free it. -EXTERN_C struct TCServer *tc_server_new_sync(struct TCString url, - struct TCUuid client_id, - struct TCString encryption_secret, - struct TCString *error_out); +EXTERN_C struct TCServer *tc_server_new_sync(struct TCString url, struct TCUuid client_id, + struct TCString encryption_secret, + struct TCString *error_out); // Create a new TCServer that connects to the Google Cloud Platform. See the TaskChampion docs // for the description of the arguments. @@ -445,10 +445,9 @@ EXTERN_C struct TCServer *tc_server_new_sync(struct TCString url, // returned. The caller must free this string. // // The server must be freed after it is used - tc_replica_sync does not automatically free it. -EXTERN_C struct TCServer *tc_server_new_gcp(struct TCString bucket, - struct TCString credential_path, - struct TCString encryption_secret, - struct TCString *error_out); +EXTERN_C struct TCServer *tc_server_new_gcp(struct TCString bucket, struct TCString credential_path, + struct TCString encryption_secret, + struct TCString *error_out); // Free a server. The server may not be used after this function returns and must not be freed // more than once. @@ -483,31 +482,31 @@ typedef struct TCReplica TCReplica; // ***** TCReplicaOpType ***** enum TCReplicaOpType #ifdef __cplusplus - : uint32_t -#endif // __cplusplus + : uint32_t +#endif // __cplusplus { - Create = 0, - Delete = 1, - Update = 2, - UndoPoint = 3, + Create = 0, + Delete = 1, + Update = 2, + UndoPoint = 3, }; #ifndef __cplusplus typedef uint32_t TCReplicaOpType; -#endif // __cplusplus +#endif // __cplusplus // ***** TCReplicaOp ***** struct TCReplicaOp { - TCReplicaOpType operation_type; - void* inner; + TCReplicaOpType operation_type; + void *inner; }; typedef struct TCReplicaOp TCReplicaOp; // ***** TCReplicaOpList ***** struct TCReplicaOpList { - struct TCReplicaOp *items; - size_t len; - size_t capacity; + struct TCReplicaOp *items; + size_t len; + size_t capacity; }; typedef struct TCReplicaOpList TCReplicaOpList; @@ -519,9 +518,8 @@ EXTERN_C struct TCReplica *tc_replica_new_in_memory(void); // Create a new TCReplica with an on-disk database having the given filename. On error, a string // is written to the error_out parameter (if it is not NULL) and NULL is returned. The caller // must free this string. -EXTERN_C struct TCReplica *tc_replica_new_on_disk(struct TCString path, - bool create_if_missing, - struct TCString *error_out); +EXTERN_C struct TCReplica *tc_replica_new_on_disk(struct TCString path, bool create_if_missing, + struct TCString *error_out); // Add an UndoPoint, if one has not already been added by this Replica. This occurs automatically // when a change is made. The `force` flag allows forcing a new UndoPoint even if one has already @@ -545,7 +543,8 @@ EXTERN_C struct TCTaskList tc_replica_all_tasks(struct TCReplica *rep); // // If undone_out is not NULL, then on success it is set to 1 if operations were undone, or 0 if // there are no operations that can be done. -EXTERN_C TCResult tc_replica_commit_undo_ops(struct TCReplica *rep, TCReplicaOpList tc_undo_ops, int32_t *undone_out); +EXTERN_C TCResult tc_replica_commit_undo_ops(struct TCReplica *rep, TCReplicaOpList tc_undo_ops, + int32_t *undone_out); // Delete a task. The task must exist. Note that this is different from setting status to // Deleted; this is the final purge of the task. @@ -582,14 +581,14 @@ EXTERN_C TCReplicaOpList tc_replica_get_undo_ops(struct TCReplica *rep); // Create a new task. The task must not already exist. // // Returns the task, or NULL on error. -EXTERN_C struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid); +EXTERN_C struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, + struct TCUuid tcuuid); // Create a new task. The task must not already exist. // // Returns the task, or NULL on error. -EXTERN_C struct TCTask *tc_replica_new_task(struct TCReplica *rep, - enum TCStatus status, - struct TCString description); +EXTERN_C struct TCTask *tc_replica_new_task(struct TCReplica *rep, enum TCStatus status, + struct TCString description); // Get the number of local, un-synchronized operations (not including undo points), or -1 on // error. @@ -606,7 +605,8 @@ EXTERN_C TCResult tc_replica_rebuild_working_set(struct TCReplica *rep, bool ren // Synchronize this replica with a server. // // The `server` argument remains owned by the caller, and must be freed explicitly. -EXTERN_C TCResult tc_replica_sync(struct TCReplica *rep, struct TCServer *server, bool avoid_snapshots); +EXTERN_C TCResult tc_replica_sync(struct TCReplica *rep, struct TCServer *server, + bool avoid_snapshots); // Get the current working set for this replica. The resulting value must be freed // with tc_working_set_free. @@ -636,8 +636,8 @@ EXTERN_C struct TCString tc_replica_op_get_uuid(struct TCReplicaOp *op); // Return value field of ReplicaOp. EXTERN_C struct TCString tc_replica_op_get_value(struct TCReplicaOp *op); -// Free a vector of ReplicaOp. The vector may not be used after this function returns and must not be freed -// more than once. +// Free a vector of ReplicaOp. The vector may not be used after this function returns and must not +// be freed more than once. EXTERN_C void tc_replica_op_list_free(struct TCReplicaOpList *oplist); // ***** TCTask ***** @@ -719,7 +719,8 @@ EXTERN_C struct TCKVList tc_task_get_taskmap(struct TCTask *task); // Get the named UDA from the task. // // Returns a TCString with NULL ptr field if the UDA does not exist. -EXTERN_C struct TCString tc_task_get_uda(struct TCTask *task, struct TCString ns, struct TCString key); +EXTERN_C struct TCString tc_task_get_uda(struct TCTask *task, struct TCString ns, + struct TCString key); // Get all UDAs for this task. // @@ -809,7 +810,8 @@ EXTERN_C TCResult tc_task_set_description(struct TCTask *task, struct TCString d EXTERN_C TCResult tc_task_set_entry(struct TCTask *task, time_t entry); // Set a legacy UDA on a mutable task. -EXTERN_C TCResult tc_task_set_legacy_uda(struct TCTask *task, struct TCString key, struct TCString value); +EXTERN_C TCResult tc_task_set_legacy_uda(struct TCTask *task, struct TCString key, + struct TCString value); // Set a mutable task's modified timestamp. The value cannot be zero. EXTERN_C TCResult tc_task_set_modified(struct TCTask *task, time_t modified); @@ -818,13 +820,12 @@ EXTERN_C TCResult tc_task_set_modified(struct TCTask *task, time_t modified); EXTERN_C TCResult tc_task_set_status(struct TCTask *task, enum TCStatus status); // Set a UDA on a mutable task. -EXTERN_C TCResult tc_task_set_uda(struct TCTask *task, - struct TCString ns, - struct TCString key, - struct TCString value); +EXTERN_C TCResult tc_task_set_uda(struct TCTask *task, struct TCString ns, struct TCString key, + struct TCString value); // Set a mutable task's property value by name. If value.ptr is NULL, the property is removed. -EXTERN_C TCResult tc_task_set_value(struct TCTask *task, struct TCString property, struct TCString value); +EXTERN_C TCResult tc_task_set_value(struct TCTask *task, struct TCString property, + struct TCString value); // Set a mutable task's wait timestamp. Pass wait=0 to unset the wait field. EXTERN_C TCResult tc_task_set_wait(struct TCTask *task, time_t wait); @@ -916,7 +917,8 @@ typedef struct TCWorkingSet TCWorkingSet; // Get the UUID for the task at the given index. Returns true if the UUID exists in the working // set. If not, returns false and does not change uuid_out. -EXTERN_C bool tc_working_set_by_index(struct TCWorkingSet *ws, size_t index, struct TCUuid *uuid_out); +EXTERN_C bool tc_working_set_by_index(struct TCWorkingSet *ws, size_t index, + struct TCUuid *uuid_out); // Get the working set index for the task with the given UUID. Returns 0 if the task is not in // the working set. diff --git a/src/tc/util.cpp b/src/tc/util.cpp index 318f9ed0f..3339615ce 100644 --- a/src/tc/util.cpp +++ b/src/tc/util.cpp @@ -27,8 +27,9 @@ #include // cmake.h include header must come first -#include #include +#include + #include "tc/Replica.h" #include "tc/Task.h" @@ -36,48 +37,43 @@ using namespace tc::ffi; namespace tc { //////////////////////////////////////////////////////////////////////////////// -TCString string2tc (const std::string& str) -{ - return tc_string_clone_with_len (str.data (), str.size ()); +TCString string2tc(const std::string& str) { + return tc_string_clone_with_len(str.data(), str.size()); } //////////////////////////////////////////////////////////////////////////////// -std::string tc2string_clone (const TCString& str) -{ +std::string tc2string_clone(const TCString& str) { size_t len; - auto ptr = tc_string_content_with_len (&str, &len); - auto rv = std::string (ptr, len); + auto ptr = tc_string_content_with_len(&str, &len); + auto rv = std::string(ptr, len); return rv; } //////////////////////////////////////////////////////////////////////////////// -std::string tc2string (TCString& str) -{ +std::string tc2string(TCString& str) { auto rv = tc2string_clone(str); - tc_string_free (&str); + tc_string_free(&str); return rv; } //////////////////////////////////////////////////////////////////////////////// -TCUuid uuid2tc(const std::string& str) -{ +TCUuid uuid2tc(const std::string& str) { TCString tcstr = tc_string_borrow(str.c_str()); TCUuid rv; if (TC_RESULT_OK != tc_uuid_from_str(tcstr, &rv)) { - throw std::string ("invalid UUID"); + throw std::string("invalid UUID"); } return rv; } //////////////////////////////////////////////////////////////////////////////// -std::string tc2uuid (TCUuid& uuid) -{ +std::string tc2uuid(TCUuid& uuid) { char s[TC_UUID_STRING_BYTES]; - tc_uuid_to_buf (uuid, s); + tc_uuid_to_buf(uuid, s); std::string str; - str.assign (s, TC_UUID_STRING_BYTES); + str.assign(s, TC_UUID_STRING_BYTES); return str; } //////////////////////////////////////////////////////////////////////////////// -} +} // namespace tc diff --git a/src/tc/util.h b/src/tc/util.h index 66ca54b2e..87d54cd16 100644 --- a/src/tc/util.h +++ b/src/tc/util.h @@ -28,24 +28,25 @@ #define INCLUDED_TC_UTIL #include + #include "tc/ffi.h" namespace tc { - // convert a std::string into a TCString, copying the contained data - tc::ffi::TCString string2tc(const std::string&); +// convert a std::string into a TCString, copying the contained data +tc::ffi::TCString string2tc(const std::string&); - // convert a TCString into a std::string, leaving the TCString as-is - std::string tc2string_clone(const tc::ffi::TCString&); +// convert a TCString into a std::string, leaving the TCString as-is +std::string tc2string_clone(const tc::ffi::TCString&); - // convert a TCString into a std::string, freeing the TCString - std::string tc2string(tc::ffi::TCString&); +// convert a TCString into a std::string, freeing the TCString +std::string tc2string(tc::ffi::TCString&); - // convert a TCUuid into a std::string - std::string tc2uuid(tc::ffi::TCUuid&); +// convert a TCUuid into a std::string +std::string tc2uuid(tc::ffi::TCUuid&); - // parse a std::string into a TCUuid (throwing if parse fails) - tc::ffi::TCUuid uuid2tc(const std::string&); -} +// parse a std::string into a TCUuid (throwing if parse fails) +tc::ffi::TCUuid uuid2tc(const std::string&); +} // namespace tc #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/src/util.cpp b/src/util.cpp index da6eb4f93..04593791f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -34,48 +34,46 @@ #ifdef FREEBSD #define _WITH_GETLINE #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include #include +#include +#include +#include +#include #include -#include +#include +#include +#include #include +#include #include #include -#include -#define STRING_UTIL_CONFIRM_YES "yes" -#define STRING_UTIL_CONFIRM_YES_U "Yes" -#define STRING_UTIL_CONFIRM_NO "no" -#define STRING_UTIL_CONFIRM_ALL "all" -#define STRING_UTIL_CONFIRM_ALL_U "All" -#define STRING_UTIL_CONFIRM_QUIT "quit" +#include +#include +#include +#include +#include +#include +#include + +#define STRING_UTIL_CONFIRM_YES "yes" +#define STRING_UTIL_CONFIRM_YES_U "Yes" +#define STRING_UTIL_CONFIRM_NO "no" +#define STRING_UTIL_CONFIRM_ALL "all" +#define STRING_UTIL_CONFIRM_ALL_U "All" +#define STRING_UTIL_CONFIRM_QUIT "quit" static const char* newline = "\n"; -static const char* noline = ""; +static const char* noline = ""; //////////////////////////////////////////////////////////////////////////////// -static void signal_handler (int s) -{ - if (s == SIGINT) - { +static void signal_handler(int s) { + if (s == SIGINT) { std::cout << "\n\nInterrupted: No changes made.\n"; - exit (1); + exit(1); } } @@ -84,45 +82,38 @@ static void signal_handler (int s) // 1 = yes // 2 = all // 3 = quit -int confirm4 (const std::string& question) -{ - std::vector options {STRING_UTIL_CONFIRM_YES_U, - STRING_UTIL_CONFIRM_YES, - STRING_UTIL_CONFIRM_NO, - STRING_UTIL_CONFIRM_ALL_U, - STRING_UTIL_CONFIRM_ALL, - STRING_UTIL_CONFIRM_QUIT}; - std::vector matches; +int confirm4(const std::string& question) { + std::vector options{STRING_UTIL_CONFIRM_YES_U, STRING_UTIL_CONFIRM_YES, + STRING_UTIL_CONFIRM_NO, STRING_UTIL_CONFIRM_ALL_U, + STRING_UTIL_CONFIRM_ALL, STRING_UTIL_CONFIRM_QUIT}; + std::vector matches; - signal (SIGINT, signal_handler); + signal(SIGINT, signal_handler); - do - { - std::cout << question - << " (" - << options[1] << '/' - << options[2] << '/' - << options[4] << '/' - << options[5] - << ") "; + do { + std::cout << question << " (" << options[1] << '/' << options[2] << '/' << options[4] << '/' + << options[5] << ") "; - std::string answer {""}; - std::getline (std::cin, answer); - Context::getContext ().debug ("STDIN '" + answer + '\''); - answer = std::cin.eof () ? STRING_UTIL_CONFIRM_QUIT : Lexer::lowerCase (Lexer::trim (answer)); - autoComplete (answer, options, matches, 1); // Hard-coded 1. - } - while (! std::cin.eof () && matches.size () != 1); + std::string answer{""}; + std::getline(std::cin, answer); + Context::getContext().debug("STDIN '" + answer + '\''); + answer = std::cin.eof() ? STRING_UTIL_CONFIRM_QUIT : Lexer::lowerCase(Lexer::trim(answer)); + autoComplete(answer, options, matches, 1); // Hard-coded 1. + } while (!std::cin.eof() && matches.size() != 1); - signal (SIGINT, SIG_DFL); + signal(SIGINT, SIG_DFL); - if (matches.size () == 1) - { - if (matches[0] == STRING_UTIL_CONFIRM_YES_U) return 1; - else if (matches[0] == STRING_UTIL_CONFIRM_YES) return 1; - else if (matches[0] == STRING_UTIL_CONFIRM_ALL_U) return 2; - else if (matches[0] == STRING_UTIL_CONFIRM_ALL) return 2; - else if (matches[0] == STRING_UTIL_CONFIRM_QUIT) return 3; + if (matches.size() == 1) { + if (matches[0] == STRING_UTIL_CONFIRM_YES_U) + return 1; + else if (matches[0] == STRING_UTIL_CONFIRM_YES) + return 1; + else if (matches[0] == STRING_UTIL_CONFIRM_ALL_U) + return 2; + else if (matches[0] == STRING_UTIL_CONFIRM_ALL) + return 2; + else if (matches[0] == STRING_UTIL_CONFIRM_QUIT) + return 3; } return 0; @@ -135,16 +126,15 @@ int confirm4 (const std::string& question) // For the implementation details, refer to // https://svnweb.freebsd.org/base/head/sys/kern/kern_uuid.c #if defined(FREEBSD) || defined(OPENBSD) -const std::string uuid () -{ +const std::string uuid() { uuid_t id; uint32_t status; - char *buffer (0); - uuid_create (&id, &status); - uuid_to_string (&id, &buffer, &status); + char* buffer(0); + uuid_create(&id, &status); + uuid_to_string(&id, &buffer, &status); - std::string res (buffer); - free (buffer); + std::string res(buffer); + free(buffer); return res; } @@ -153,26 +143,24 @@ const std::string uuid () //////////////////////////////////////////////////////////////////////////////// #ifndef HAVE_UUID_UNPARSE_LOWER // Older versions of libuuid don't have uuid_unparse_lower(), only uuid_unparse() -void uuid_unparse_lower (uuid_t uu, char *out) -{ - uuid_unparse (uu, out); - // Characters in out are either 0-9, a-z, '-', or A-Z. A-Z is mapped to - // a-z by bitwise or with 0x20, and the others already have this bit set - for (size_t i = 0; i < 36; ++i) out[i] |= 0x20; +void uuid_unparse_lower(uuid_t uu, char* out) { + uuid_unparse(uu, out); + // Characters in out are either 0-9, a-z, '-', or A-Z. A-Z is mapped to + // a-z by bitwise or with 0x20, and the others already have this bit set + for (size_t i = 0; i < 36; ++i) out[i] |= 0x20; } #endif -const std::string uuid () -{ +const std::string uuid() { uuid_t id; - uuid_generate (id); - char buffer[100] {}; - uuid_unparse_lower (id, buffer); + uuid_generate(id); + char buffer[100]{}; + uuid_unparse_lower(id, buffer); // Bug found by Steven de Brouwer. buffer[36] = '\0'; - return std::string (buffer); + return std::string(buffer); } #endif @@ -201,19 +189,15 @@ const std::string uuid () // - delimiter is the character used to split up projects into subprojects. // - defaults to the period, '.' // -const std::string indentProject ( - const std::string& project, - const std::string& whitespace /* = " " */, - char delimiter /* = '.' */) -{ +const std::string indentProject(const std::string& project, + const std::string& whitespace /* = " " */, + char delimiter /* = '.' */) { // Count the delimiters in *i. std::string prefix = ""; std::string::size_type pos = 0; std::string::size_type lastpos = 0; - while ((pos = project.find (delimiter, pos + 1)) != std::string::npos) - { - if (pos != project.size () - 1) - { + while ((pos = project.find(delimiter, pos + 1)) != std::string::npos) { + if (pos != project.size() - 1) { prefix += whitespace; lastpos = pos; } @@ -223,23 +207,19 @@ const std::string indentProject ( if (lastpos == 0) child = project; else - child = project.substr (lastpos + 1); + child = project.substr(lastpos + 1); return prefix + child; } //////////////////////////////////////////////////////////////////////////////// -const std::vector extractParents ( - const std::string& project, - const char& delimiter /* = '.' */) -{ - std::vector vec; +const std::vector extractParents(const std::string& project, + const char& delimiter /* = '.' */) { + std::vector vec; std::string::size_type pos = 0; std::string::size_type copyUntil = 0; - while ((copyUntil = project.find (delimiter, pos + 1)) != std::string::npos) - { - if (copyUntil != project.size () - 1) - vec.push_back (project.substr (0, copyUntil)); + while ((copyUntil = project.find(delimiter, pos + 1)) != std::string::npos) { + if (copyUntil != project.size() - 1) vec.push_back(project.substr(0, copyUntil)); pos = copyUntil; } return vec; @@ -247,75 +227,63 @@ const std::vector extractParents ( //////////////////////////////////////////////////////////////////////////////// #ifndef HAVE_TIMEGM -time_t timegm (struct tm *tm) -{ +time_t timegm(struct tm* tm) { time_t ret; - char *tz; - tz = getenv ("TZ"); - setenv ("TZ", "UTC", 1); - tzset (); - ret = mktime (tm); + char* tz; + tz = getenv("TZ"); + setenv("TZ", "UTC", 1); + tzset(); + ret = mktime(tm); if (tz) - setenv ("TZ", tz, 1); + setenv("TZ", tz, 1); else - unsetenv ("TZ"); - tzset (); + unsetenv("TZ"); + tzset(); return ret; } #endif //////////////////////////////////////////////////////////////////////////////// -bool nontrivial (const std::string& input) -{ +bool nontrivial(const std::string& input) { std::string::size_type i = 0; int character; - while ((character = utf8_next_char (input, i))) - if (! unicodeWhitespace (character)) - return true; + while ((character = utf8_next_char(input, i))) + if (!unicodeWhitespace(character)) return true; return false; } //////////////////////////////////////////////////////////////////////////////// -const char* optionalBlankLine () -{ - return Context::getContext ().verbose ("blank") ? newline : noline; +const char* optionalBlankLine() { + return Context::getContext().verbose("blank") ? newline : noline; } //////////////////////////////////////////////////////////////////////////////// -void setHeaderUnderline (Table& table) -{ +void setHeaderUnderline(Table& table) { // If an alternating row color is specified, notify the table. - if (Context::getContext ().color ()) - { - Color alternate (Context::getContext ().config.get ("color.alternate")); - table.colorOdd (alternate); - table.intraColorOdd (alternate); + if (Context::getContext().color()) { + Color alternate(Context::getContext().config.get("color.alternate")); + table.colorOdd(alternate); + table.intraColorOdd(alternate); - if (Context::getContext ().config.getBoolean ("fontunderline")) - { - table.colorHeader (Color ("underline " + Context::getContext ().config.get ("color.label"))); + if (Context::getContext().config.getBoolean("fontunderline")) { + table.colorHeader(Color("underline " + Context::getContext().config.get("color.label"))); + } else { + table.colorHeader(Color(Context::getContext().config.get("color.label"))); + table.underlineHeaders(); } + } else { + if (Context::getContext().config.getBoolean("fontunderline")) + table.colorHeader(Color("underline")); else - { - table.colorHeader (Color (Context::getContext ().config.get ("color.label"))); - table.underlineHeaders (); - } - } - else - { - if (Context::getContext ().config.getBoolean ("fontunderline")) - table.colorHeader (Color ("underline")); - else - table.underlineHeaders (); + table.underlineHeaders(); } } //////////////////////////////////////////////////////////////////////////////// // Perform strtol on a string and check if the extracted value matches. // -bool extractLongInteger (const std::string& input, long& output) -{ - output = strtol (input.c_str (), nullptr, 10); - return (format ("{1}", output) == input); +bool extractLongInteger(const std::string& input, long& output) { + output = strtol(input.c_str(), nullptr, 10); + return (format("{1}", output) == input); } diff --git a/src/util.h b/src/util.h index 62e8223b0..a4f94a1a9 100644 --- a/src/util.h +++ b/src/util.h @@ -30,10 +30,11 @@ #include // cmake.h include header must come first +#include + +#include #include #include -#include -#include #if defined(FREEBSD) || defined(OPENBSD) #include #else @@ -42,30 +43,26 @@ #include // util.cpp -int confirm4 (const std::string&); +int confirm4(const std::string&); #ifndef HAVE_UUID_UNPARSE_LOWER -void uuid_unparse_lower (uuid_t uu, char *out); +void uuid_unparse_lower(uuid_t uu, char* out); #endif -const std::string uuid (); +const std::string uuid(); -const std::string indentProject ( - const std::string&, - const std::string& whitespace = " ", - char delimiter = '.'); +const std::string indentProject(const std::string&, const std::string& whitespace = " ", + char delimiter = '.'); -const std::vector extractParents ( - const std::string&, - const char& delimiter = '.'); +const std::vector extractParents(const std::string&, const char& delimiter = '.'); #ifndef HAVE_TIMEGM - time_t timegm (struct tm *tm); +time_t timegm(struct tm* tm); #endif -bool nontrivial (const std::string&); -const char* optionalBlankLine (); -void setHeaderUnderline (Table&); -bool extractLongInteger (const std::string&, long&); +bool nontrivial(const std::string&); +const char* optionalBlankLine(); +void setHeaderUnderline(Table&); +bool extractLongInteger(const std::string&, long&); #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/test/README.md b/test/README.md index 6b19ec215..eafc2d956 100644 --- a/test/README.md +++ b/test/README.md @@ -139,4 +139,4 @@ For anyone looking for test-related tasks to take on, here are some suggestions: * All the attribute modifiers need to be tested, only a few are. - * Aliases are not well tested, and fragile. \ No newline at end of file + * Aliases are not well tested, and fragile. diff --git a/test/abbreviation.test.py b/test/abbreviation.test.py index f85c883fa..94b0c4577 100755 --- a/test/abbreviation.test.py +++ b/test/abbreviation.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -88,10 +89,11 @@ class TestAbbreviation(TestCase): class TestBug1006(TestCase): """Bug with expansion of abbreviation "des" in task descriptions and annotations. - It happens for all the shortcuts for column attributes that are automatically - completed. This is because DOM elements are checked before standard words - when strings are tokenized. + It happens for all the shortcuts for column attributes that are automatically + completed. This is because DOM elements are checked before standard words + when strings are tokenized. """ + def setUp(self): self.t = Task() self.t.config("verbose", "affected") @@ -158,6 +160,7 @@ class TestBug1687(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/add.test.py b/test/add.test.py index 60331b7a2..ac635c03d 100755 --- a/test/add.test.py +++ b/test/add.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -58,11 +59,11 @@ class TestAdd(TestCase): def test_floating_point_preservation(self): """924: Verify that floating point numbers are unmolested - Bug 924: '1.0' --> '1.0000' + Bug 924: '1.0' --> '1.0000' """ self.t("add release 1.0") self.t("add 'release 2.0'") - self.t("add \\\"release 3.0\\\"") + self.t('add \\"release 3.0\\"') code, out, err = self.t("_get 1.description") self.assertEqual(out, "release 1.0\n") @@ -76,19 +77,19 @@ class TestAdd(TestCase): def test_escaped_quotes_are_preserved(self): """917: Verify that escaped quotes are preserved - Bug 917: escaping runs amok + Bug 917: escaping runs amok """ self.t("add one \\'two\\' three") - self.t("add four \\\"five\\\" six") + self.t('add four \\"five\\" six') code, out, err = self.t("list") self.assertIn("one 'two' three", out) - self.assertIn("four \"five\" six", out) + self.assertIn('four "five" six', out) def test_extra_space_in_path(self): """884: Test that path-like args are preserved - Bug 884: Extra space in path name. + Bug 884: Extra space in path name. """ self.t("add /one/two/three/") self.t("add '/four/five/six/'") @@ -100,9 +101,9 @@ class TestAdd(TestCase): def test_parentheses_and_spaces_preserved(self): """819: Test parentheses and spacing is preserved on add - Bug 819: When I run "task add foo\'s bar." the description of the new task is "foo 's bar .". + Bug 819: When I run "task add foo\'s bar." the description of the new task is "foo 's bar .". """ - self.t("add foo\\\'s bar") + self.t("add foo\\'s bar") self.t("add foo (bar)") self.t("add 'baz (qux)'") @@ -114,11 +115,11 @@ class TestAdd(TestCase): def test_single_quote_preserved(self): """1642: Test single quote in a terminated multi-word string is preserved - TW-1642: After "--", an apostrophe unexpectedly ends the task description + TW-1642: After "--", an apostrophe unexpectedly ends the task description """ - self.t("add -- \"Return Randy's stuff\"") + self.t('add -- "Return Randy\'s stuff"') - code, out, err = self.t ("_get 1.description") + code, out, err = self.t("_get 1.description") self.assertIn("Return Randy's stuff\n", out) @@ -190,7 +191,7 @@ class Test1549(TestCase): """ # This command will hang and therefore timeout in 2.4.1. - code, out, err = self.t('rc.verbose:new-id add 1e x') + code, out, err = self.t("rc.verbose:new-id add 1e x") self.assertIn("Created task 1.", out) @@ -202,7 +203,7 @@ class TestBug1612(TestCase): def test_spurious_whitespace(self): """1612: ensure that extra whitespace does not get added. - tw-1612: Spurious whitespace added in task descriptions around certain symbols + tw-1612: Spurious whitespace added in task descriptions around certain symbols """ self.t("add 'foo-bar (http://baz.org/)'") self.t("add 'spam (foo bar)'") @@ -232,8 +233,8 @@ class TestBug1719(TestCase): def test_improper_ordinals(self): """1719: Description cannot contain improper ordinals""" - self.t("add one 1th"); - self.t("add two 23rd"); + self.t("add one 1th") + self.t("add two 23rd") code, out, err = self.t("_get 1.description") self.assertEqual("one 1th\n", out) @@ -244,6 +245,7 @@ class TestBug1719(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/alias.test.py b/test/alias.test.py index 816d978a0..f7cb4f918 100755 --- a/test/alias.test.py +++ b/test/alias.test.py @@ -29,6 +29,7 @@ import sys import os import unittest from datetime import datetime + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -55,28 +56,25 @@ class TestAlias(TestCase): # Sanity check that _projects command outputs the "Home" project code, out, err = self.t("_projects") - self.assertIn(expected, out, - msg="task _projects -> Home") + self.assertIn(expected, out, msg="task _projects -> Home") # Check that foo command outputs the "Home" project code, out, err = self.t("foo") - self.assertIn(expected, out, - msg="task foo -> _projects > Home") + self.assertIn(expected, out, msg="task foo -> _projects > Home") # Check that bar command outputs the "Home" project code, out, err = self.t("bar") - self.assertIn(expected, out, - msg="task bar -> foo > _projects > Home") + self.assertIn(expected, out, msg="task bar -> foo > _projects > Home") # Check that baz command outputs the "Home" project code, out, err = self.t("baz") - self.assertIn(expected, out, - msg="task baz -> bar > foo > _projects > Home") + self.assertIn(expected, out, msg="task baz -> bar > foo > _projects > Home") # Check that qux command outputs the "Home" project code, out, err = self.t("qux") - self.assertIn(expected, out, - msg="task qux -> baz > bar > foo > _projects > Home") + self.assertIn( + expected, out, msg="task qux -> baz > bar > foo > _projects > Home" + ) def test_alias_with_implicit_filter(self): """Test alias containing simple filter string""" @@ -91,17 +89,17 @@ class TestAlias(TestCase): # Sanity check that _projects command outputs # both the "Home" and "Work" projects code, out, err = self.t("_projects") - self.assertIn("Home", out, - msg="task _projects -> Home") - self.assertIn("Work", out, - msg="task _projects -> Work") + self.assertIn("Home", out, msg="task _projects -> Home") + self.assertIn("Work", out, msg="task _projects -> Work") # Check that foo command outputs the "Home" project code, out, err = self.t("foofilter") - self.assertIn("Home", out, - msg="task foofilter -> project:Home _projects > Home") - self.assertNotIn("Work", out, - msg="task foofilter -> project:Home _projects > Work") + self.assertIn( + "Home", out, msg="task foofilter -> project:Home _projects > Home" + ) + self.assertNotIn( + "Work", out, msg="task foofilter -> project:Home _projects > Work" + ) def test_alias_with_implicit_complex_filter(self): """Test alias containing filter string with conjuction""" @@ -116,20 +114,28 @@ class TestAlias(TestCase): # Check that hometoday command outputs the "Home urgent task" code, out, err = self.t("hometoday") - self.assertIn("Home urgent task", out, - msg="task hometoday -> project:Home and due:today minimal > " - "Home urgent task") + self.assertIn( + "Home urgent task", + out, + msg="task hometoday -> project:Home and due:today minimal > " + "Home urgent task", + ) # It should not output "Home task", as that one is not due:today - self.assertNotIn("Home task", out, - msg="task hometoday -> project:Home and due:today minimal > " - "Home task") + self.assertNotIn( + "Home task", + out, + msg="task hometoday -> project:Home and due:today minimal > " "Home task", + ) # It should not output "Work task" either, it has entirely wrong # project - self.assertNotIn("Work task", out, - msg="task hometoday -> project:Home and due:today minimal > " - "Work task") + self.assertNotIn( + "Work task", + out, + msg="task hometoday -> project:Home and due:today minimal > " "Work task", + ) + class TestAliasesCommand(TestCase): def setUp(self): @@ -142,6 +148,7 @@ class TestAliasesCommand(TestCase): code, out, err = self.t("_aliases") self.assertIn("foo", out) + class TestBug1652(TestCase): def setUp(self): """Executed before each test in the class""" @@ -156,6 +163,7 @@ class TestBug1652(TestCase): self.assertIn("Deleted 1 task.", out) self.assertNotIn("No matches.", err) + class TestBug1031(TestCase): def setUp(self): """Executed before each test in the class""" @@ -196,23 +204,24 @@ class Test1445(TestCase): def test_alias_single_word(self): """1445: Verify single-word aliases""" - self.t.config('alias.when', 'execute date') - code, out, err = self.t('when') + self.t.config("alias.when", "execute date") + code, out, err = self.t("when") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) self.assertIn(str(datetime.now().year), out) def test_alias_multi_word(self): """1445: Verify multi-word aliases""" - self.t.config('alias.worktasks', 'list +work') - self.t('add one +work') - self.t('add two') - code, out, err = self.t('worktasks') + self.t.config("alias.worktasks", "list +work") + self.t("add one +work") + self.t("add two") + code, out, err = self.t("worktasks") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('one', out) + self.assertIn("one", out) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/annotate.test.py b/test/annotate.test.py index c0c6cf05b..210095826 100755 --- a/test/annotate.test.py +++ b/test/annotate.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -77,27 +78,45 @@ class TestAnnotate(TestCase): # NOTE: Use 'rrr' to guarantee a unique report name. Using 'r' # conflicts with 'recurring'. self.t.config("report.rrr.description", "rrr") - self.t.config("report.rrr.columns", "id,description") - self.t.config("report.rrr.sort", "id+") - self.t.config("dateformat", "m/d/Y") - self.t.config("color", "0") + self.t.config("report.rrr.columns", "id,description") + self.t.config("report.rrr.sort", "id+") + self.t.config("dateformat", "m/d/Y") + self.t.config("color", "0") code, out, err = self.t("rrr") self.assertTasksExist(out) - self.assertRegex(out, "one\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+foo1", - msg='full - first annotation task 1') - self.assertRegex(out, "foo1\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+foo2", - msg='full - first annotation task 1') - self.assertRegex(out, "foo2\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+foo3", - msg='full - first annotation task 1') - self.assertRegex(out, "two\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+bar1", - msg='full - first annotation task 1') - self.assertRegex(out, "bar1\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+bar2", - msg='full - first annotation task 1') - self.assertRegex(out, "three\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+baz1", - msg='full - first annotation task 1') + self.assertRegex( + out, + "one\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+foo1", + msg="full - first annotation task 1", + ) + self.assertRegex( + out, + "foo1\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+foo2", + msg="full - first annotation task 1", + ) + self.assertRegex( + out, + "foo2\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+foo3", + msg="full - first annotation task 1", + ) + self.assertRegex( + out, + "two\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+bar1", + msg="full - first annotation task 1", + ) + self.assertRegex( + out, + "bar1\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+bar2", + msg="full - first annotation task 1", + ) + self.assertRegex( + out, + "three\n.+\\d{1,2}/\\d{1,2}/\\d{4}\\s+baz1", + msg="full - first annotation task 1", + ) def test_annotate_dateformat(self): """Testing annotations in reports using dateformat.annotation""" @@ -105,26 +124,45 @@ class TestAnnotate(TestCase): # NOTE: Use 'rrr' to guarantee a unique report name. Using 'r' # conflicts with 'recurring'. self.t.config("report.rrr.description", "rrr") - self.t.config("report.rrr.columns", "id,description") - self.t.config("report.rrr.sort", "id+") - self.t.config("dateformat.annotation", "yMD HNS") + self.t.config("report.rrr.columns", "id,description") + self.t.config("report.rrr.sort", "id+") + self.t.config("dateformat.annotation", "yMD HNS") code, out, err = self.t("rrr") self.assertTasksExist(out) - self.assertRegex(out, "one\n.+\\d{1,6}\\s+\\d{1,6}\\s+foo1", - msg="dateformat - first annotation task 1") - self.assertRegex(out, "foo1\n.+\\d{1,6}\\s+\\d{1,6}\\s+foo2", - msg="dateformat - second annotation task 1") - self.assertRegex(out, "foo2\n.+\\d{1,6}\\s+\\d{1,6}\\s+foo3", - msg="dateformat - third annotation task 1") - self.assertRegex(out, "two\n.+\\d{1,6}\\s+\\d{1,6}\\s+bar1", - msg="dateformat - first annotation task 2") - self.assertRegex(out, "bar1\n.+\\d{1,6}\\s+\\d{1,6}\\s+bar2", - msg="dateformat - second annotation task 2") - self.assertRegex(out, "three\n.+\\d{1,6}\\s+\\d{1,6}\\s+baz1", - msg="dateformat - first annotation task 3") + self.assertRegex( + out, + "one\n.+\\d{1,6}\\s+\\d{1,6}\\s+foo1", + msg="dateformat - first annotation task 1", + ) + self.assertRegex( + out, + "foo1\n.+\\d{1,6}\\s+\\d{1,6}\\s+foo2", + msg="dateformat - second annotation task 1", + ) + self.assertRegex( + out, + "foo2\n.+\\d{1,6}\\s+\\d{1,6}\\s+foo3", + msg="dateformat - third annotation task 1", + ) + self.assertRegex( + out, + "two\n.+\\d{1,6}\\s+\\d{1,6}\\s+bar1", + msg="dateformat - first annotation task 2", + ) + self.assertRegex( + out, + "bar1\n.+\\d{1,6}\\s+\\d{1,6}\\s+bar2", + msg="dateformat - second annotation task 2", + ) + self.assertRegex( + out, + "three\n.+\\d{1,6}\\s+\\d{1,6}\\s+baz1", + msg="dateformat - first annotation task 3", + ) + class TestAnnotationPropagation(TestCase): def setUp(self): @@ -138,7 +176,7 @@ class TestAnnotationPropagation(TestCase): def test_annotate_recurring(self): """Test propagation of annotation to recurring siblings""" self.t("add foo due:eom recur:weekly") - self.t("list") # GC/handleRecurrence + self.t("list") # GC/handleRecurrence self.t("2 annotate bar", input="y\n") code, out, err = self.t("all rc.verbose:nothing") @@ -194,6 +232,7 @@ class TestBug495(TestCase): code, out, err = self.t("_get 1.annotations.1.description") self.assertEqual("This is -- a -- test\n", out) + class TestBug694(TestCase): def setUp(self): self.t = Task() @@ -211,6 +250,7 @@ class TestBug694(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/append.test.py b/test/append.test.py index 589941f1c..512c00355 100755 --- a/test/append.test.py +++ b/test/append.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -89,6 +90,7 @@ class TestBug440(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/args.test.py b/test/args.test.py index 277704ad4..31281bb08 100755 --- a/test/args.test.py +++ b/test/args.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -44,27 +45,33 @@ class TestArgs(TestCase): self.t("add project:p pri:H +tag foo") code, out, err = self.t("_get 1.description") - self.assertIn("foo\n", out, msg='add project:p pri:H +tag foo') + self.assertIn("foo\n", out, msg="add project:p pri:H +tag foo") self.t("1 modify project:p pri:H +tag -- foo") code, out, err = self.t("_get 1.description") - self.assertIn("foo\n", out, msg='add project:p pri:H +tag -- foo') + self.assertIn("foo\n", out, msg="add project:p pri:H +tag -- foo") self.t("1 modify project:p pri:H -- +tag foo") code, out, err = self.t("_get 1.description") - self.assertIn("+tag foo\n", out, msg='add project:p pri:H -- +tag foo') + self.assertIn("+tag foo\n", out, msg="add project:p pri:H -- +tag foo") self.t("1 modify project:p -- pri:H +tag foo") code, out, err = self.t("_get 1.description") - self.assertIn("pri:H +tag foo\n", out, msg='add project:p -- pri:H +tag foo') + self.assertIn("pri:H +tag foo\n", out, msg="add project:p -- pri:H +tag foo") self.t("1 modify -- project:p pri:H +tag foo") code, out, err = self.t("_get 1.description") - self.assertIn("project:p pri:H +tag foo\n", out, msg='add -- project:p pri:H +tag foo') + self.assertIn( + "project:p pri:H +tag foo\n", out, msg="add -- project:p pri:H +tag foo" + ) self.t("1 modify -- project:p pri:H +tag foo --") code, out, err = self.t("_get 1.description") - self.assertIn("project:p pri:H +tag foo --\n", out, msg='add -- project:p pri:H +tag foo --') + self.assertIn( + "project:p pri:H +tag foo --\n", + out, + msg="add -- project:p pri:H +tag foo --", + ) class TestIDPosition(TestCase): @@ -94,6 +101,7 @@ class TestIDPosition(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/basetest/CMakeLists.txt b/test/basetest/CMakeLists.txt index f78450e77..67daf1190 100644 --- a/test/basetest/CMakeLists.txt +++ b/test/basetest/CMakeLists.txt @@ -5,4 +5,4 @@ configure_file(hooks.py hooks.py COPYONLY) configure_file(meta.py meta.py COPYONLY) configure_file(task.py task.py COPYONLY) configure_file(testing.py testing.py COPYONLY) -configure_file(utils.py utils.py) \ No newline at end of file +configure_file(utils.py utils.py) diff --git a/test/basetest/exceptions.py b/test/basetest/exceptions.py index 89ad8bbe9..ccab35a2d 100644 --- a/test/basetest/exceptions.py +++ b/test/basetest/exceptions.py @@ -1,25 +1,29 @@ import signal -sig_names = dict((k, v) for v, k in reversed(sorted(signal.__dict__.items())) - if v.startswith('SIG') and not v.startswith('SIG_')) +sig_names = dict( + (k, v) + for v, k in reversed(sorted(signal.__dict__.items())) + if v.startswith("SIG") and not v.startswith("SIG_") +) class CommandError(Exception): def __init__(self, cmd, code, out, err=None, msg=None): - DEFAULT = ("Command '{{0}}' was {signal}'ed. " - "SIGABRT usually means task timed out.\n") + DEFAULT = ( + "Command '{{0}}' was {signal}'ed. " + "SIGABRT usually means task timed out.\n" + ) if msg is None: msg_suffix = "\n*** Start STDOUT ***\n{2}\n*** End STDOUT ***\n" if err is not None: - msg_suffix += ( - "\n*** Start STDERR ***\n{3}\n*** End STDERR ***\n" - ) + msg_suffix += "\n*** Start STDERR ***\n{3}\n*** End STDERR ***\n" if code < 0: self.msg = DEFAULT.format(signal=sig_names[abs(code)]) else: - self.msg = ("Command '{0}' finished with unexpected exit " - "code '{1}'.\n") + self.msg = ( + "Command '{0}' finished with unexpected exit " "code '{1}'.\n" + ) self.msg += msg_suffix else: @@ -43,12 +47,12 @@ class TimeoutWaitingFor(object): self.name = name def __repr__(self): - return "*** Timeout reached while waiting for {0} ***".format( - self.name) + return "*** Timeout reached while waiting for {0} ***".format(self.name) class StreamsAreMerged(object): def __repr__(self): return "*** Streams are merged, STDERR is not available ***" + # vim: ai sts=4 et sw=4 diff --git a/test/basetest/hooks.py b/test/basetest/hooks.py index a8ff5184d..9332dc7b0 100644 --- a/test/basetest/hooks.py +++ b/test/basetest/hooks.py @@ -4,6 +4,7 @@ import os from sys import stderr import shutil import stat + try: import simplejson as json except ImportError: @@ -15,8 +16,8 @@ from .exceptions import HookError class InvalidJSON(object): - """Object representing the original unparsed JSON string and the JSON error - """ + """Object representing the original unparsed JSON string and the JSON error""" + def __init__(self, original, error): self.original = original self.error = error @@ -38,6 +39,7 @@ class Hooks(object): """Abstraction to help interact with hooks (add, remove) during tests and keep track of which are active. """ + def __init__(self, datadir): """Initialize hooks container which keeps track of active hooks and @@ -63,8 +65,7 @@ class Hooks(object): enabled = ", ".join(enabled) or None disabled = ", ".join(disabled) or None - return "".format(enabled, - disabled) + return "".format(enabled, disabled) def __getitem__(self, name): return self._hooks[name] @@ -134,8 +135,7 @@ class Hooks(object): hook._delete() def clear(self): - """Remove all existing hooks and empty the hook registry - """ + """Remove all existing hooks and empty the hook registry""" self._hooks = {} # Remove any existing hooks @@ -150,10 +150,11 @@ class Hooks(object): class Hook(object): - """Represents a hook script and provides methods to enable/disable hooks - """ - def __init__(self, hookname, hookdir, content=None, default=False, - default_hookpath=None): + """Represents a hook script and provides methods to enable/disable hooks""" + + def __init__( + self, hookname, hookdir, content=None, default=False, default_hookpath=None + ): """Initialize and create the hook This class supports creating hooks in two ways: @@ -181,24 +182,25 @@ class Hook(object): self._check_hook_not_exists(self.hookfile) if not default and content is None: - raise HookError("Cannot create hookfile {0} without content. " - "If using a builtin hook pass default=True" - .format(self.hookname)) + raise HookError( + "Cannot create hookfile {0} without content. " + "If using a builtin hook pass default=True".format(self.hookname) + ) if os.path.isfile(self.hookfile): - raise HookError("Hook with name {0} already exists. " - "Did you forget to remove() it before recreating?" - .format(self.hookname)) + raise HookError( + "Hook with name {0} already exists. " + "Did you forget to remove() it before recreating?".format(self.hookname) + ) if default: - self.default_hookfile = os.path.join(self.default_hookpath, - self.hookname) + self.default_hookfile = os.path.join(self.default_hookpath, self.hookname) self._check_hook_exists(self.default_hookfile) # Symlinks change permission of source file, cannot use one here shutil.copy(self.default_hookfile, self.hookfile) else: self.default_hookfile = None - with open(self.hookfile, 'w') as fh: + with open(self.hookfile, "w") as fh: fh.write(content) def __eq__(self, other): @@ -250,16 +252,19 @@ class Hook(object): if self.hookname.startswith(hooktype): break else: - stderr.write("WARNING: {0} is not a valid hook type. " - "It will not be triggered\n".format(self.hookname)) + stderr.write( + "WARNING: {0} is not a valid hook type. " + "It will not be triggered\n".format(self.hookname) + ) def _remove_file(self, file): try: os.remove(file) except OSError as e: if e.errno == errno.ENOENT: - raise HookError("Hook with name {0} was not found on " - "hooks/ folder".format(file)) + raise HookError( + "Hook with name {0} was not found on " "hooks/ folder".format(file) + ) else: raise @@ -271,18 +276,15 @@ class Hook(object): self._remove_hookfile(self.hookfile) def enable(self): - """Make hookfile executable to allow triggering - """ + """Make hookfile executable to allow triggering""" os.chmod(self.hookfile, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) def disable(self): - """Remove hookfile executable bit to deny triggering - """ + """Remove hookfile executable bit to deny triggering""" os.chmod(self.hookfile, stat.S_IREAD | stat.S_IWRITE) def is_active(self): - """Check if hook is active by verifying the execute bit - """ + """Check if hook is active by verifying the execute bit""" return os.access(self.hookfile, os.X_OK) @@ -290,6 +292,7 @@ class LoggedHook(Hook): """A variant of a Hook that allows checking that the hook was called, what was received via STDIN and what was answered to STDOUT """ + def __init__(self, *args, **kwargs): super(LoggedHook, self).__init__(*args, **kwargs) @@ -300,8 +303,7 @@ class LoggedHook(Hook): self.wrappedname = "original_" + self.hookname self.wrappedfile = os.path.join(self.hookdir, self.wrappedname) - self.original_wrapper = os.path.join(self.default_hookpath, - "wrapper.sh") + self.original_wrapper = os.path.join(self.default_hookpath, "wrapper.sh") self.hooklog_in = self.wrappedfile + ".log.in" self.hooklog_out = self.wrappedfile + ".log.out" @@ -326,11 +328,10 @@ class LoggedHook(Hook): self._remove_file(self.hooklog_out) def _setup_wrapper(self): - """Setup wrapper shell script to allow capturing input/output of hook - """ + """Setup wrapper shell script to allow capturing input/output of hook""" # Create empty hooklog to allow checking that hook executed - open(self.hooklog_in, 'w').close() - open(self.hooklog_out, 'w').close() + open(self.hooklog_in, "w").close() + open(self.hooklog_out, "w").close() # Rename the original hook to the name that will be used by wrapper self._check_hook_not_exists(self.wrappedfile) @@ -340,8 +341,7 @@ class LoggedHook(Hook): shutil.copy(self.original_wrapper, self.hookfile) def _get_log_stat(self): - """Return the most recent change timestamp and size of both logfiles - """ + """Return the most recent change timestamp and size of both logfiles""" stdin = os.stat(self.hooklog_in) stdout = os.stat(self.hooklog_out) @@ -349,8 +349,7 @@ class LoggedHook(Hook): return last_change, stdin.st_size, stdout.st_size def _use_cache(self): - """Check if log files were changed since last check - """ + """Check if log files were changed since last check""" try: last_change = self._cache["last_change"] except KeyError: @@ -367,20 +366,17 @@ class LoggedHook(Hook): return True def enable(self): - """Make hookfile executable to allow triggering - """ + """Make hookfile executable to allow triggering""" super(LoggedHook, self).enable() os.chmod(self.wrappedfile, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) def disable(self): - """Remove hookfile executable bit to deny triggering - """ + """Remove hookfile executable bit to deny triggering""" super(LoggedHook, self).disable() os.chmod(self.wrappedfile, stat.S_IREAD | stat.S_IWRITE) def is_active(self): - """Check if hook is active by verifying the execute bit - """ + """Check if hook is active by verifying the execute bit""" parent_is_active = super(LoggedHook, self).disable() return parent_is_active and os.access(self.wrappedfile, os.X_OK) @@ -407,16 +403,17 @@ class LoggedHook(Hook): if self._use_cache(): return self._cache["log"] - log = {"calls": [], - "input": { - "json": [], - }, - "output": { - "json": [], - "msgs": [], - }, - "exitcode": None, - } + log = { + "calls": [], + "input": { + "json": [], + }, + "output": { + "json": [], + "msgs": [], + }, + "exitcode": None, + } with open(self.hooklog_in) as fh: for i, line in enumerate(fh): @@ -426,16 +423,19 @@ class LoggedHook(Hook): # Timestamp includes nanosecond resolution timestamp = tstamp.split(" ")[-1] # convert timestamp to python datetime object - log["calls"].append({ - "timestamp": datetime.fromtimestamp(float(timestamp)), - "args": args, - }) + log["calls"].append( + { + "timestamp": datetime.fromtimestamp(float(timestamp)), + "args": args, + } + ) elif line.startswith("{"): # Decode json input (to hook) log["input"]["json"].append(json_decoder(line)) else: - raise IOError("Unexpected content on STDIN line {0}: {1}" - .format(i, line)) + raise IOError( + "Unexpected content on STDIN line {0}: {1}".format(i, line) + ) with open(self.hooklog_out) as fh: for line in fh: @@ -464,49 +464,43 @@ class LoggedHook(Hook): """ log = self.get_logs() - assert len(log["calls"]) == count, ("{0} calls expected for {1} but " - "found {2}".format( - count, - self.hookname, - log["calls"] - )) + assert ( + len(log["calls"]) == count + ), "{0} calls expected for {1} but " "found {2}".format( + count, self.hookname, log["calls"] + ) def assertExitcode(self, exitcode): - """Check if current hook finished with the expected exit code - """ + """Check if current hook finished with the expected exit code""" log = self.get_logs() - assert log["exitcode"] == exitcode, ("Expected exit code {0} for {1} " - "but found {2}".format( - exitcode, - self.hookname, - log["exitcode"] - )) + assert ( + log["exitcode"] == exitcode + ), "Expected exit code {0} for {1} " "but found {2}".format( + exitcode, self.hookname, log["exitcode"] + ) def assertValidJSONOutput(self): - """Check if current hook output is valid JSON in all expected replies - """ + """Check if current hook output is valid JSON in all expected replies""" log = self.get_logs() for i, out in enumerate(log["output"]["json"]): - assert not isinstance(out, InvalidJSON), ("Invalid JSON found at " - "reply number {0} with " - "content {1}".format( - i + 1, - out.original - )) + assert not isinstance(out, InvalidJSON), ( + "Invalid JSON found at " + "reply number {0} with " + "content {1}".format(i + 1, out.original) + ) def assertInvalidJSONOutput(self): - """Check if current hook output is invalid JSON in any expected reply - """ + """Check if current hook output is invalid JSON in any expected reply""" log = self.get_logs() for i, out in enumerate(log["output"]["json"]): - assert isinstance(out, InvalidJSON), ("Valid JSON found at reply " - "number {0} with content " - "{1}".format( - i + 1, - out.original - )) + assert isinstance(out, InvalidJSON), ( + "Valid JSON found at reply " + "number {0} with content " + "{1}".format(i + 1, out.original) + ) + # vim: ai sts=4 et sw=4 diff --git a/test/basetest/meta.py b/test/basetest/meta.py index 055f5ca95..f5b30a5ad 100644 --- a/test/basetest/meta.py +++ b/test/basetest/meta.py @@ -7,6 +7,7 @@ class MetaTest(type): Creates test_methods in the TestCase class dynamically named after the arguments used. """ + @staticmethod def make_function(classname, *args, **kwargs): def test(self): @@ -35,4 +36,5 @@ class MetaTest(type): return super(MetaTest, meta).__new__(meta, classname, bases, dct) + # vim: ai sts=4 et sw=4 diff --git a/test/basetest/task.py b/test/basetest/task.py index d426ff9db..be30441b7 100644 --- a/test/basetest/task.py +++ b/test/basetest/task.py @@ -21,6 +21,7 @@ class Task(object): A taskw client should not be used after being destroyed. """ + DEFAULT_TASK = task_binary_location() def __init__(self, taskw=DEFAULT_TASK): @@ -43,11 +44,13 @@ class Task(object): self.reset_env() - with open(self.taskrc, 'w') as rc: - rc.write("data.location={0}\n" - "hooks=off\n" - "news.version=2.6.0\n" - "".format(self.datadir)) + with open(self.taskrc, "w") as rc: + rc.write( + "data.location={0}\n" + "hooks=off\n" + "news.version=2.6.0\n" + "".format(self.datadir) + ) # Hooks disabled until requested self.hooks = None @@ -61,14 +64,12 @@ class Task(object): return self.runSuccess(*args, **kwargs) def activate_hooks(self): - """Enable self.hooks functionality and activate hooks on config - """ + """Enable self.hooks functionality and activate hooks on config""" self.config("hooks", "1") self.hooks = Hooks(self.datadir) def reset_env(self): - """Set a new environment derived from the one used to launch the test - """ + """Set a new environment derived from the one used to launch the test""" # Copy all env variables to avoid clashing subprocess environments self.env = os.environ.copy() @@ -78,15 +79,13 @@ class Task(object): self.env["TASKRC"] = self.taskrc def config(self, var, value): - """Run setup `var` as `value` in taskd config - """ + """Run setup `var` as `value` in taskd config""" # Add -- to avoid misinterpretation of - in things like UUIDs cmd = (self.taskw, "config", "--", var, value) return run_cmd_wait(cmd, env=self.env, input="y\n") def del_config(self, var): - """Remove `var` from taskd config - """ + """Remove `var` from taskd config""" cmd = (self.taskw, "config", var) return run_cmd_wait(cmd, env=self.env, input="y\n") @@ -104,8 +103,9 @@ class Task(object): if export_filter is None: export_filter = "" - code, out, err = self.runSuccess("rc.json.array=1 {0} export" - "".format(export_filter)) + code, out, err = self.runSuccess( + "rc.json.array=1 {0} export" "".format(export_filter) + ) return json.loads(out) @@ -118,16 +118,16 @@ class Task(object): result = self.export(export_filter=export_filter) if len(result) != 1: - descriptions = [task.get('description') or '[description-missing]' - for task in result] + descriptions = [ + task.get("description") or "[description-missing]" for task in result + ] raise ValueError( "One task should match the '{0}' filter, '{1}' " "matches:\n {2}".format( - export_filter or '', - len(result), - '\n '.join(descriptions) - )) + export_filter or "", len(result), "\n ".join(descriptions) + ) + ) return result[0] @@ -146,8 +146,7 @@ class Task(object): return args - def runSuccess(self, args="", input=None, merge_streams=False, - timeout=5): + def runSuccess(self, args="", input=None, merge_streams=False, timeout=5): """Invoke task with given arguments and fail if exit code != 0 Use runError if you want exit_code to be tested automatically and @@ -171,10 +170,9 @@ class Task(object): args = self._split_string_args_if_string(args) command.extend(args) - output = run_cmd_wait_nofail(command, input, - merge_streams=merge_streams, - env=self.env, - timeout=timeout) + output = run_cmd_wait_nofail( + command, input, merge_streams=merge_streams, env=self.env, timeout=timeout + ) if output[0] != 0: raise CommandError(command, *output) @@ -205,10 +203,9 @@ class Task(object): args = self._split_string_args_if_string(args) command.extend(args) - output = run_cmd_wait_nofail(command, input, - merge_streams=merge_streams, - env=self.env, - timeout=timeout) + output = run_cmd_wait_nofail( + command, input, merge_streams=merge_streams, env=self.env, timeout=timeout + ) # output[0] is the exit code if output[0] == 0 or output[0] is None: @@ -217,8 +214,7 @@ class Task(object): return output def destroy(self): - """Cleanup the data folder and release server port for other instances - """ + """Cleanup the data folder and release server port for other instances""" try: shutil.rmtree(self.datadir) except OSError as e: @@ -237,8 +233,10 @@ class Task(object): self.destroy = lambda: None def __destroyed(self, *args, **kwargs): - raise AttributeError("Task instance has been destroyed. " - "Create a new instance if you need a new client.") + raise AttributeError( + "Task instance has been destroyed. " + "Create a new instance if you need a new client." + ) def diag(self, merge_streams_with=None): """Run task diagnostics. @@ -302,4 +300,5 @@ class Task(object): # Use advanced time format self._command = [cmd, "-f", faketime] + self._command + # vim: ai sts=4 et sw=4 diff --git a/test/basetest/testing.py b/test/basetest/testing.py index ab6a026cc..56154f925 100644 --- a/test/basetest/testing.py +++ b/test/basetest/testing.py @@ -7,14 +7,14 @@ class BaseTestCase(unittest.TestCase): def tap(self, out): sys.stderr.write("--- tap output start ---\n") for line in out.splitlines(): - sys.stderr.write(line + '\n') + sys.stderr.write(line + "\n") sys.stderr.write("--- tap output end ---\n") @unittest.skipIf(TASKW_SKIP, "TASKW_SKIP set, skipping task tests.") class TestCase(BaseTestCase): - """Automatically skips tests if TASKW_SKIP is present in the environment - """ + """Automatically skips tests if TASKW_SKIP is present in the environment""" + pass diff --git a/test/basetest/utils.py b/test/basetest/utils.py index 9bcb6bc06..2d665d8c3 100644 --- a/test/basetest/utils.py +++ b/test/basetest/utils.py @@ -9,11 +9,13 @@ import atexit import tempfile from subprocess import Popen, PIPE, STDOUT from threading import Thread + try: from Queue import Queue, Empty except ImportError: from queue import Queue, Empty from time import sleep + try: import simplejson as json except ImportError: @@ -21,15 +23,13 @@ except ImportError: from .exceptions import CommandError, TimeoutWaitingFor USED_PORTS = set() -ON_POSIX = 'posix' in sys.builtin_module_names +ON_POSIX = "posix" in sys.builtin_module_names # Directory relative to basetest module location CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) # Location of binary files (usually the src/ folder) -BIN_PREFIX = os.path.abspath( - os.path.join("${CMAKE_BINARY_DIR}","src") -) +BIN_PREFIX = os.path.abspath(os.path.join("${CMAKE_BINARY_DIR}", "src")) # Default location of test hooks DEFAULT_HOOK_PATH = os.path.abspath( @@ -45,7 +45,7 @@ TASKW_SKIP = os.environ.get("TASKW_SKIP", False) # Environment flags to control use of PATH or in-tree binaries TASK_USE_PATH = os.environ.get("TASK_USE_PATH", False) -UUID_REGEXP = ("[0-9A-Fa-f]{8}-" + ("[0-9A-Fa-f]{4}-" * 3) + "[0-9A-Fa-f]{12}") +UUID_REGEXP = "[0-9A-Fa-f]{8}-" + ("[0-9A-Fa-f]{4}-" * 3) + "[0-9A-Fa-f]{12}" def task_binary_location(cmd="task"): @@ -65,9 +65,8 @@ def binary_location(cmd, USE_PATH=False): return os.path.join(BIN_PREFIX, cmd) -def wait_condition(cond, timeout=10, sleeptime=.01): - """Wait for condition to return anything other than None - """ +def wait_condition(cond, timeout=10, sleeptime=0.01): + """Wait for condition to return anything other than None""" # NOTE Increasing sleeptime can dramatically increase testsuite runtime # It also reduces CPU load significantly if timeout is None: @@ -92,8 +91,8 @@ def wait_condition(cond, timeout=10, sleeptime=.01): def wait_process(pid, timeout=None): - """Wait for process to finish - """ + """Wait for process to finish""" + def process(): try: os.kill(pid, 0) @@ -120,13 +119,18 @@ def _queue_output(arguments, pidq, outputq): # pid None is read by the main thread as a crash of the process pidq.put(None) - outputq.put(( - "", - ("Unexpected exception caught during execution of taskw: '{0}' . " - "If you are running out-of-tree tests set TASK_USE_PATH=1 " - "in shell env before execution and add the " - "location of the task(d) binary to the PATH".format(e)), - 255)) # false exitcode + outputq.put( + ( + "", + ( + "Unexpected exception caught during execution of taskw: '{0}' . " + "If you are running out-of-tree tests set TASK_USE_PATH=1 " + "in shell env before execution and add the " + "location of the task(d) binary to the PATH".format(e) + ), + 255, + ) + ) # false exitcode return @@ -137,15 +141,14 @@ def _queue_output(arguments, pidq, outputq): out, err = proc.communicate(input_data) if sys.version_info > (3,): - out, err = out.decode('utf-8'), err.decode('utf-8') + out, err = out.decode("utf-8"), err.decode("utf-8") # Give the output back to the caller outputq.put((out, err, proc.returncode)) def _retrieve_output(thread, timeout, queue, thread_error): - """Fetch output from taskw subprocess queues - """ + """Fetch output from taskw subprocess queues""" # Try to join the thread on failure abort thread.join(timeout) if thread.is_alive(): @@ -184,16 +187,16 @@ def _get_output(arguments, timeout=None): # Process crashed or timed out for some reason if pid is None: - return _retrieve_output(t, output_timeout, outputq, - "TaskWarrior to start") + return _retrieve_output(t, output_timeout, outputq, "TaskWarrior to start") # Wait for process to finish (normal execution) state = wait_process(pid, timeout) if state: # Process finished - return _retrieve_output(t, output_timeout, outputq, - "TaskWarrior thread to join") + return _retrieve_output( + t, output_timeout, outputq, "TaskWarrior thread to join" + ) # If we reach this point we assume the process got stuck or timed out for sig in (signal.SIGABRT, signal.SIGTERM, signal.SIGKILL): @@ -210,15 +213,21 @@ def _get_output(arguments, timeout=None): if state: # Process finished - return _retrieve_output(t, output_timeout, outputq, - "TaskWarrior to die") + return _retrieve_output(t, output_timeout, outputq, "TaskWarrior to die") # This should never happen but in case something goes really bad raise OSError("TaskWarrior stopped responding and couldn't be killed") -def run_cmd_wait(cmd, input=None, stdout=PIPE, stderr=PIPE, - merge_streams=False, env=os.environ, timeout=None): +def run_cmd_wait( + cmd, + input=None, + stdout=PIPE, + stderr=PIPE, + merge_streams=False, + env=os.environ, + timeout=None, +): "Run a subprocess and wait for it to finish" if input is None: @@ -265,8 +274,7 @@ def run_cmd_wait_nofail(*args, **kwargs): def memoize(obj): - """Keep an in-memory cache of function results given its inputs - """ + """Keep an in-memory cache of function results given its inputs""" cache = obj.cache = {} @functools.wraps(obj) @@ -275,11 +283,13 @@ def memoize(obj): if key not in cache: cache[key] = obj(*args, **kwargs) return cache[key] + return memoizer try: from shutil import which + which = memoize(which) except ImportError: # NOTE: This is shutil.which backported from python-3.3.3 @@ -294,12 +304,12 @@ except ImportError: path. """ + # Check that a given file can be accessed with the correct mode. # Additionally check that `file` is not a directory, as on Windows # directories pass the os.access check. def _access_check(fn, mode): - return (os.path.exists(fn) and os.access(fn, mode) and - not os.path.isdir(fn)) + return os.path.exists(fn) and os.access(fn, mode) and not os.path.isdir(fn) # If we're given a path with a directory part, look it up directly # rather than referring to PATH directories. This includes checking @@ -348,16 +358,15 @@ except ImportError: def parse_datafile(file): - """Parse .data files on the client and server treating files as JSON - """ + """Parse .data files on the client and server treating files as JSON""" data = [] with open(file) as fh: for line in fh: line = line.rstrip("\n") # Turn [] strings into {} to be treated properly as JSON hashes - if line.startswith('[') and line.endswith(']'): - line = '{' + line[1:-1] + '}' + if line.startswith("[") and line.endswith("]"): + line = "{" + line[1:-1] + "}" if line.startswith("{"): data.append(json.loads(line)) @@ -370,6 +379,7 @@ def mkstemp(data): """ Create a temporary file that is removed at process exit """ + def rmtemp(name): try: os.remove(name) @@ -377,7 +387,7 @@ def mkstemp(data): pass f = tempfile.NamedTemporaryFile(delete=False) - f.write(data.encode('utf-8') if not isinstance(data, bytes) else data) + f.write(data.encode("utf-8") if not isinstance(data, bytes) else data) f.close() # Ensure removal at end of python session @@ -387,11 +397,11 @@ def mkstemp(data): def mkstemp_exec(data): - """Create a temporary executable file that is removed at process exit - """ + """Create a temporary executable file that is removed at process exit""" name = mkstemp(data) os.chmod(name, 0o755) return name + # vim: ai sts=4 et sw=4 diff --git a/test/bash_completion.test.py b/test/bash_completion.test.py index 6730236b7..360fe7d8d 100755 --- a/test/bash_completion.test.py +++ b/test/bash_completion.test.py @@ -29,6 +29,7 @@ import sys import os import unittest from contextlib import contextmanager + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -66,16 +67,18 @@ def prepare_tasksh(t): tasksh.append(line) - tasksh.extend([ - 'COMP_WORDS=("$@")', - 'COMP_CWORD=$(($#-1))', - '_task', - 'for reply_iter in "${COMPREPLY[@]}"; do', - ' echo $reply_iter', - 'done', - ]) + tasksh.extend( + [ + 'COMP_WORDS=("$@")', + "COMP_CWORD=$(($#-1))", + "_task", + 'for reply_iter in "${COMPREPLY[@]}"; do', + " echo $reply_iter", + "done", + ] + ) - return '\n'.join(tasksh) + return "\n".join(tasksh) class TestBashCompletionBase(TestCase): @@ -88,7 +91,7 @@ class TestBashCompletionBase(TestCase): self.t.tasksh_script = os.path.join(self.t.datadir, "task.sh") - with open(self.t.tasksh_script, 'w') as tasksh: + with open(self.t.tasksh_script, "w") as tasksh: tasksh.write(prepare_tasksh(self.t)) @@ -170,6 +173,7 @@ class TestProject(TestBashCompletionBase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/blocked.test.py b/test/blocked.test.py index b673f76f0..0eae3d2c4 100755 --- a/test/blocked.test.py +++ b/test/blocked.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase @@ -49,6 +50,7 @@ class TestBug1381(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/bulk.test.py b/test/bulk.test.py index 75f7adc79..e66c13aad 100755 --- a/test/bulk.test.py +++ b/test/bulk.test.py @@ -29,6 +29,7 @@ import sys import os import signal import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -138,7 +139,9 @@ class TestBulk(TestCase): self.assertNotIn("Deleting task", out) # Test with 3 tasks, denying delete. - code, out, err = self.t.runError("1-3 delete rc.confirmation:1", input="n\nn\nn\n") + code, out, err = self.t.runError( + "1-3 delete rc.confirmation:1", input="n\nn\nn\n" + ) self.assertNotIn("(yes/no)", out) self.assertIn("(yes/no/all/quit)", out) self.assertNotIn("Deleted task 1", out) @@ -208,6 +211,7 @@ class TestBugBulk(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/burndown.test.py b/test/burndown.test.py index ce5d18ae7..9c31cd7a3 100755 --- a/test/burndown.test.py +++ b/test/burndown.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -97,6 +98,7 @@ class TestBurndownCommand(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/calc.test.py b/test/calc.test.py index 7072aca70..1eb10c72a 100755 --- a/test/calc.test.py +++ b/test/calc.test.py @@ -30,6 +30,7 @@ import os import re import signal import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -39,8 +40,9 @@ from basetest.utils import BIN_PREFIX, run_cmd_wait, run_cmd_wait_nofail CALC = os.path.join(BIN_PREFIX, "calc") -@unittest.skipIf(not os.path.isfile(CALC), - "calc binary not available in {0}".format(CALC)) +@unittest.skipIf( + not os.path.isfile(CALC), "calc binary not available in {0}".format(CALC) +) class TestCalc(TestCase): def test_regular_math(self): """regular math""" @@ -56,7 +58,9 @@ class TestCalc(TestCase): def test_postfix_math(self): """postfix math""" - code, out, err = run_cmd_wait((CALC, "--debug", "--postfix", "12 3600 * 34 60 * 56 + +")) + code, out, err = run_cmd_wait( + (CALC, "--debug", "--postfix", "12 3600 * 34 60 * 56 + +") + ) self.assertIn("Eval literal number ↑'12'", out) self.assertIn("Eval literal number ↑'3600'", out) @@ -122,18 +126,17 @@ class TestBug1254(TestCase): self.assertEqual(expected, code, "Exit code was non-zero ({0})".format(code)) def test_no_segmentation_fault_calc_negative_multiplication(self): - """1254: calc can multiply zero and negative numbers - """ + """1254: calc can multiply zero and negative numbers""" self.run_command("calc 0*-1") def test_calc_positive_multiplication(self): - """1254: calc can multiply negative zero and positive - """ + """1254: calc can multiply negative zero and positive""" self.run_command("calc 0*1") if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/calendar.test.py b/test/calendar.test.py index 52a13b8dd..40089df81 100755 --- a/test/calendar.test.py +++ b/test/calendar.test.py @@ -29,6 +29,7 @@ import sys import os import unittest from datetime import datetime, timedelta + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -38,6 +39,7 @@ from basetest import Task, TestCase def timestamp_in_holiday_format(time): return time.strftime("%Y%m%d") + class TestCalendarCommandLine(TestCase): def setUp(self): """Executed before each test in the class""" @@ -56,7 +58,9 @@ class TestCalendarCommandLine(TestCase): def test_basic_command_offset(self): """Verify 'calendar rc.calendar.offset:on rc.calendar.offset.value:1' does not fail""" - code, out, err = self.t("calendar rc.calendar.offset:on rc.calendar.offset.value:1") + code, out, err = self.t( + "calendar rc.calendar.offset:on rc.calendar.offset.value:1" + ) self.assertIn("Su Mo Tu We Th Fr Sa", out) def test_basic_command(self): @@ -72,13 +76,17 @@ class TestCalendarCommandLine(TestCase): def test_basic_command_details(self): """Verify 'calendar rc.calendar.details:full rc.calendar.details.report:list' does not fail""" self.t("add task_with_due_date due:tomorrow") - code, out, err = self.t("calendar rc.calendar.details:full rc.calendar.details.report:list") + code, out, err = self.t( + "calendar rc.calendar.details:full rc.calendar.details.report:list" + ) self.assertIn("task_with_due_date", out) def test_basic_command_details_color(self): """Verify 'calendar rc.calendar.details:full rc.calendar.details.report:list rc._forcecolor:on' does not fail""" self.t("add task_with_due_date due:tomorrow") - code, out, err = self.t("calendar rc.calendar.details:full rc.calendar.details.report:list rc._forcecolor:on") + code, out, err = self.t( + "calendar rc.calendar.details:full rc.calendar.details.report:list rc._forcecolor:on" + ) self.assertIn("task_with_due_date", out) def test_basic_command_holidays(self): @@ -88,29 +96,37 @@ class TestCalendarCommandLine(TestCase): def test_basic_command_single_holiday(self): """Verify 'calendar rc.holiday.test.name:donkeyday rc.holiday.test.date:[tomorrws date] rc.calendar.holidays:full' does not fail""" - code, out, err = self.t("calendar rc.holiday.test.name:donkeyday rc.holliday.test.date:{0} rc.calendar.holidays:full".format(self.tomorrow)) + code, out, err = self.t( + "calendar rc.holiday.test.name:donkeyday rc.holliday.test.date:{0} rc.calendar.holidays:full".format( + self.tomorrow + ) + ) self.assertRegex(out, "Date +Holiday") def test_basic_command_multiday_holiday(self): """Verify 'calendar rc.holiday.test.name:donkeyday rc.holiday.test.start:[tomorrws date] rc.holiday.test.end:[date a month later] rc.calendar.holidays:full' does not fail""" - code, out, err = self.t("calendar rc.holiday.test.name:donkeyday rc.holiday.test.start:{0} rc.holiday.test.end:{1} rc.calendar.holidays:full".format(self.tomorrow, self.next_month)) + code, out, err = self.t( + "calendar rc.holiday.test.name:donkeyday rc.holiday.test.start:{0} rc.holiday.test.end:{1} rc.calendar.holidays:full".format( + self.tomorrow, self.next_month + ) + ) self.assertRegex(out, "Date +Holiday") def test_y_argument(self): """Verify 'calendar y' does not fail""" code, out, err = self.t("calendar y") - self.assertIn("January", out) - self.assertIn("February", out) - self.assertIn("March", out) - self.assertIn("April", out) - self.assertIn("May", out) - self.assertIn("June", out) - self.assertIn("July", out) - self.assertIn("August", out) + self.assertIn("January", out) + self.assertIn("February", out) + self.assertIn("March", out) + self.assertIn("April", out) + self.assertIn("May", out) + self.assertIn("June", out) + self.assertIn("July", out) + self.assertIn("August", out) self.assertIn("September", out) - self.assertIn("October", out) - self.assertIn("November", out) - self.assertIn("December", out) + self.assertIn("October", out) + self.assertIn("November", out) + self.assertIn("December", out) self.assertNotIn("Could not recognize argument", err) def test_due_argument(self): @@ -249,6 +265,7 @@ class TestCalendarCommandLine(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/caseless.test.py b/test/caseless.test.py index 5e6e46cdc..9e3ade09a 100755 --- a/test/caseless.test.py +++ b/test/caseless.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -38,11 +39,11 @@ class TestCaseless(TestCase): @classmethod def setUpClass(cls): """Executed once before any test in the class""" - cls.t = Task () + cls.t = Task() cls.t.config("report.ls.columns", "id,project,priority,description") - cls.t.config("report.ls.labels", "ID,Proj,Pri,Description") - cls.t.config("report.ls.sort", "priority-,project+") - cls.t.config("report.ls.filter", "status:pending") + cls.t.config("report.ls.labels", "ID,Proj,Pri,Description") + cls.t.config("report.ls.sort", "priority-,project+") + cls.t.config("report.ls.filter", "status:pending") def setUp(self): """Executed before each test in the class""" @@ -102,21 +103,30 @@ class TestCaseless(TestCase): def test_annotation_filter(self): """Verify annotation filter with and without case sensitivity""" - code, out, err = self.t.runError("rc.search.case.sensitive:yes ls description.contains:Three") + code, out, err = self.t.runError( + "rc.search.case.sensitive:yes ls description.contains:Three" + ) self.assertNotIn("one two three", out) - code, out, err = self.t("rc.search.case.sensitive:no ls description.contains:Three") + code, out, err = self.t( + "rc.search.case.sensitive:no ls description.contains:Three" + ) self.assertIn("one two three", out) - code, out, err = self.t.runError("rc.search.case.sensitive:yes ls description.contains:Six") + code, out, err = self.t.runError( + "rc.search.case.sensitive:yes ls description.contains:Six" + ) self.assertNotIn("one two three", out) - code, out, err = self.t("rc.search.case.sensitive:no ls description.contains:Six") + code, out, err = self.t( + "rc.search.case.sensitive:no ls description.contains:Six" + ) self.assertIn("one two three", out) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/col.test.cpp b/test/col.test.cpp index c4be0ea4f..0e7d8290b 100644 --- a/test/col.test.cpp +++ b/test/col.test.cpp @@ -27,19 +27,18 @@ #include // cmake.h include header must come first -#include #include #include +#include #include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest test (12); +int main(int, char**) { + UnitTest test(12); // Ensure environment has no influence. - unsetenv ("TASKDATA"); - unsetenv ("TASKRC"); + unsetenv("TASKDATA"); + unsetenv("TASKRC"); ColumnID columnID; unsigned int minimum = 0; @@ -47,37 +46,36 @@ int main (int, char**) Task t1; t1.id = 3; - columnID.measure (t1, minimum, maximum); - test.is ((int)minimum, 1, "id:3 --> ColID::measure minimum 1"); - test.is ((int)maximum, 1, "id:3 --> ColID::measure maximum 1"); + columnID.measure(t1, minimum, maximum); + test.is((int)minimum, 1, "id:3 --> ColID::measure minimum 1"); + test.is((int)maximum, 1, "id:3 --> ColID::measure maximum 1"); t1.id = 33; - columnID.measure (t1, minimum, maximum); - test.is ((int)minimum, 2, "id:33 --> ColID::measure minimum 2"); - test.is ((int)maximum, 2, "id:33 --> ColID::measure maximum 2"); + columnID.measure(t1, minimum, maximum); + test.is((int)minimum, 2, "id:33 --> ColID::measure minimum 2"); + test.is((int)maximum, 2, "id:33 --> ColID::measure maximum 2"); t1.id = 333; - columnID.measure (t1, minimum, maximum); - test.is ((int)minimum, 3, "id:333 --> ColID::measure minimum 3"); - test.is ((int)maximum, 3, "id:333 --> ColID::measure maximum 3"); + columnID.measure(t1, minimum, maximum); + test.is((int)minimum, 3, "id:333 --> ColID::measure minimum 3"); + test.is((int)maximum, 3, "id:333 --> ColID::measure maximum 3"); t1.id = 3333; - columnID.measure (t1, minimum, maximum); - test.is ((int)minimum, 4, "id:3333 --> ColID::measure minimum 4"); - test.is ((int)maximum, 4, "id:3333 --> ColID::measure maximum 4"); + columnID.measure(t1, minimum, maximum); + test.is((int)minimum, 4, "id:3333 --> ColID::measure minimum 4"); + test.is((int)maximum, 4, "id:3333 --> ColID::measure maximum 4"); t1.id = 33333; - columnID.measure (t1, minimum, maximum); - test.is ((int)minimum, 5, "id:33333 --> ColID::measure minimum 5"); - test.is ((int)maximum, 5, "id:33333 --> ColID::measure maximum 5"); + columnID.measure(t1, minimum, maximum); + test.is((int)minimum, 5, "id:33333 --> ColID::measure minimum 5"); + test.is((int)maximum, 5, "id:33333 --> ColID::measure maximum 5"); t1.id = 333333; - columnID.measure (t1, minimum, maximum); - test.is ((int)minimum, 6, "id:333333 --> ColID::measure minimum 6"); - test.is ((int)maximum, 6, "id:333333 --> ColID::measure maximum 6"); + columnID.measure(t1, minimum, maximum); + test.is((int)minimum, 6, "id:333333 --> ColID::measure minimum 6"); + test.is((int)maximum, 6, "id:333333 --> ColID::measure maximum 6"); return 0; } //////////////////////////////////////////////////////////////////////////////// - diff --git a/test/color.cmd.test.py b/test/color.cmd.test.py index 305d1dab3..17e20865e 100755 --- a/test/color.cmd.test.py +++ b/test/color.cmd.test.py @@ -29,6 +29,7 @@ import sys import os import unittest import platform + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -45,12 +46,12 @@ class TestColorCommand(TestCase): self.t = Task() def test_colors_off(self): - """ Verify 'task colors' shows an error with color:off""" + """Verify 'task colors' shows an error with color:off""" code, out, err = self.t.runError("colors") self.assertIn("Color is currently turned off", out) def test_colors_all(self): - """ Verify 'task colors' shows all colors""" + """Verify 'task colors' shows all colors""" code, out, err = self.t("colors rc._forcecolor:on") self.assertIn("Basic colors", out) self.assertIn("Effects", out) @@ -60,12 +61,12 @@ class TestColorCommand(TestCase): self.assertIn("Try running 'task color white on red'.", out) def test_colors_sample(self): - """ Verify 'task colors red' shows a sample""" + """Verify 'task colors red' shows a sample""" code, out, err = self.t("colors rc._forcecolor:on red") self.assertRegex(out, "Your sample:\n\n .\\[31mtask color red.\\[0m") def test_colors_legend(self): - """ Verify 'task colors legend' shows theme colors""" + """Verify 'task colors legend' shows theme colors""" code, out, err = self.t("colors rc._forcecolor:on legend") self.assertRegex(out, r"color.debug\s+.\[0m\s.\[38;5;4mcolor4\s+.\[0m") @@ -74,8 +75,10 @@ class TestColorCommand(TestCase): code, out, err = self.t("colors rc._forcecolor:on rc.color.debug:red legend") self.assertRegex(out, r"color.debug\s+.\[0m\s.\[31mred\s+.\[0m") + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/color.rules.test.py b/test/color.rules.test.py index 52cc4026a..422ae25a1 100755 --- a/test/color.rules.test.py +++ b/test/color.rules.test.py @@ -29,6 +29,7 @@ import sys import os import unittest import platform + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -42,181 +43,182 @@ class TestColorRules(TestCase): cls.t = Task() # Controlling peripheral color. - cls.t.config('_forcecolor', 'on') - cls.t.config('fontunderline', 'off') # label underlining complicates the tests. - cls.t.config('color.alternate', '') # alternating color complicateѕ the tests. - cls.t.config('default.command', 'list') - cls.t.config('uda.xxx.type', 'numeric') - cls.t.config('uda.xxx.label', 'XXX') + cls.t.config("_forcecolor", "on") + cls.t.config("fontunderline", "off") # label underlining complicates the tests. + cls.t.config("color.alternate", "") # alternating color complicateѕ the tests. + cls.t.config("default.command", "list") + cls.t.config("uda.xxx.type", "numeric") + cls.t.config("uda.xxx.label", "XXX") # Color rules. - cls.t.config('color.active', 'red') - cls.t.config('color.blocked', 'red') - cls.t.config('color.blocking', 'blue') - cls.t.config('color.due', 'red') - cls.t.config('color.overdue', 'blue') - cls.t.config('color.error', 'blue') - cls.t.config('color.header', 'blue') - cls.t.config('color.footnote', 'red') - cls.t.config('color.debug', 'green') - cls.t.config('color.project.x', 'red') - cls.t.config('color.project.none', '') - cls.t.config('color.uda.priority.H', 'red') - cls.t.config('color.uda.priority.M', 'blue') - cls.t.config('color.uda.priority.L', 'green') - cls.t.config('color.keyword.keyword', 'red') - cls.t.config('color.tagged', '') - cls.t.config('color.tag.none', '') - cls.t.config('color.tag.x', 'red') - cls.t.config('color.recurring', 'red') - cls.t.config('color.uda.xxx', 'red') - cls.t.config('color.uda.xxx.4', 'blue') + cls.t.config("color.active", "red") + cls.t.config("color.blocked", "red") + cls.t.config("color.blocking", "blue") + cls.t.config("color.due", "red") + cls.t.config("color.overdue", "blue") + cls.t.config("color.error", "blue") + cls.t.config("color.header", "blue") + cls.t.config("color.footnote", "red") + cls.t.config("color.debug", "green") + cls.t.config("color.project.x", "red") + cls.t.config("color.project.none", "") + cls.t.config("color.uda.priority.H", "red") + cls.t.config("color.uda.priority.M", "blue") + cls.t.config("color.uda.priority.L", "green") + cls.t.config("color.keyword.keyword", "red") + cls.t.config("color.tagged", "") + cls.t.config("color.tag.none", "") + cls.t.config("color.tag.x", "red") + cls.t.config("color.recurring", "red") + cls.t.config("color.uda.xxx", "red") + cls.t.config("color.uda.xxx.4", "blue") - cls.t('add control task') # 1 - cls.t('add active task') # 2 - cls.t('2 start') - cls.t('add blocked task') # 3 - cls.t('add blocking task') # 4 - cls.t('3 modify depends:4') - cls.t('add tomorrow due:tomorrow') # 5 - cls.t('add yesterday due:yesterday') # 6 - cls.t('add anhourago due:now-1h') # 7 - cls.t('add someday due:yesterday') # 8 - cls.t('add project_x project:x') # 9 - cls.t('add pri_h priority:H') # 10 - cls.t('add pri_m priority:M') # 11 - cls.t('add pri_l priority:L') # 12 - cls.t('add keyword') # 13 - cls.t('add tag_x +x') # 14 - cls.t('add uda_xxx_1 xxx:1') # 15 - cls.t('add uda_xxx_4 xxx:4') # 16 - cls.t('add recurring due:tomorrow recur:1week') # 17 Keep this last + cls.t("add control task") # 1 + cls.t("add active task") # 2 + cls.t("2 start") + cls.t("add blocked task") # 3 + cls.t("add blocking task") # 4 + cls.t("3 modify depends:4") + cls.t("add tomorrow due:tomorrow") # 5 + cls.t("add yesterday due:yesterday") # 6 + cls.t("add anhourago due:now-1h") # 7 + cls.t("add someday due:yesterday") # 8 + cls.t("add project_x project:x") # 9 + cls.t("add pri_h priority:H") # 10 + cls.t("add pri_m priority:M") # 11 + cls.t("add pri_l priority:L") # 12 + cls.t("add keyword") # 13 + cls.t("add tag_x +x") # 14 + cls.t("add uda_xxx_1 xxx:1") # 15 + cls.t("add uda_xxx_4 xxx:4") # 16 + cls.t("add recurring due:tomorrow recur:1week") # 17 Keep this last def test_control(self): """No color on control task.""" - code, out, err = self.t('1 info') - self.assertNotIn('\x1b[', out) + code, out, err = self.t("1 info") + self.assertNotIn("\x1b[", out) def test_disable_in_pipe(self): """No color in pipe unless forced.""" - code, out, err = self.t('2 info rc._forcecolor:off') - self.assertNotIn('\x1b[', out) + code, out, err = self.t("2 info rc._forcecolor:off") + self.assertNotIn("\x1b[", out) def test_active(self): """Active color rule.""" - code, out, err = self.t('/active/ info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/active/ info") + self.assertIn("\x1b[31m", out) def test_blocked(self): """Blocked color rule.""" - code, out, err = self.t('/blocked/ info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/blocked/ info") + self.assertIn("\x1b[31m", out) def test_blocking(self): """Blocking color rule.""" - code, out, err = self.t('/blocking/ info') - self.assertIn('\x1b[34m', out) + code, out, err = self.t("/blocking/ info") + self.assertIn("\x1b[34m", out) def test_due_yesterday(self): """Overdue color rule.""" - code, out, err = self.t('/yesterday/ info') - self.assertIn('\x1b[34m', out) + code, out, err = self.t("/yesterday/ info") + self.assertIn("\x1b[34m", out) def test_due_anhourago(self): """Overdue color rule from an hour ago.""" - code, out, err = self.t('/anhourago/ info') + code, out, err = self.t("/anhourago/ info") # Match 4-bit or 8-bit blue color code - self.assertRegex(out, '\x1b\\[(38;5;4|34)m') + self.assertRegex(out, "\x1b\\[(38;5;4|34)m") def test_due_tomorrow(self): """Due tomorrow color rule.""" - code, out, err = self.t('/tomorrow/ info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/tomorrow/ info") + self.assertIn("\x1b[31m", out) def test_due_someday(self): """Due someday color rule.""" - code, out, err = self.t('/someday/ info') - self.assertIn('\x1b[', out) + code, out, err = self.t("/someday/ info") + self.assertIn("\x1b[", out) def test_color_error(self): """Error color.""" - code, out, err = self.t.runError('add error priority:X') - self.assertIn('\x1b[34m', err) + code, out, err = self.t.runError("add error priority:X") + self.assertIn("\x1b[34m", err) def test_color_header(self): """Header color.""" - code, out, err = self.t('rc.verbose=header,default /control/') - self.assertIn('\x1b[34m', err) + code, out, err = self.t("rc.verbose=header,default /control/") + self.assertIn("\x1b[34m", err) def test_color_footnote(self): """Footnote color.""" - code, out, err = self.t('rc.verbose=on /control/') - self.assertIn('\x1b[31mConfiguration override', err) + code, out, err = self.t("rc.verbose=on /control/") + self.assertIn("\x1b[31mConfiguration override", err) def test_color_debug(self): """Debug color.""" - code, out, err = self.t('rc.debug=1 /control/') - self.assertIn('\x1b[32mTimer', err) + code, out, err = self.t("rc.debug=1 /control/") + self.assertIn("\x1b[32mTimer", err) def test_project_x(self): """Project x color rule.""" - code, out, err = self.t('/project_x/ info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/project_x/ info") + self.assertIn("\x1b[31m", out) def test_project_none(self): """Project none color rule.""" - code, out, err = self.t('/control/ rc.color.project.none=red info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/control/ rc.color.project.none=red info") + self.assertIn("\x1b[31m", out) def test_priority_h(self): """Priority H color rule.""" - code, out, err = self.t('/pri_h/ info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/pri_h/ info") + self.assertIn("\x1b[31m", out) def test_priority_m(self): """Priority M color rule.""" - code, out, err = self.t('/pri_m/ info') - self.assertIn('\x1b[34m', out) + code, out, err = self.t("/pri_m/ info") + self.assertIn("\x1b[34m", out) def test_priority_l(self): """Priority L color rule.""" - code, out, err = self.t('/pri_l/ info') - self.assertIn('\x1b[32m', out) + code, out, err = self.t("/pri_l/ info") + self.assertIn("\x1b[32m", out) def test_keyword(self): """Keyword color rule.""" - code, out, err = self.t('/keyword/ info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/keyword/ info") + self.assertIn("\x1b[31m", out) def test_tag_x(self): """Tag x color rule.""" - code, out, err = self.t('/tag_x/ info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/tag_x/ info") + self.assertIn("\x1b[31m", out) def test_tag_none(self): """Tag none color rule.""" - code, out, err = self.t('/control/ rc.color.tag.none=red info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/control/ rc.color.tag.none=red info") + self.assertIn("\x1b[31m", out) def test_tagged(self): """Tagged color rule.""" - code, out, err = self.t('/tag_x/ rc.color.tag.x= rc.color.tagged=blue info') - self.assertIn('\x1b[34m', out) + code, out, err = self.t("/tag_x/ rc.color.tag.x= rc.color.tagged=blue info") + self.assertIn("\x1b[34m", out) def test_recurring(self): """Recurring color rule.""" - code, out, err = self.t('/recurring/ info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/recurring/ info") + self.assertIn("\x1b[31m", out) def test_uda(self): """UDA color rule.""" - code, out, err = self.t('/uda_xxx_1/ info') - self.assertIn('\x1b[31m', out) + code, out, err = self.t("/uda_xxx_1/ info") + self.assertIn("\x1b[31m", out) def test_uda_value(self): """UDA Value color rule.""" - code, out, err = self.t('/uda_xxx_4/ rc.color.uda.xxx= info') - self.assertIn('\x1b[34m', out) + code, out, err = self.t("/uda_xxx_4/ rc.color.uda.xxx= info") + self.assertIn("\x1b[34m", out) + class TestColorRulesMerging(TestCase): @@ -225,37 +227,48 @@ class TestColorRulesMerging(TestCase): self.t = Task() # Controlling peripheral color. - self.t.config('_forcecolor', 'on') - self.t.config('fontunderline', 'off') # label underlining complicates the tests. - self.t.config('color.alternate', '') # alternating color complicateѕ the tests. - self.t.config('default.command', 'list') + self.t.config("_forcecolor", "on") + self.t.config( + "fontunderline", "off" + ) # label underlining complicates the tests. + self.t.config("color.alternate", "") # alternating color complicateѕ the tests. + self.t.config("default.command", "list") # Color rules. Only due and tagged affect resulting color. - self.t.config('color.due', 'red') - self.t.config('color.tagged','on white') - self.t.config('rule.color.precedence', 'due,tagged') + self.t.config("color.due", "red") + self.t.config("color.tagged", "on white") + self.t.config("rule.color.precedence", "due,tagged") - self.t('add due:tomorrow +home hometask') # Task that matches both color rules + self.t("add due:tomorrow +home hometask") # Task that matches both color rules - @unittest.skipIf('CYGWIN' in platform.system(), 'Skipping color merge test for Cygwin') - @unittest.skipIf('FreeBSD' in platform.system(), 'Skipping color merge test for FREEBSD') + @unittest.skipIf( + "CYGWIN" in platform.system(), "Skipping color merge test for Cygwin" + ) + @unittest.skipIf( + "FreeBSD" in platform.system(), "Skipping color merge test for FREEBSD" + ) def test_colors_merge(self): """Tests whether colors merge""" - code, out, err = self.t('1 info') - self.assertIn('\x1b[31;47mhometask', out) # Red on white + code, out, err = self.t("1 info") + self.assertIn("\x1b[31;47mhometask", out) # Red on white - @unittest.skipIf('CYGWIN' in platform.system(), 'Skipping color merge test for Cygwin') - @unittest.skipIf('FreeBSD' in platform.system(), 'Skipping color merge test for FREEBSD') + @unittest.skipIf( + "CYGWIN" in platform.system(), "Skipping color merge test for Cygwin" + ) + @unittest.skipIf( + "FreeBSD" in platform.system(), "Skipping color merge test for FREEBSD" + ) def test_colors_merge_off(self): """No color merge behaviour with rule.color.merge=no""" - self.t.config('rule.color.merge', 'no') + self.t.config("rule.color.merge", "no") - code, out, err = self.t('1 info') - self.assertIn('\x1b[31mhometask', out) # Red + code, out, err = self.t("1 info") + self.assertIn("\x1b[31mhometask", out) # Red if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/columns.test.py b/test/columns.test.py index 2a7dd6afb..7e839414e 100755 --- a/test/columns.test.py +++ b/test/columns.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -40,7 +41,7 @@ class TestDescriptionFormats(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.xxx.columns", "id,description") - cls.t.config("verbose", "nothing") + cls.t.config("verbose", "nothing") cls.t("add zero") cls.t("add one long description to exceed a certain string size") @@ -65,13 +66,18 @@ class TestDescriptionFormats(TestCase): def test_description_oneline(self): """Verify formatting of 'description.oneline' column""" code, out, err = self.t("xxx rc.report.xxx.columns:id,description.oneline") - self.assertRegex(out, r"one long description to exceed a certain string size \d{4}-\d{2}-\d{2}") + self.assertRegex( + out, + r"one long description to exceed a certain string size \d{4}-\d{2}-\d{2}", + ) self.assertIn("annotation", out) self.assertNotIn("[1]", out) def test_description_truncated(self): """Verify formatting of 'description.truncated' column""" - code, out, err = self.t("xxx rc.detection:off rc.defaultwidth:40 rc.report.xxx.columns:id,description.truncated") + code, out, err = self.t( + "xxx rc.detection:off rc.defaultwidth:40 rc.report.xxx.columns:id,description.truncated" + ) self.assertIn("exceed a c...", out) self.assertNotIn("annotation", out) self.assertNotIn("[1]", out) @@ -84,13 +90,17 @@ class TestDescriptionFormats(TestCase): def test_description_truncated_count(self): """Verify formatting of 'description.truncated_count' column""" - code, out, err = self.t("xxx rc.detection:off rc.defaultwidth:40 rc.report.xxx.columns:id,description.truncated_count") + code, out, err = self.t( + "xxx rc.detection:off rc.defaultwidth:40 rc.report.xxx.columns:id,description.truncated_count" + ) self.assertIn("exceed... [1]", out) self.assertNotIn("annotation", out) def test_description_format_unrecognized(self): """Verify descriptionuuid.donkey formatting fails""" - code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,description.donkey") + code, out, err = self.t.runError( + "xxx rc.report.xxx.columns:id,description.donkey" + ) self.assertEqual(err, "Unrecognized column format 'description.donkey'\n") @@ -100,7 +110,7 @@ class TestUUIDFormats(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.xxx.columns", "id,uuid") - cls.t.config("verbose", "nothing") + cls.t.config("verbose", "nothing") cls.t("add zero") code, out, err = cls.t("_get 1.uuid") @@ -131,7 +141,7 @@ class TestUrgencyFormats(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.xxx.columns", "id,urgency") - cls.t.config("verbose", "nothing") + cls.t.config("verbose", "nothing") cls.t("add one project:A due:yesterday +tag") @@ -161,7 +171,7 @@ class TestIDFormats(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.xxx.columns", "id") - cls.t.config("verbose", "nothing") + cls.t.config("verbose", "nothing") cls.t("add zero") @@ -186,7 +196,7 @@ class TestStatusFormats(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.xxx.columns", "id,status") - cls.t.config("verbose", "nothing") + cls.t.config("verbose", "nothing") cls.t("add zero") cls.t("add one") @@ -231,7 +241,7 @@ class TestRecurringAttributeFormats(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.xxx.columns", "id") - cls.t.config("verbose", "nothing") + cls.t.config("verbose", "nothing") cls.t("add one due:eoy recur:monthly") cls.t("list") @@ -241,22 +251,34 @@ class TestRecurringAttributeFormats(TestCase): def test_recurrence_formats_short(self): """Verify formatting of assorted short recurrence columns""" - code, out, err = self.t("xxx rc.report.xxx.columns:id,status,due,recur.indicator,mask,imask,parent.short") + code, out, err = self.t( + "xxx rc.report.xxx.columns:id,status,due,recur.indicator,mask,imask,parent.short" + ) self.assertRegex(out, r"1\sRecurring\s+\d{4}-\d{2}-\d{2}\s+R\s+-") - self.assertRegex(out, r"2\sPending\s+\d{4}-\d{2}-\d{2}\s+R\s+0\s+[0-9a-fA-F]{8}") + self.assertRegex( + out, r"2\sPending\s+\d{4}-\d{2}-\d{2}\s+R\s+0\s+[0-9a-fA-F]{8}" + ) def test_recurrence_formats_long(self): """Verify formatting of assorted long recurrence columns""" - code, out, err = self.t("xxx rc.report.xxx.columns:id,status,due,recur.duration,mask,imask,parent.long") + code, out, err = self.t( + "xxx rc.report.xxx.columns:id,status,due,recur.duration,mask,imask,parent.long" + ) self.assertRegex(out, r"1\sRecurring\s+\d{4}-\d{2}-\d{2}\s+P30D\s+-") - self.assertRegex(out, r"2\sPending\s+\d{4}-\d{2}-\d{2}\s+P30D\s+0\s+[0-9a-fA-F-]{36}") + self.assertRegex( + out, r"2\sPending\s+\d{4}-\d{2}-\d{2}\s+P30D\s+0\s+[0-9a-fA-F-]{36}" + ) def test_recurrence_format_unrecognized(self): """Verify *.donkey formatting fails""" - code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,status,due,recur.donkey,mask,imask,parent.long") + code, out, err = self.t.runError( + "xxx rc.report.xxx.columns:id,status,due,recur.donkey,mask,imask,parent.long" + ) self.assertEqual(err, "Unrecognized column format 'recur.donkey'\n") - code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,status,due,recur.duration,mask,imask,parent.donkey") + code, out, err = self.t.runError( + "xxx rc.report.xxx.columns:id,status,due,recur.duration,mask,imask,parent.donkey" + ) self.assertEqual(err, "Unrecognized column format 'parent.donkey'\n") @@ -266,7 +288,7 @@ class TestProjectFormats(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.xxx.columns", "id,project,description") - cls.t.config("verbose", "nothing") + cls.t.config("verbose", "nothing") cls.t("add one project:TOP") cls.t("add two project:TOP.MIDDLE") @@ -278,27 +300,33 @@ class TestProjectFormats(TestCase): def test_project_format_full(self): """Verify project.full formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,project.full,description") - self.assertRegex(out, r'1\s+TOP\s+one') - self.assertRegex(out, r'2\s+TOP.MIDDLE\s+two') - self.assertRegex(out, r'3\s+TOP.MIDDLE.BOTTOM\s+three') + self.assertRegex(out, r"1\s+TOP\s+one") + self.assertRegex(out, r"2\s+TOP.MIDDLE\s+two") + self.assertRegex(out, r"3\s+TOP.MIDDLE.BOTTOM\s+three") def test_project_format_parent(self): """Verify project.parent formatting""" - code, out, err = self.t("xxx rc.report.xxx.columns:id,project.parent,description") - self.assertRegex(out, r'1\s+TOP\s+one') - self.assertRegex(out, r'2\s+TOP\s+two') - self.assertRegex(out, r'3\s+TOP\s+three') + code, out, err = self.t( + "xxx rc.report.xxx.columns:id,project.parent,description" + ) + self.assertRegex(out, r"1\s+TOP\s+one") + self.assertRegex(out, r"2\s+TOP\s+two") + self.assertRegex(out, r"3\s+TOP\s+three") def test_project_format_indented(self): """Verify project.indented formatting""" - code, out, err = self.t("xxx rc.report.xxx.columns:id,project.indented,description") - self.assertRegex(out, r'1\s+TOP\s+one') - self.assertRegex(out, r'2\s+MIDDLE\s+two') - self.assertRegex(out, r'3\s+BOTTOM\s+three') + code, out, err = self.t( + "xxx rc.report.xxx.columns:id,project.indented,description" + ) + self.assertRegex(out, r"1\s+TOP\s+one") + self.assertRegex(out, r"2\s+MIDDLE\s+two") + self.assertRegex(out, r"3\s+BOTTOM\s+three") def test_project_format_unrecognized(self): """Verify project.donkey formatting fails""" - code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,project.donkey,description") + code, out, err = self.t.runError( + "xxx rc.report.xxx.columns:id,project.donkey,description" + ) self.assertEqual(err, "Unrecognized column format 'project.donkey'\n") @@ -308,28 +336,30 @@ class TestTagsFormats(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.xxx.columns", "id,tags,description") - cls.t.config("verbose", "nothing") + cls.t.config("verbose", "nothing") cls.t("add one +tag1 +tag2") def test_tags_format_list(self): """Verify tags.list formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,tags.list") - self.assertRegex(out, r'1\s+tag1\stag2$') + self.assertRegex(out, r"1\s+tag1\stag2$") def test_tags_format_indicator(self): """Verify tags.indicator formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,tags.indicator") - self.assertRegex(out, r'1\s+\+$') + self.assertRegex(out, r"1\s+\+$") def test_tags_format_count(self): """Verify tags.count formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,tags.count") - self.assertRegex(out, r'1\s+\[2\]$') + self.assertRegex(out, r"1\s+\[2\]$") def test_tags_format_unrecognized(self): """Verify tags.donkey formatting fails""" - code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,tags.donkey,description") + code, out, err = self.t.runError( + "xxx rc.report.xxx.columns:id,tags.donkey,description" + ) self.assertEqual(err, "Unrecognized column format 'tags.donkey'\n") @@ -339,7 +369,7 @@ class TestDateFormats(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.xxx.columns", "id,due") - cls.t.config("verbose", "nothing") + cls.t.config("verbose", "nothing") cls.t("add one due:yesterday") cls.t("add two due:tomorrow") @@ -347,54 +377,56 @@ class TestDateFormats(TestCase): def test_date_format_formatted(self): """Verify due.formatted formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,due.formatted") - self.assertRegex(out, r'1\s+\d{4}-\d{2}-\d{2}') - self.assertRegex(out, r'2\s+\d{4}-\d{2}-\d{2}') + self.assertRegex(out, r"1\s+\d{4}-\d{2}-\d{2}") + self.assertRegex(out, r"2\s+\d{4}-\d{2}-\d{2}") def test_date_format_julian(self): """Verify due.julian formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,due.julian") - self.assertRegex(out, r'1\s+\d+\.\d+') - self.assertRegex(out, r'2\s+\d+\.\d+') + self.assertRegex(out, r"1\s+\d+\.\d+") + self.assertRegex(out, r"2\s+\d+\.\d+") def test_date_format_epoch(self): """Verify due.epoch formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,due.epoch") - self.assertRegex(out, r'1\s+\d{10}') - self.assertRegex(out, r'2\s+\d{10}') + self.assertRegex(out, r"1\s+\d{10}") + self.assertRegex(out, r"2\s+\d{10}") def test_date_format_iso(self): """Verify due.iso formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,due.iso") - self.assertRegex(out, r'1\s+\d{8}T\d{6}Z') - self.assertRegex(out, r'2\s+\d{8}T\d{6}Z') + self.assertRegex(out, r"1\s+\d{8}T\d{6}Z") + self.assertRegex(out, r"2\s+\d{8}T\d{6}Z") def test_date_format_age(self): """Verify due.age formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,due.age") - self.assertRegex(out, r'1\s+[0-9.]+d') - self.assertRegex(out, r'2\s+-[0-9.]+[hmin]+') + self.assertRegex(out, r"1\s+[0-9.]+d") + self.assertRegex(out, r"2\s+-[0-9.]+[hmin]+") def test_date_format_remaining(self): """Verify due.remaining formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,due.remaining") - self.assertRegex(out, r'1') - self.assertRegex(out, r'2\s+\d+\S+') + self.assertRegex(out, r"1") + self.assertRegex(out, r"2\s+\d+\S+") def test_date_format_relative(self): """Verify due.relative formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,due.relative") - self.assertRegex(out, r'1\s+-[0-9.]+d') - self.assertRegex(out, r'2\s+[0-9.]+[hmin]+') + self.assertRegex(out, r"1\s+-[0-9.]+d") + self.assertRegex(out, r"2\s+[0-9.]+[hmin]+") def test_date_format_countdown(self): """Verify due.countdown formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,due.countdown") - self.assertRegex(out, r'1\s+') - self.assertRegex(out, r'2\s+\d+\S+') + self.assertRegex(out, r"1\s+") + self.assertRegex(out, r"2\s+\d+\S+") def test_date_format_unrecognized(self): """Verify due.donkey formatting fails""" - code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,due.donkey,description") + code, out, err = self.t.runError( + "xxx rc.report.xxx.columns:id,due.donkey,description" + ) self.assertEqual(err, "Unrecognized column format 'due.donkey'\n") @@ -406,9 +438,9 @@ class TestCustomColumns(TestCase): def test_unrecognized_column(self): """verify that using a bogus colum generates an error""" self.t.config("report.foo.description", "DESC") - self.t.config("report.foo.columns", "id,foo,description") - self.t.config("report.foo.sort", "id+") - self.t.config("report.foo.filter", "project:A") + self.t.config("report.foo.columns", "id,foo,description") + self.t.config("report.foo.sort", "id+") + self.t.config("report.foo.filter", "project:A") # Generate the usage screen, and locate the custom report on it. code, out, err = self.t.runError("foo") @@ -420,8 +452,8 @@ class TestUDAFormats(TestCase): def setUpClass(cls): """Executed once before any test in the class""" cls.t = Task() - cls.t.config("report.xxx.columns", "id,priority") - cls.t.config("verbose", "nothing") + cls.t.config("report.xxx.columns", "id,priority") + cls.t.config("verbose", "nothing") cls.t.config("uda.priority.indicator", "P") cls.t("add one priority:H") @@ -429,19 +461,18 @@ class TestUDAFormats(TestCase): def test_uda_format_formatted(self): """Verify priority.default formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,priority.default") - self.assertRegex(out, r'1\s+H') + self.assertRegex(out, r"1\s+H") def test_uda_format_indicator(self): """Verify priority.indicator formatting""" code, out, err = self.t("xxx rc.report.xxx.columns:id,priority.indicator") - self.assertRegex(out, r'1\s+P') + self.assertRegex(out, r"1\s+P") def test_uda_format_unrecognized(self): """Verify priority.donkey formatting fails""" code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,priority.donkey") self.assertEqual(err, "Unrecognized column format 'priority.donkey'\n") - """ depends list* 1 2 10 count [3] @@ -450,6 +481,7 @@ depends list* 1 2 10 start active* ✓ """ + class TestFeature1061(TestCase): def setUp(self): """Executed before each test in the class""" @@ -479,6 +511,7 @@ class TestFeature1061(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/commands.test.py b/test/commands.test.py index bed870791..ee2951852 100755 --- a/test/commands.test.py +++ b/test/commands.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -42,19 +43,24 @@ class TestCommands(TestCase): """Verify 'add', 'modify', 'list' dna""" code, out, err = self.t("commands") self.assertRegex(out, r"add\s+operation\s+RW\s+Ctxt\s+Mods\s+Adds a new task") - self.assertRegex(out, r"list\s+report\s+RO\s+ID\s+GC\s+Ctxt\s+Filt\s+Most details of") + self.assertRegex( + out, r"list\s+report\s+RO\s+ID\s+GC\s+Ctxt\s+Filt\s+Most details of" + ) self.assertRegex(out, r"modify\s+operation\s+RW\s+Filt\s+Mods\s+Modifies the") def test_command_dna_color(self): """Verify 'add', 'modify', 'list' dna""" code, out, err = self.t("commands rc._forcecolor:on") self.assertRegex(out, r"add\s+operation\s+RW\s+Ctxt\s+Mods\s+Adds a new task") - self.assertRegex(out, r"list\s+report\s+RO\s+ID\s+GC\s+Ctxt\s+Filt\s+Most details of") + self.assertRegex( + out, r"list\s+report\s+RO\s+ID\s+GC\s+Ctxt\s+Filt\s+Most details of" + ) self.assertRegex(out, r"modify\s+operation\s+RW\s+Filt\s+Mods\s+Modifies the") if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/completed.test.py b/test/completed.test.py index 92799fa99..91f612808 100755 --- a/test/completed.test.py +++ b/test/completed.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -49,13 +50,14 @@ class TestCompleted(TestCase): self.t("2 delete", input="y\n") code, out, err = self.t("completed") - self.assertIn('one', out) - self.assertNotIn('two', out) - self.assertNotIn('three', out) + self.assertIn("one", out) + self.assertNotIn("two", out) + self.assertNotIn("three", out) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/configuration.test.py b/test/configuration.test.py index a96bee9aa..e4a89e9ce 100755 --- a/test/configuration.test.py +++ b/test/configuration.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -58,8 +59,8 @@ class TestConfiguration(TestCase): def test_config_completion(self): """verify that the '_config' command generates a full list""" code, out, err = self.t("_config") - self.assertIn("_forcecolor", out) # first - self.assertIn("xterm.title", out) # last + self.assertIn("_forcecolor", out) # first + self.assertIn("xterm.title", out) # last def test_config_nothing(self): """Verify error handling with no args""" @@ -94,6 +95,7 @@ class TestBug1475(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/confirmation.test.py b/test/confirmation.test.py index 1c904def7..bf2d23c60 100755 --- a/test/confirmation.test.py +++ b/test/confirmation.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -44,7 +45,7 @@ class TestConfirmation(TestCase): self.t.config("confirmation", "on") # Create some playthings. - for id in range(1,11): + for id in range(1, 11): self.t("add foo") # Test the various forms of "Yes". @@ -87,7 +88,9 @@ class TestBug1438(TestCase): code, out, err = self.t("list") self.assertIn("Sometimes", out) - code, out, err = self.t("rc.confirmation=off rc.recurrence.confirmation=off 2 mod /Sometimes/Everytime/") + code, out, err = self.t( + "rc.confirmation=off rc.recurrence.confirmation=off 2 mod /Sometimes/Everytime/" + ) self.assertIn("Modified 1 task", out) code, out, err = self.t("list") self.assertIn("Everytime", out) @@ -95,6 +98,7 @@ class TestBug1438(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/context.test.py b/test/context.test.py index 2b3661ec2..db5405394 100755 --- a/test/context.test.py +++ b/test/context.test.py @@ -44,186 +44,205 @@ class ContextManagementTest(TestCase): """With confirmation active, prompt if context filter matches no tasks""" self.t.config("confirmation", "on") - code, out, err = self.t.runError('context define work project:Work', input="y\nn\n") + code, out, err = self.t.runError( + "context define work project:Work", input="y\nn\n" + ) self.assertIn("The filter 'project:Work' matches 0 pending tasks.", out) self.assertNotIn("Context 'work' defined", out) # Assert the config contains context definition - self.assertNotIn('context.work=project:Work\n', self.t.taskrc_content) + self.assertNotIn("context.work=project:Work\n", self.t.taskrc_content) def test_context_define(self): """Test simple context definition.""" - code, out, err = self.t('context define work project:Work', input="y\ny\nn\n") + code, out, err = self.t("context define work project:Work", input="y\ny\nn\n") self.assertIn("Context 'work' defined", out) # Assert the config contains read context definition - context_line = 'context.work.read=project:Work\n' + context_line = "context.work.read=project:Work\n" self.assertIn(context_line, self.t.taskrc_content) # Assert that it contains the definition only once self.assertEqual(self.t.taskrc_content.count(context_line), 1) # Assert the config does not contain write context definition - context_line = 'context.work.write=project:Work\n' + context_line = "context.work.write=project:Work\n" self.assertNotIn(context_line, self.t.taskrc_content) # Assert that legacy style was not used # Assert the config contains read context definition - context_line = 'context.work=project:Work\n' + context_line = "context.work=project:Work\n" self.assertNotIn(context_line, self.t.taskrc_content) def test_context_redefine_same_definition(self): """Test re-defining the context with the same definition.""" - self.t('context define work project:Work', input='y\ny\ny\n') - code, out, err = self.t('context define work project:Work', input='y\ny\ny\n') + self.t("context define work project:Work", input="y\ny\ny\n") + code, out, err = self.t("context define work project:Work", input="y\ny\ny\n") self.assertIn("Context 'work' defined (read, write).", out) # Assert the config contains context definition - context_line = 'context.work.read=project:Work\n' + context_line = "context.work.read=project:Work\n" self.assertIn(context_line, self.t.taskrc_content) self.assertEqual(self.t.taskrc_content.count(context_line), 1) - context_line = 'context.work.write=project:Work\n' + context_line = "context.work.write=project:Work\n" self.assertIn(context_line, self.t.taskrc_content) self.assertEqual(self.t.taskrc_content.count(context_line), 1) def test_context_redefine_different_definition(self): """Test re-defining the context with different definition.""" - self.t('context define work project:Work', input='y\ny\ny\n') - code, out, err = self.t('context define work +work', input='y\ny\ny\n') + self.t("context define work project:Work", input="y\ny\ny\n") + code, out, err = self.t("context define work +work", input="y\ny\ny\n") self.assertIn("Context 'work' defined", out) # Assert the config does not contain the old context definition - self.assertNotIn('context.work.read=project:Work\n', self.t.taskrc_content) - self.assertNotIn('context.work.write=project:Work\n', self.t.taskrc_content) + self.assertNotIn("context.work.read=project:Work\n", self.t.taskrc_content) + self.assertNotIn("context.work.write=project:Work\n", self.t.taskrc_content) # Assert the config contains context definition - context_line = 'context.work.read=+work\n' + context_line = "context.work.read=+work\n" self.assertIn(context_line, self.t.taskrc_content) self.assertEqual(self.t.taskrc_content.count(context_line), 1) - context_line = 'context.work.write=+work\n' + context_line = "context.work.write=+work\n" self.assertIn(context_line, self.t.taskrc_content) self.assertEqual(self.t.taskrc_content.count(context_line), 1) def test_context_define_invalid_for_write_due_to_modifier(self): """Test definition of a context that is not a valid write context.""" self.t.config("confirmation", "off") - code, out, err = self.t('context define urgent due.before:today') + code, out, err = self.t("context define urgent due.before:today") self.assertIn("Context 'urgent' defined", out) # Assert the config contains read context definition - context_line = 'context.urgent.read=due.before:today\n' + context_line = "context.urgent.read=due.before:today\n" self.assertIn(context_line, self.t.taskrc_content) # Assert that it contains the definition only once self.assertEqual(self.t.taskrc_content.count(context_line), 1) # Assert the config does not contain write context definition - context_line = 'context.urgent.write=due.before:today\n' + context_line = "context.urgent.write=due.before:today\n" self.assertNotIn(context_line, self.t.taskrc_content) # Assert that the write context was not set at all - self.assertNotIn('context.urgent.write=', self.t.taskrc_content) + self.assertNotIn("context.urgent.write=", self.t.taskrc_content) # Assert that legacy style was not used # Assert the config contains read context definition - context_line = 'context.urgent=due.before:today\n' + context_line = "context.urgent=due.before:today\n" self.assertNotIn(context_line, self.t.taskrc_content) def test_context_define_invalid_for_write_due_to_operator(self): """Test definition of a context that is not a valid write context because it uses an OR operator.""" self.t.config("confirmation", "off") - code, out, err = self.t('context define urgent due:today or +next') + code, out, err = self.t("context define urgent due:today or +next") self.assertIn("Context 'urgent' defined", out) # Assert the config contains read context definition - context_line = 'context.urgent.read=due:today or +next\n' + context_line = "context.urgent.read=due:today or +next\n" self.assertIn(context_line, self.t.taskrc_content) # Assert that it contains the definition only once self.assertEqual(self.t.taskrc_content.count(context_line), 1) # Assert the config does not contain write context definition - context_line = 'context.urgent.write=due:today or +next\n' + context_line = "context.urgent.write=due:today or +next\n" self.assertNotIn(context_line, self.t.taskrc_content) # Assert that the write context was not set at all - self.assertNotIn('context.urgent.write=', self.t.taskrc_content) + self.assertNotIn("context.urgent.write=", self.t.taskrc_content) # Assert that legacy style was not used # Assert the config contains read context definition - context_line = 'context.urgent=due:today or +next\n' + context_line = "context.urgent=due:today or +next\n" self.assertNotIn(context_line, self.t.taskrc_content) def test_context_define_invalid_for_write_due_to_tag_exclusion(self): """Test definition of a context that is not a valid write context because it contains a tag exclusion.""" self.t.config("confirmation", "off") - code, out, err = self.t('context define nowork due:today -work') + code, out, err = self.t("context define nowork due:today -work") self.assertIn("Context 'nowork' defined", out) # Assert the config contains read context definition - context_line = 'context.nowork.read=due:today -work\n' + context_line = "context.nowork.read=due:today -work\n" self.assertIn(context_line, self.t.taskrc_content) # Assert that it contains the definition only once self.assertEqual(self.t.taskrc_content.count(context_line), 1) # Assert the config does not contain write context definition - context_line = 'context.nowork.write=due:today -work\n' + context_line = "context.nowork.write=due:today -work\n" self.assertNotIn(context_line, self.t.taskrc_content) # Assert that the write context was not set at all - self.assertNotIn('context.nowork.write=', self.t.taskrc_content) + self.assertNotIn("context.nowork.write=", self.t.taskrc_content) # Assert that legacy style was not used # Assert the config contains read context definition - context_line = 'context.nowork=due:today -work\n' + context_line = "context.nowork=due:today -work\n" self.assertNotIn(context_line, self.t.taskrc_content) def test_context_delete(self): """Test simple context deletion.""" - self.t('context define work project:Work', input='y\ny\n') - code, out, err = self.t('context delete work', input='y\ny\n') + self.t("context define work project:Work", input="y\ny\n") + code, out, err = self.t("context delete work", input="y\ny\n") self.assertIn("Context 'work' deleted.", out) # Assert that taskrc does not countain context work definition - self.assertFalse(any('context.work' in line for line in self.t.taskrc_content)) + self.assertFalse(any("context.work" in line for line in self.t.taskrc_content)) def test_context_delete_undefined(self): """Test deletion of undefined context.""" - code, out, err = self.t.runError('context delete foo', input='y\n') + code, out, err = self.t.runError("context delete foo", input="y\n") self.assertIn("Context 'foo' not found.", err) # Assert that taskrc does not countain context work definition - self.assertFalse(any('context.foo.read=' in line for line in self.t.taskrc_content)) - self.assertFalse(any('context.foo.write=' in line for line in self.t.taskrc_content)) + self.assertFalse( + any("context.foo.read=" in line for line in self.t.taskrc_content) + ) + self.assertFalse( + any("context.foo.write=" in line for line in self.t.taskrc_content) + ) def test_context_delete_unset_after_removal(self): """Test that context is unset if its definition has been removed.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context work') - code, out, err = self.t('context delete work', input='y\ny\n') + self.t("context define work project:Work", input="y\ny\n") + self.t("context work") + code, out, err = self.t("context delete work", input="y\ny\n") self.assertIn("Context 'work' deleted.", out) # Assert that taskrc does not countain context work definition - self.assertFalse(any('context.work=' in line for line in self.t.taskrc_content)) - self.assertFalse(any('context.work.read=' in line for line in self.t.taskrc_content)) - self.assertFalse(any('context.work.write=' in line for line in self.t.taskrc_content)) + self.assertFalse(any("context.work=" in line for line in self.t.taskrc_content)) + self.assertFalse( + any("context.work.read=" in line for line in self.t.taskrc_content) + ) + self.assertFalse( + any("context.work.write=" in line for line in self.t.taskrc_content) + ) # Aseert that the context is not set - code, out, err = self.t('context show') - self.assertIn('No context is currently applied.', out) - self.assertFalse(any(re.search(r"^context(\.(read|write))?=", line) for line in self.t.taskrc_content)) + code, out, err = self.t("context show") + self.assertIn("No context is currently applied.", out) + self.assertFalse( + any( + re.search(r"^context(\.(read|write))?=", line) + for line in self.t.taskrc_content + ) + ) def test_context_list_active(self): """Test the 'context list' command.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context define home +home', input='y\ny\n') - self.t('context home') - code, out, err = self.t('context list') - contains_work = lambda line: 'work' in line and 'project:Work' in line and 'no' in line - contains_home = lambda line: 'home' in line and '+home' in line and 'yes' in line + self.t("context define work project:Work", input="y\ny\n") + self.t("context define home +home", input="y\ny\n") + self.t("context home") + code, out, err = self.t("context list") + contains_work = ( + lambda line: "work" in line and "project:Work" in line and "no" in line + ) + contains_home = ( + lambda line: "home" in line and "+home" in line and "yes" in line + ) # Assert that output contains work and home context definitions exactly # once @@ -232,39 +251,41 @@ class ContextManagementTest(TestCase): def test_context_list_legacy(self): """Test the determination of legacy context definition.""" - self.t('config context.old project:Old', input='y\n') - self.t('context old') - code, out, err = self.t('context list') + self.t("config context.old project:Old", input="y\n") + self.t("context old") + code, out, err = self.t("context list") # Assert that "old" context has only the read component defined - self.assertRegex(out, r'read\s+project:Old\s+yes') - self.assertRegex(out, r'write\s+yes') + self.assertRegex(out, r"read\s+project:Old\s+yes") + self.assertRegex(out, r"write\s+yes") def test_context_initially_empty(self): """Test that no context is set initially.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context define home +home', input='y\ny\n') + self.t("context define work project:Work", input="y\ny\n") + self.t("context define home +home", input="y\ny\n") - code, out, err = self.t('context show') - self.assertIn('No context is currently applied.', out) - self.assertFalse(any(re.search("^context=", line) for line in self.t.taskrc_content)) + code, out, err = self.t("context show") + self.assertIn("No context is currently applied.", out) + self.assertFalse( + any(re.search("^context=", line) for line in self.t.taskrc_content) + ) def test_context_setting(self): """Test simple context setting.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context define home home', input='y\ny\n') + self.t("context define work project:Work", input="y\ny\n") + self.t("context define home home", input="y\ny\n") - code, out, err = self.t('context home') + code, out, err = self.t("context home") self.assertIn("Context 'home' set.", out) self.assertIn("context=home\n", self.t.taskrc_content) def test_context_resetting(self): """Test resetting the same context.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context define home +home', input='y\ny\n') + self.t("context define work project:Work", input="y\ny\n") + self.t("context define home +home", input="y\ny\n") - self.t('context home') - code, out, err = self.t('context home') + self.t("context home") + code, out, err = self.t("context home") self.assertIn("Context 'home' set.", out) context_line = "context=home\n" @@ -272,92 +293,98 @@ class ContextManagementTest(TestCase): def test_context_switching(self): """Test changing the context.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context define home +home', input='y\ny\n') + self.t("context define work project:Work", input="y\ny\n") + self.t("context define home +home", input="y\ny\n") # Switch to home context - code, out, err = self.t('context home') + code, out, err = self.t("context home") self.assertIn("Context 'home' set.", out) self.assertEqual(self.t.taskrc_content.count("context=home\n"), 1) # Switch to work context - code, out, err = self.t('context work') + code, out, err = self.t("context work") self.assertIn("Context 'work' set.", out) self.assertNotIn("context=home\n", self.t.taskrc_content) self.assertEqual(self.t.taskrc_content.count("context=work\n"), 1) # Switch back to home context - code, out, err = self.t('context home') + code, out, err = self.t("context home") self.assertIn("Context 'home' set.", out) self.assertNotIn("context=work\n", self.t.taskrc_content) self.assertEqual(self.t.taskrc_content.count("context=home\n"), 1) def test_context_unsetting(self): """Test removing the context.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context define home +home', input='y\ny\n') + self.t("context define work project:Work", input="y\ny\n") + self.t("context define home +home", input="y\ny\n") - self.t('context home') - code, out, err = self.t('context none') + self.t("context home") + code, out, err = self.t("context none") # Assert expected output. self.assertIn("Context unset.", out) # Assert no context definition in the taskrc - contains_any_context = lambda line: re.match('^context=', line) - self.assertFalse(any(contains_any_context(line) for line in self.t.taskrc_content)) + contains_any_context = lambda line: re.match("^context=", line) + self.assertFalse( + any(contains_any_context(line) for line in self.t.taskrc_content) + ) # Assert no context showing up using show subcommand - code, out, err = self.t('context show') + code, out, err = self.t("context show") self.assertIn("No context is currently applied.", out) def test_context_unsetting_after_switching(self): """Test unsetting the context after changing the context around.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context define home +home', input='y\ny\n') + self.t("context define work project:Work", input="y\ny\n") + self.t("context define home +home", input="y\ny\n") # Switch to contexts around - self.t('context home') - self.t('context work') - self.t('context home') + self.t("context home") + self.t("context work") + self.t("context home") # Unset the context - code, out, err = self.t('context none') + code, out, err = self.t("context none") # Assert expected output. self.assertIn("Context unset.", out) # Assert no context definition in the taskrc - contains_any_context = lambda line: re.match('^context=', line) - self.assertFalse(any(contains_any_context(line) for line in self.t.taskrc_content)) + contains_any_context = lambda line: re.match("^context=", line) + self.assertFalse( + any(contains_any_context(line) for line in self.t.taskrc_content) + ) # Assert no context showing up using show subcommand - code, out, err = self.t('context show') + code, out, err = self.t("context show") self.assertIn("No context is currently applied.", out) def test_context_unsetting_with_no_context_set(self): """Test removing the context when no context is set.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context define home +home', input='y\ny\n') + self.t("context define work project:Work", input="y\ny\n") + self.t("context define home +home", input="y\ny\n") - code, out, err = self.t.runError('context none') + code, out, err = self.t.runError("context none") # Assert expected output. self.assertIn("Context not unset.", err) # Assert no context definition in the taskrc - contains_any_context = lambda line: re.match('^context=', line) - self.assertFalse(any(contains_any_context(line) for line in self.t.taskrc_content)) + contains_any_context = lambda line: re.match("^context=", line) + self.assertFalse( + any(contains_any_context(line) for line in self.t.taskrc_content) + ) # Assert no context showing up using show subcommand - code, out, err = self.t('context show') + code, out, err = self.t("context show") self.assertIn("No context is currently applied.", out) def test_context(self): """Test the _context command.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context define home +home', input='y\ny\n') - code, out, err = self.t('_context') + self.t("context define work project:Work", input="y\ny\n") + self.t("context define home +home", input="y\ny\n") + code, out, err = self.t("_context") # Assert expected output. self.assertIn("work", out.splitlines()) @@ -366,12 +393,12 @@ class ContextManagementTest(TestCase): def test_context_completion(self): """Test the _context command with some context set.""" - self.t('context define work project:Work', input='y\ny\n') - self.t('context define home +home', input='y\ny\n') + self.t("context define work project:Work", input="y\ny\n") + self.t("context define home +home", input="y\ny\n") # Activate some context - self.t('context work') - code, out, err = self.t('_context') + self.t("context work") + code, out, err = self.t("_context") # Assert expected output. self.assertIn("work", out.splitlines()) @@ -386,9 +413,9 @@ class ContextEvaluationTest(TestCase): self.t.config("confirmation", "off") # Setup contexts - self.t('context define work project:Work') - self.t('context define home +home') - self.t('context define today due:today') + self.t("context define work project:Work") + self.t("context define home +home") + self.t("context define today due:today") # Setup tasks self.t('add project:Work "work task"') @@ -398,7 +425,7 @@ class ContextEvaluationTest(TestCase): def test_context_evaluation(self): """Test the context applied with report list command.""" - code, out, err = self.t('list') + code, out, err = self.t("list") # Assert all the tasks are present in the output self.assertIn("work task", out) @@ -407,8 +434,8 @@ class ContextEvaluationTest(TestCase): self.assertIn("home today task", out) # Set the home context and rerun the report - self.t('context home') - code, out, err = self.t('list') + self.t("context home") + code, out, err = self.t("list") # Assert all the tasks with the home tag are present in the output self.assertNotIn("work task", out) @@ -418,7 +445,7 @@ class ContextEvaluationTest(TestCase): def test_context_evaluation_switching(self): """Test swtiching context using the list report.""" - code, out, err = self.t('list') + code, out, err = self.t("list") # Assert all the tasks are present in the output self.assertIn("work task", out) @@ -427,8 +454,8 @@ class ContextEvaluationTest(TestCase): self.assertIn("home today task", out) # Set the home context and rerun the report - self.t('context home') - code, out, err = self.t('list') + self.t("context home") + code, out, err = self.t("list") # Assert all the tasks with the home tag are present in the output self.assertNotIn("work task", out) @@ -437,8 +464,8 @@ class ContextEvaluationTest(TestCase): self.assertIn("home today task", out) # Set the work context and rerun the report - self.t('context work') - code, out, err = self.t('list') + self.t("context work") + code, out, err = self.t("list") # Assert all the tasks with the home tag are present in the output self.assertIn("work task", out) @@ -447,8 +474,8 @@ class ContextEvaluationTest(TestCase): self.assertNotIn("home today task", out) # Set the today context and rerun the report - self.t('context today') - code, out, err = self.t('list') + self.t("context today") + code, out, err = self.t("list") # Assert all the tasks with the home tag are present in the output self.assertNotIn("work task", out) @@ -458,8 +485,8 @@ class ContextEvaluationTest(TestCase): def test_context_evaluation_unset(self): """Test unsetting context with report list command.""" - self.t('context home') - code, out, err = self.t('list') + self.t("context home") + code, out, err = self.t("list") # Assert all the tasks home tagged tasks are present self.assertNotIn("work task", out) @@ -468,8 +495,8 @@ class ContextEvaluationTest(TestCase): self.assertIn("home today task", out) # Set the context to none - self.t('context none') - code, out, err = self.t('list') + self.t("context none") + code, out, err = self.t("list") # Assert all the tasks are present in the output self.assertIn("work task", out) @@ -481,8 +508,8 @@ class ContextEvaluationTest(TestCase): """Test the context applied with report list command combined with user filters.""" # Set the home context - self.t('context home') - code, out, err = self.t('list due:today') + self.t("context home") + code, out, err = self.t("list due:today") # Assert all the tasks are present in the output self.assertNotIn("work task", out) @@ -491,8 +518,8 @@ class ContextEvaluationTest(TestCase): self.assertIn("home today task", out) # Set the work context and rerun the report - self.t('context work') - code, out, err = self.t('list due:today') + self.t("context work") + code, out, err = self.t("list due:today") # Assert all the tasks are present in the output self.assertNotIn("work task", out) @@ -506,10 +533,10 @@ class ContextEvaluationTest(TestCase): filters are used. """ - self.t('context home') + self.t("context home") # Try task not included in context - output = self.t('1 list')[1] + output = self.t("1 list")[1] # Assert that ID filter works even if it does not match the context self.assertIn("work task", output) @@ -518,7 +545,7 @@ class ContextEvaluationTest(TestCase): self.assertNotIn("home today task", output) # Try task included in context - output = self.t('2 list')[1] + output = self.t("2 list")[1] # Assert that ID filter works if it does match # the context (sanity check) @@ -528,7 +555,7 @@ class ContextEvaluationTest(TestCase): self.assertNotIn("home today task", output) # Test for combination of IDs - output = self.t('1 2 list')[1] + output = self.t("1 2 list")[1] # Assert that ID filter works if it partly matches # and partly does not match the context @@ -543,12 +570,12 @@ class ContextEvaluationTest(TestCase): filters are used. """ - self.t('context home') - first_uuid = self.t('_get 1.uuid')[1] - second_uuid = self.t('_get 2.uuid')[1] + self.t("context home") + first_uuid = self.t("_get 1.uuid")[1] + second_uuid = self.t("_get 2.uuid")[1] # Try task not included in context - output = self.t('%s list' % first_uuid)[1] + output = self.t("%s list" % first_uuid)[1] # Assert that UUID filter works even if it does not match # the context @@ -558,7 +585,7 @@ class ContextEvaluationTest(TestCase): self.assertNotIn("home today task", output) # Try task included in context - output = self.t('%s list' % second_uuid)[1] + output = self.t("%s list" % second_uuid)[1] # Assert that UUID filter works if it does match # the context (sanity check) @@ -568,7 +595,7 @@ class ContextEvaluationTest(TestCase): self.assertNotIn("home today task", output) # Test for combination of UUIDs - output = self.t('%s %s list' % (first_uuid, second_uuid))[1] + output = self.t("%s %s list" % (first_uuid, second_uuid))[1] # Assert that UUID filter works if it partly matches # and partly does not match the context @@ -585,7 +612,7 @@ class ContextEvaluationTest(TestCase): self.t.config("report.list.context", "0") # Get the tasks - code, out, err = self.t('list') + code, out, err = self.t("list") # Assert all the tasks are present in the output self.assertIn("work task", out) @@ -594,9 +621,9 @@ class ContextEvaluationTest(TestCase): self.assertIn("home today task", out) # Set the home context and rerun the report - self.t('context home') + self.t("context home") - code, out, err = self.t('list') + code, out, err = self.t("list") # Assert nothing changed - all the tasks are present in the output self.assertIn("work task", out) @@ -643,7 +670,10 @@ class ContextErrorHandling(TestCase): """Verify 'task context show' with contexts works""" self.t.config("confirmation", "off") code, out, err = self.t("context define work +work") - self.assertIn("Context 'work' defined (read, write). Use 'task context work' to activate.", out) + self.assertIn( + "Context 'work' defined (read, write). Use 'task context work' to activate.", + out, + ) code, out, err = self.t("context work") self.assertIn("Context 'work' set. Use 'task context none' to remove.", out) @@ -660,6 +690,7 @@ class ContextErrorHandling(TestCase): code, out, err = self.t("context show") self.assertIn("No context is currently applied.", out) + class TestBug1734(TestCase): def setUp(self): self.t = Task() @@ -679,6 +710,7 @@ class TestBug1734(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python syntax=python diff --git a/test/count.test.py b/test/count.test.py index 3cd712d30..d3f6969ed 100755 --- a/test/count.test.py +++ b/test/count.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -56,6 +57,7 @@ class TestCount(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/custom.config.test.py b/test/custom.config.test.py index 329c0f516..5124802b2 100755 --- a/test/custom.config.test.py +++ b/test/custom.config.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -41,10 +42,12 @@ class TestCustomConfig(TestCase): self.t.config("alias.xyzzyx", "status:waiting") self.t.config("imnotrecognized", "kai") - self.DIFFER_MSG = ("Some of your .taskrc variables differ from the " - "default values.") - self.NOT_RECOG_MSG = ("Your .taskrc file contains these unrecognized " - "variables:") + self.DIFFER_MSG = ( + "Some of your .taskrc variables differ from the " "default values." + ) + self.NOT_RECOG_MSG = ( + "Your .taskrc file contains these unrecognized " "variables:" + ) def test_show_alias(self): """task show - warns when non-default values are matched @@ -89,6 +92,7 @@ class TestCustomConfig(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/custom.recur_ind.test.py b/test/custom.recur_ind.test.py index d0084cdae..a1a11e696 100755 --- a/test/custom.recur_ind.test.py +++ b/test/custom.recur_ind.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -41,26 +42,27 @@ class TestCustomRecurIndicator(TestCase): def test_recurrence_indicator(self): """Add a recurring and non-recurring task, look for the indicator.""" - self.t.config("report.foo.columns", "id,recur.indicator") - self.t.config("report.foo.labels", "ID,R") - self.t.config("report.foo.sort", "id+") - self.t.config("verbose", "nothing") + self.t.config("report.foo.columns", "id,recur.indicator") + self.t.config("report.foo.labels", "ID,R") + self.t.config("report.foo.sort", "id+") + self.t.config("verbose", "nothing") self.t("add foo due:tomorrow recur:weekly") self.t("add bar") code, out, err = self.t("foo") self.assertIn(" 1 R", out) - self.assertIn(" 2", out) + self.assertIn(" 2", out) self.assertIn(" 3 R", out) code, out, err = self.t("foo rc.recurrence.indicator=RE") self.assertIn(" 1 RE", out) - self.assertIn(" 2", out) + self.assertIn(" 2", out) self.assertIn(" 3 RE", out) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/custom.tag_ind.test.py b/test/custom.tag_ind.test.py index ae24bf9a4..2c67897a9 100755 --- a/test/custom.tag_ind.test.py +++ b/test/custom.tag_ind.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -40,9 +41,9 @@ class TestCustomTagIndicator(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.foo.description", "DESC") - cls.t.config("report.foo.columns", "id,tags.indicator") - cls.t.config("report.foo.labels", "ID,T") - cls.t.config("report.foo.sort", "id+") + cls.t.config("report.foo.columns", "id,tags.indicator") + cls.t.config("report.foo.labels", "ID,T") + cls.t.config("report.foo.sort", "id+") cls.t("add foo +tag") cls.t("add bar") @@ -61,6 +62,7 @@ class TestCustomTagIndicator(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/custom.test.py b/test/custom.test.py index bc839e37a..bf7a194ff 100755 --- a/test/custom.test.py +++ b/test/custom.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -39,10 +40,10 @@ class TestCustomReports(TestCase): """Executed before each test in the class""" self.t = Task() self.t.config("report.foo.description", "DESC") - self.t.config("report.foo.labels", "ID,DESCRIPTION") - self.t.config("report.foo.columns", "id,description") - self.t.config("report.foo.sort", "id+") - self.t.config("report.foo.filter", "project:A") + self.t.config("report.foo.labels", "ID,DESCRIPTION") + self.t.config("report.foo.columns", "id,description") + self.t.config("report.foo.sort", "id+") + self.t.config("report.foo.filter", "project:A") def test_custom_report_help(self): """Verify custom report description is shown in help""" @@ -72,19 +73,23 @@ class TestCustomReports(TestCase): code, out, err = self.t("foo rc._forcecolor:on rc.report.foo.filter:") self.assertIn("[44m", out) + class TestCustomErrorHandling(TestCase): def setUp(self): self.t = Task() def test_size_mismatch(self): self.t.config("report.foo.columns", "id,description") - self.t.config("report.foo.labels", "id") + self.t.config("report.foo.labels", "id") code, out, err = self.t.runError("foo") - self.assertIn("There are different numbers of columns and labels for report 'foo'.", err) + self.assertIn( + "There are different numbers of columns and labels for report 'foo'.", err + ) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/date.iso.test.py b/test/date.iso.test.py index ca656b7a3..41999314e 100755 --- a/test/date.iso.test.py +++ b/test/date.iso.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -60,6 +61,7 @@ class TestDateISOAndEpoch(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/dateformat.test.py b/test/dateformat.test.py index 60c46470d..3a1ffd41e 100755 --- a/test/dateformat.test.py +++ b/test/dateformat.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -40,8 +41,8 @@ class TestDateformat(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("report.xxx.columns", "id,due") - cls.t.config("report.xxx.labels", "ID,DUE") - cls.t.config("report.xxx.sort", "id") + cls.t.config("report.xxx.labels", "ID,DUE") + cls.t.config("report.xxx.sort", "id") def setUp(self): """Executed before each test in the class""" @@ -55,6 +56,7 @@ class TestDateformat(TestCase): code, out, err = self.t("xxx rc.dateformat:YMDTHNS") self.assertEqual(out.count("20150704T000000"), 3) + class TestBug886(TestCase): def setUp(self): """Executed before each test in the class""" @@ -63,12 +65,12 @@ class TestBug886(TestCase): def test_invalid_day(self): """886: Test invalid day synonym - Bug 886: tw doesn't warn the user if, e.g., a weekday cannot be resolved properly + Bug 886: tw doesn't warn the user if, e.g., a weekday cannot be resolved properly """ - code, out, err =self.t("add one due:sun") + code, out, err = self.t("add one due:sun") self.assertIn("Created task 1.", out) - code, out, err =self.t.runError("add two due:donkey") + code, out, err = self.t.runError("add two due:donkey") self.assertIn("'donkey' is not a valid date", err) @@ -79,33 +81,36 @@ class TestBug986(TestCase): def test_dateformat_precedence(self): """986: Verify rc.dateformat.info takes precedence over rc.dateformat""" - self.t('add test') - self.t('1 start') + self.t("add test") + self.t("1 start") - code, out, err = self.t('1 info rc.dateformat:XX rc.dateformat.info:__') - self.assertIn('__', out) - self.assertNotIn('XX', out) + code, out, err = self.t("1 info rc.dateformat:XX rc.dateformat.info:__") + self.assertIn("__", out) + self.assertNotIn("XX", out) - code, out, err = self.t('1 info rc.dateformat:__ rc.dateformat.info:') - self.assertIn('__', out) + code, out, err = self.t("1 info rc.dateformat:__ rc.dateformat.info:") + self.assertIn("__", out) class TestBug1620(TestCase): def setUp(self): """Executed before each test in the class""" self.t = Task() - self.t.config('dateformat', 'YMD-HN') + self.t.config("dateformat", "YMD-HN") def test_dateformat_overrides_iso(self): """1620: Verify that a defined dateformat overrides the ISO interpretation""" - code, out, err = self.t ('add pro:vorhaben due:20150601-1415 tatusmeeting vorbereiten') + code, out, err = self.t( + "add pro:vorhaben due:20150601-1415 tatusmeeting vorbereiten" + ) - code, out, err = self.t ('_get 1.due') + code, out, err = self.t("_get 1.due") self.assertEqual(out, "2015-06-01T14:15:00\n") - code, out, err = self.t ('long') + code, out, err = self.t("long") self.assertIn("20150601-1415", out) + class TestCapitalizedDays(TestCase): """Make sure capitalized names such as 'Friday' work. @@ -124,28 +129,30 @@ class TestCapitalizedDays(TestCase): def test_dateformat_capitalized(self): """Verify upper case days and months work""" # Lower case: - code, out, err = self.t('add sometask due:mon') - code, out, err = self.t('add sometask due:monday') - code, out, err = self.t('add sometask due:jan') - code, out, err = self.t('add sometask due:january') + code, out, err = self.t("add sometask due:mon") + code, out, err = self.t("add sometask due:monday") + code, out, err = self.t("add sometask due:jan") + code, out, err = self.t("add sometask due:january") # Upper case days of the week - code, out, err = self.t('add sometask due:Tue') - code, out, err = self.t('add sometask due:Tuesday') - code, out, err = self.t('add sometask due:Thu') - code, out, err = self.t('add sometask due:Thursday') + code, out, err = self.t("add sometask due:Tue") + code, out, err = self.t("add sometask due:Tuesday") + code, out, err = self.t("add sometask due:Thu") + code, out, err = self.t("add sometask due:Thursday") # Upper case months: - code, out, err = self.t('add sometask due:Jan') - code, out, err = self.t('add sometask due:January') - code, out, err = self.t('add sometask due:Jun') - code, out, err = self.t('add sometask due:June') - code, out, err = self.t('add sometask due:May') + code, out, err = self.t("add sometask due:Jan") + code, out, err = self.t("add sometask due:January") + code, out, err = self.t("add sometask due:Jun") + code, out, err = self.t("add sometask due:June") + code, out, err = self.t("add sometask due:May") # Incorrect: - code, out, err = self.t.runError('add sometask due:Yo') - code, out, err = self.t.runError('add sometask due:TU') + code, out, err = self.t.runError("add sometask due:Yo") + code, out, err = self.t.runError("add sometask due:TU") + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/datesort.test.py b/test/datesort.test.py index 5f270cb37..81712ad89 100755 --- a/test/datesort.test.py +++ b/test/datesort.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -41,13 +42,13 @@ class TestDateSort(TestCase): def test_datesort(self): """Verify dates sort properly with a report date format that hides date details""" - self.t.config("verbose", "nothing") - self.t.config("dateformat", "YMD") - self.t.config("report.small_list.columns", "due,description") - self.t.config("report.small_list.labels", "Due,Description") - self.t.config("report.small_list.sort", "due+") - self.t.config("report.small_list.filter", "status:pending") - self.t.config("report.small_list.dateformat", "D") + self.t.config("verbose", "nothing") + self.t.config("dateformat", "YMD") + self.t.config("report.small_list.columns", "due,description") + self.t.config("report.small_list.labels", "Due,Description") + self.t.config("report.small_list.sort", "due+") + self.t.config("report.small_list.filter", "status:pending") + self.t.config("report.small_list.dateformat", "D") self.t("add one due:20150101") self.t("add two due:20150201") @@ -65,6 +66,7 @@ class TestDateSort(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/datetime-negative.test.py b/test/datetime-negative.test.py index 8dfc258a4..5d80639c9 100755 --- a/test/datetime-negative.test.py +++ b/test/datetime-negative.test.py @@ -44,10 +44,10 @@ class BaseDateTimeNegativeTest(TestCase): self.t = Task() def assertInvalidDatetimeFormat(self, value): - self.t.runError('add due:{0} test1'.format(value)) - self.t.runError('add scheduled:{0} test2'.format(value)) - self.t.runError('add wait:{0} test3'.format(value)) - self.t.runError('add until:{0} test4'.format(value)) + self.t.runError("add due:{0} test1".format(value)) + self.t.runError("add scheduled:{0} test2".format(value)) + self.t.runError("add wait:{0} test3".format(value)) + self.t.runError("add until:{0} test4".format(value)) class TestIncorrectDate(BaseDateTimeNegativeTest): @@ -58,60 +58,60 @@ class TestIncorrectDate(BaseDateTimeNegativeTest): """ def test_set_incorrect_datetime_randomstring(self): - self.assertInvalidDatetimeFormat('random') + self.assertInvalidDatetimeFormat("random") @unittest.skip # see #2996 def test_set_incorrect_datetime_negative_in_YYYY_MM_DD(self): - self.assertInvalidDatetimeFormat('-2014-07-07') + self.assertInvalidDatetimeFormat("-2014-07-07") def test_set_incorrect_datetime_missing_day_in_YYYY_MM_DD(self): - self.assertInvalidDatetimeFormat('2014-07-') + self.assertInvalidDatetimeFormat("2014-07-") def test_set_incorrect_datetime_month_zero_in_YYYY_MM_DD(self): - self.assertInvalidDatetimeFormat('2014-0-12') + self.assertInvalidDatetimeFormat("2014-0-12") def test_set_incorrect_datetime_invalid_characters_in_YYYY_MM_DD(self): - self.assertInvalidDatetimeFormat('abcd-ab-ab') + self.assertInvalidDatetimeFormat("abcd-ab-ab") def test_set_incorrect_datetime_day_as_zeros_in_YYYY_DDD(self): - self.assertInvalidDatetimeFormat('2014-000') + self.assertInvalidDatetimeFormat("2014-000") def test_set_incorrect_datetime_overlap_day_in_nonoverlap_year_in_YYYY_DDD(self): - self.assertInvalidDatetimeFormat('2014-366') + self.assertInvalidDatetimeFormat("2014-366") def test_set_incorrect_datetime_medium_overlap_day_in_YYYY_DDD(self): - self.assertInvalidDatetimeFormat('2014-999') + self.assertInvalidDatetimeFormat("2014-999") def test_set_incorrect_datetime_huge_overlap_day_in_YYYY_DDD(self): - self.assertInvalidDatetimeFormat('2014-999999999') + self.assertInvalidDatetimeFormat("2014-999999999") def test_set_incorrect_datetime_week_with_the_number_zero_in_YYYY_Www(self): - self.assertInvalidDatetimeFormat('2014-W00') + self.assertInvalidDatetimeFormat("2014-W00") def test_set_incorrect_datetime_overflow_in_week_in_YYYY_Www(self): - self.assertInvalidDatetimeFormat('2014-W54') + self.assertInvalidDatetimeFormat("2014-W54") # Unsupported non-extended form. def test_set_incorrect_datetime_day_zero_in_YYYY_WwwD(self): - self.assertInvalidDatetimeFormat('2014-W240') + self.assertInvalidDatetimeFormat("2014-W240") # Unsupported non-extended form. def test_set_incorrect_datetime_day_eight_in_YYYY_WwwD(self): - self.assertInvalidDatetimeFormat('2014-W248') + self.assertInvalidDatetimeFormat("2014-W248") # Unsupported non-extended form. def test_set_incorrect_datetime_day_two_hundred_in_YYYY_WwwD(self): - self.assertInvalidDatetimeFormat('2014-W24200') + self.assertInvalidDatetimeFormat("2014-W24200") # Disabled: Looks like 'hhmm-hh' - #def test_set_incorrect_datetime_month_zero_in_YYYY_MM(self): + # def test_set_incorrect_datetime_month_zero_in_YYYY_MM(self): # self.assertInvalidDatetimeFormat('2014-00') def test_set_incorrect_datetime_overflow_month_in_YYYY_MM(self): - self.assertInvalidDatetimeFormat('2014-13') + self.assertInvalidDatetimeFormat("2014-13") def test_set_incorrect_datetime_huge_overflow_month_in_YYYY_MM(self): - self.assertInvalidDatetimeFormat('2014-99') + self.assertInvalidDatetimeFormat("2014-99") class TestIncorrectTime(BaseDateTimeNegativeTest): @@ -121,276 +121,276 @@ class TestIncorrectTime(BaseDateTimeNegativeTest): """ def test_set_incorrect_datetime_hour_overflow_in_hh_mm(self): - self.assertInvalidDatetimeFormat('25:00') + self.assertInvalidDatetimeFormat("25:00") def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm(self): - self.assertInvalidDatetimeFormat('99:00') + self.assertInvalidDatetimeFormat("99:00") def test_set_incorrect_datetime_minute_overflow_in_hh_mm(self): - self.assertInvalidDatetimeFormat('12:60') + self.assertInvalidDatetimeFormat("12:60") def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm(self): - self.assertInvalidDatetimeFormat('12:99') + self.assertInvalidDatetimeFormat("12:99") def test_set_incorrect_datetime_invalid_minutes_in_hh_mm(self): - self.assertInvalidDatetimeFormat('12:ab') + self.assertInvalidDatetimeFormat("12:ab") def test_set_incorrect_datetime_invalid_hours_in_hh_mm(self): - self.assertInvalidDatetimeFormat('ab:12') + self.assertInvalidDatetimeFormat("ab:12") def test_set_incorrect_datetime_invalid_time_in_hh_mm(self): - self.assertInvalidDatetimeFormat('ab:cd') + self.assertInvalidDatetimeFormat("ab:cd") @unittest.skip # see #2996 def test_set_incorrect_datetime_negative_hours_in_hh_mm(self): - self.assertInvalidDatetimeFormat('-12:12') + self.assertInvalidDatetimeFormat("-12:12") def test_set_incorrect_datetime_negative_minutes_in_hh_mm(self): - self.assertInvalidDatetimeFormat('12:-12') + self.assertInvalidDatetimeFormat("12:-12") def test_set_incorrect_datetime_hour_overflow_in_hh_mmZ(self): - self.assertInvalidDatetimeFormat('25:00Z') + self.assertInvalidDatetimeFormat("25:00Z") def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mmZ(self): - self.assertInvalidDatetimeFormat('99:00Z') + self.assertInvalidDatetimeFormat("99:00Z") def test_set_incorrect_datetime_minute_overflow_in_hh_mmZ(self): - self.assertInvalidDatetimeFormat('12:60Z') + self.assertInvalidDatetimeFormat("12:60Z") def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mmZ(self): - self.assertInvalidDatetimeFormat('12:99Z') + self.assertInvalidDatetimeFormat("12:99Z") def test_set_incorrect_datetime_invalid_minutes_in_hh_mmZ(self): - self.assertInvalidDatetimeFormat('12:abZ') + self.assertInvalidDatetimeFormat("12:abZ") def test_set_incorrect_datetime_invalid_hours_in_hh_mmZ(self): - self.assertInvalidDatetimeFormat('ab:12Z') + self.assertInvalidDatetimeFormat("ab:12Z") def test_set_incorrect_datetime_invalid_time_in_hh_mmZ(self): - self.assertInvalidDatetimeFormat('ab:cdZ') + self.assertInvalidDatetimeFormat("ab:cdZ") @unittest.skip # see #2996 def test_set_incorrect_datetime_negative_hours_in_hh_mmZ(self): - self.assertInvalidDatetimeFormat('-12:12Z') + self.assertInvalidDatetimeFormat("-12:12Z") def test_set_incorrect_datetime_negative_minutes_in_hh_mmZ(self): - self.assertInvalidDatetimeFormat('12:-12Z') + self.assertInvalidDatetimeFormat("12:-12Z") def test_set_incorrect_datetime_hour_overflow_in_hh_mm_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('25:00+01:00') + self.assertInvalidDatetimeFormat("25:00+01:00") def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('99:00+01:00') + self.assertInvalidDatetimeFormat("99:00+01:00") def test_set_incorrect_datetime_minute_overflow_in_hh_mm_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:60+01:00') + self.assertInvalidDatetimeFormat("12:60+01:00") def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:99+01:00') + self.assertInvalidDatetimeFormat("12:99+01:00") def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:ab+01:00') + self.assertInvalidDatetimeFormat("12:ab+01:00") def test_set_incorrect_datetime_invalid_hours_in_hh_mm_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('ab:12+01:00') + self.assertInvalidDatetimeFormat("ab:12+01:00") def test_set_incorrect_datetime_invalid_time_in_hh_mm_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('ab:cd+01:00') + self.assertInvalidDatetimeFormat("ab:cd+01:00") @unittest.skip # see #2996 def test_set_incorrect_datetime_negative_hours_in_hh_mm_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('-12:12+01:00') + self.assertInvalidDatetimeFormat("-12:12+01:00") def test_set_incorrect_datetime_negative_minutes_in_hh_mm_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:-12+01:00') + self.assertInvalidDatetimeFormat("12:-12+01:00") def test_set_incorrect_datetime_hour_overflow_in_hh_mm_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('25:00-01:00') + self.assertInvalidDatetimeFormat("25:00-01:00") def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('99:00-01:00') + self.assertInvalidDatetimeFormat("99:00-01:00") def test_set_incorrect_datetime_minute_overflow_in_hh_mm_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:60-01:00') + self.assertInvalidDatetimeFormat("12:60-01:00") def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:99-01:00') + self.assertInvalidDatetimeFormat("12:99-01:00") def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:ab-01:00') + self.assertInvalidDatetimeFormat("12:ab-01:00") def test_set_incorrect_datetime_invalid_hours_in_hh_mm_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('ab:12-01:00') + self.assertInvalidDatetimeFormat("ab:12-01:00") def test_set_incorrect_datetime_invalid_time_in_hh_mm_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('ab:cd-01:00') + self.assertInvalidDatetimeFormat("ab:cd-01:00") @unittest.skip # see #2996 def test_set_incorrect_datetime_negative_hours_in_hh_mm_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('-12:12-01:00') + self.assertInvalidDatetimeFormat("-12:12-01:00") def test_set_incorrect_datetime_negative_minutes_in_hh_mm_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:-12-01:00') + self.assertInvalidDatetimeFormat("12:-12-01:00") def test_set_incorrect_datetime_hour_overflow_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('25:00:00') + self.assertInvalidDatetimeFormat("25:00:00") def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('99:00:00') + self.assertInvalidDatetimeFormat("99:00:00") def test_set_incorrect_datetime_minute_overflow_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('12:60:00') + self.assertInvalidDatetimeFormat("12:60:00") def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('12:99:00') + self.assertInvalidDatetimeFormat("12:99:00") def test_set_incorrect_datetime_second_overflow_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('12:12:60') + self.assertInvalidDatetimeFormat("12:12:60") def test_set_incorrect_datetime_huge_second_overflow_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('12:12:99') + self.assertInvalidDatetimeFormat("12:12:99") def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('12:ab:00') + self.assertInvalidDatetimeFormat("12:ab:00") def test_set_incorrect_datetime_invalid_hours_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('ab:12:00') + self.assertInvalidDatetimeFormat("ab:12:00") def test_set_incorrect_datetime_invalid_seconds_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('12:12:ab') + self.assertInvalidDatetimeFormat("12:12:ab") def test_set_incorrect_datetime_invalid_time_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('ab:cd:ef') + self.assertInvalidDatetimeFormat("ab:cd:ef") @unittest.skip # see #2996 def test_set_incorrect_datetime_negative_hours_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('-12:12:12') + self.assertInvalidDatetimeFormat("-12:12:12") def test_set_incorrect_datetime_negative_minutes_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('12:-12:12') + self.assertInvalidDatetimeFormat("12:-12:12") def test_set_incorrect_datetime_negative_seconds_in_hh_mm_ss(self): - self.assertInvalidDatetimeFormat('12:12:-12') + self.assertInvalidDatetimeFormat("12:12:-12") def test_set_incorrect_datetime_hour_overflow_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('25:00:00Z') + self.assertInvalidDatetimeFormat("25:00:00Z") def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('99:00:00Z') + self.assertInvalidDatetimeFormat("99:00:00Z") def test_set_incorrect_datetime_minute_overflow_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('12:60:00Z') + self.assertInvalidDatetimeFormat("12:60:00Z") def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('12:99:00Z') + self.assertInvalidDatetimeFormat("12:99:00Z") def test_set_incorrect_datetime_second_overflow_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('12:12:60Z') + self.assertInvalidDatetimeFormat("12:12:60Z") def test_set_incorrect_datetime_huge_second_overflow_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('12:12:99Z') + self.assertInvalidDatetimeFormat("12:12:99Z") def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('12:ab:00Z') + self.assertInvalidDatetimeFormat("12:ab:00Z") def test_set_incorrect_datetime_invalid_hours_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('ab:12:00Z') + self.assertInvalidDatetimeFormat("ab:12:00Z") def test_set_incorrect_datetime_invalid_seconds_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('12:12:abZ') + self.assertInvalidDatetimeFormat("12:12:abZ") def test_set_incorrect_datetime_invalid_time_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('ab:cd:efZ') + self.assertInvalidDatetimeFormat("ab:cd:efZ") @unittest.skip # see #2996 def test_set_incorrect_datetime_negative_hours_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('-12:12:12Z') + self.assertInvalidDatetimeFormat("-12:12:12Z") def test_set_incorrect_datetime_negative_minutes_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('12:-12:12Z') + self.assertInvalidDatetimeFormat("12:-12:12Z") def test_set_incorrect_datetime_negative_seconds_in_hh_mm_ssZ(self): - self.assertInvalidDatetimeFormat('12:12:-12Z') + self.assertInvalidDatetimeFormat("12:12:-12Z") def test_set_incorrect_datetime_hour_overflow_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('25:00:00+01:00') + self.assertInvalidDatetimeFormat("25:00:00+01:00") def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('95:00:00+01:00') + self.assertInvalidDatetimeFormat("95:00:00+01:00") def test_set_incorrect_datetime_minute_overflow_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:60:00+01:00') + self.assertInvalidDatetimeFormat("12:60:00+01:00") def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:99:00+01:00') + self.assertInvalidDatetimeFormat("12:99:00+01:00") def test_set_incorrect_datetime_second_overflow_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:12:60+01:00') + self.assertInvalidDatetimeFormat("12:12:60+01:00") def test_set_incorrect_datetime_huge_second_overflow_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:12:99+01:00') + self.assertInvalidDatetimeFormat("12:12:99+01:00") def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:ab:00+01:00') + self.assertInvalidDatetimeFormat("12:ab:00+01:00") def test_set_incorrect_datetime_invalid_hours_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('ab:12:00+01:00') + self.assertInvalidDatetimeFormat("ab:12:00+01:00") def test_set_incorrect_datetime_invalid_seconds_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:12:ab+01:00') + self.assertInvalidDatetimeFormat("12:12:ab+01:00") def test_set_incorrect_datetime_invalid_time_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('ab:cd:ef+01:00') + self.assertInvalidDatetimeFormat("ab:cd:ef+01:00") @unittest.skip # see #2996 def test_set_incorrect_datetime_negative_hours_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('-12:12:12+01:00') + self.assertInvalidDatetimeFormat("-12:12:12+01:00") def test_set_incorrect_datetime_negative_minutes_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:-12:12+01:00') + self.assertInvalidDatetimeFormat("12:-12:12+01:00") def test_set_incorrect_datetime_negative_seconds_in_hh_mm_ss_plus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:12:-12+01:00') + self.assertInvalidDatetimeFormat("12:12:-12+01:00") def test_set_incorrect_datetime_hour_overflow_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('25:00:00-01:00') + self.assertInvalidDatetimeFormat("25:00:00-01:00") def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('95:00:00-01:00') + self.assertInvalidDatetimeFormat("95:00:00-01:00") def test_set_incorrect_datetime_minute_overflow_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:60:00-01:00') + self.assertInvalidDatetimeFormat("12:60:00-01:00") def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:99:00-01:00') + self.assertInvalidDatetimeFormat("12:99:00-01:00") def test_set_incorrect_datetime_second_overflow_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:12:60-01:00') + self.assertInvalidDatetimeFormat("12:12:60-01:00") def test_set_incorrect_datetime_huge_second_overflow_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:12:99-01:00') + self.assertInvalidDatetimeFormat("12:12:99-01:00") def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:ab:00-01:00') + self.assertInvalidDatetimeFormat("12:ab:00-01:00") def test_set_incorrect_datetime_invalid_hours_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('ab:12:00-01:00') + self.assertInvalidDatetimeFormat("ab:12:00-01:00") def test_set_incorrect_datetime_invalid_seconds_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:12:ab-01:00') + self.assertInvalidDatetimeFormat("12:12:ab-01:00") def test_set_incorrect_datetime_invalid_time_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('ab:cd:ef-01:00') + self.assertInvalidDatetimeFormat("ab:cd:ef-01:00") @unittest.skip # see #2996 def test_set_incorrect_datetime_negative_hours_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('-12:12:12-01:00') + self.assertInvalidDatetimeFormat("-12:12:12-01:00") def test_set_incorrect_datetime_negative_minutes_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:-12:12-01:00') + self.assertInvalidDatetimeFormat("12:-12:12-01:00") def test_set_incorrect_datetime_negative_seconds_in_hh_mm_ss_minus_hh_mm(self): - self.assertInvalidDatetimeFormat('12:12:-12-01:00') + self.assertInvalidDatetimeFormat("12:12:-12-01:00") # There were a group of tests that failed for the wrong reason, and these # have been removed. @@ -434,8 +434,10 @@ class TestIncorrectTime(BaseDateTimeNegativeTest): # 12:12+03:2 # 12:12+3:2 + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/debug.test.py b/test/debug.test.py index 2f4c70467..1838a51d9 100755 --- a/test/debug.test.py +++ b/test/debug.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -87,6 +88,7 @@ class TestDebugMode(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/default.test.py b/test/default.test.py index 5bab560b6..8afdf91f5 100755 --- a/test/default.test.py +++ b/test/default.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -40,8 +41,8 @@ class TestCMD(TestCase): cls.t = Task() cls.t.config("default.command", "list") - cls.t('add one') - cls.t('add two') + cls.t("add one") + cls.t("add two") def test_default_command(self): """default command""" @@ -51,8 +52,8 @@ class TestCMD(TestCase): def test_info_command(self): """info command""" - code, out, err = self.t('1') - self.assertRegex(out, r'Description\s+one') + code, out, err = self.t("1") + self.assertRegex(out, r"Description\s+one") class TestDefaults(TestCase): @@ -60,11 +61,11 @@ class TestDefaults(TestCase): def setUpClass(cls): """Executed once before any test in the class""" cls.t = Task() - cls.t.config("default.command", "list") - cls.t.config("default.project", "PROJECT") + cls.t.config("default.command", "list") + cls.t.config("default.project", "PROJECT") cls.t.config("uda.priority.default", "M") - cls.t.config("default.due", "eom") - cls.t.config("default.scheduled", "eom") + cls.t.config("default.due", "eom") + cls.t.config("default.scheduled", "eom") def test_all_defaults(self): """Verify all defaults are employed""" @@ -125,6 +126,7 @@ class TestBug1377(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/delete.test.py b/test/delete.test.py index bc998724d..29953559c 100755 --- a/test/delete.test.py +++ b/test/delete.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -46,7 +47,7 @@ class TestDelete(TestCase): uuid = out.strip() self.t("1 delete", input="y\n") - self.t.runError("list") # GC/handleRecurrence + self.t.runError("list") # GC/handleRecurrence code, out, err = self.t("_get 1.status") self.assertEqual("\n", out) @@ -77,7 +78,7 @@ class TestDelete(TestCase): code, out, err = self.t("1 done") self.assertIn("Completed 1 task.", out) - self.t("all") # GC/handleRecurrence + self.t("all") # GC/handleRecurrence code, out, err = self.t("%s delete" % uuid, input="y\n") self.assertIn("Deleted 1 task.", out) @@ -141,6 +142,7 @@ class TestDelete(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/denotate.test.py b/test/denotate.test.py index 341346434..50b81f773 100755 --- a/test/denotate.test.py +++ b/test/denotate.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -68,7 +69,9 @@ class TestDenotate(TestCase): # Failed partial match, one annotation code, out, err = self.t.runError("rc.search.case.sensitive=yes 2 denotate AL") - self.assertIn("Did not find any matching annotation to be deleted for 'AL'.", out) + self.assertIn( + "Did not find any matching annotation to be deleted for 'AL'.", out + ) # Exact match, two annotations code, out, err = self.t("1 denotate beta") @@ -91,6 +94,7 @@ class TestDenotate(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/dependencies.test.py b/test/dependencies.test.py index 971cf34a1..e9a6bc307 100755 --- a/test/dependencies.test.py +++ b/test/dependencies.test.py @@ -29,6 +29,7 @@ import string import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -81,9 +82,9 @@ class TestDependencies(TestCase): def test_circular_5(self): """Check circular dependencies are caught, using 5 tasks""" - self.t("add three") - self.t("add four") - self.t("add five") + self.t("add three") + self.t("add four") + self.t("add five") self.t("5 modify dep:4") self.t("4 modify dep:3") self.t("3 modify dep:2") @@ -116,9 +117,9 @@ class TestDependencies(TestCase): def test_modify_multiple(self): """Check circular dependencies are caught, using 5 tasks""" - self.t("add three") - self.t("add four") - self.t("add five") + self.t("add three") + self.t("add four") + self.t("add five") code, out, err = self.t("1 modify dep:2,3,4") self.assertIn("Modified 1 task.", out) @@ -235,18 +236,18 @@ class TestBug697(TestCase): def test_blocking_to_recurring(self): """697: Verify that making a blocking task into a recurring task breaks dependencies - Bug 697: Making a blocking task recur breaks dependency. - 1. Create 2 tasks: "foo" and "bar". - 2. Give "bar" a due date. - 3. Make "foo" depend on "bar". - 4. Make "bar" recur yearly. + Bug 697: Making a blocking task recur breaks dependency. + 1. Create 2 tasks: "foo" and "bar". + 2. Give "bar" a due date. + 3. Make "foo" depend on "bar". + 4. Make "bar" recur yearly. """ self.t("add one") self.t("add two") self.t("2 modify due:eom") self.t("1 modify depends:2") self.t("2 modify recur:yearly") - self.t("list") # GC/handleRecurrence + self.t("list") # GC/handleRecurrence # The problem is that although 1 --> 2, 2 is now a recurring parent, and as 1 # depends on the parent UUID, it is not something transferred to the child on @@ -332,21 +333,21 @@ class TestFeature725(TestCase): class Test1481(TestCase): def setUp(self): self.t = Task() - self.t('add parent') - self.t('add child') - self.t('add child2') - self.child1_uuid = self.t.export_one(2)['uuid'] - self.child2_uuid = self.t.export_one(3)['uuid'] + self.t("add parent") + self.t("add child") + self.t("add child2") + self.child1_uuid = self.t.export_one(2)["uuid"] + self.child2_uuid = self.t.export_one(3)["uuid"] def test_set_dependency_on_first_completed_task(self): """1481: Sets dependency on task which has been just completed.""" - self.t('2 done') + self.t("2 done") # Trigger the GC to clear up IDs - self.t('next') + self.t("next") # Set the dependency - self.t('1 modify depends:%s' % self.child1_uuid) + self.t("1 modify depends:%s" % self.child1_uuid) def test_set_dependency_on_second_completed_task(self): """ @@ -354,26 +355,25 @@ class Test1481(TestCase): before most recently completed task. """ - self.t('2 done') - self.t('3 done') + self.t("2 done") + self.t("3 done") # Trigger the GC to clear up IDs - self.t('next') + self.t("next") # Set the dependencies - self.t('1 modify depends:%s' % self.child2_uuid) + self.t("1 modify depends:%s" % self.child2_uuid) def test_set_dependency_on_two_completed_tasks(self): - """ 1481: Sets dependency on two most recent completed tasks. """ - self.t('2 done') - self.t('3 done') + """1481: Sets dependency on two most recent completed tasks.""" + self.t("2 done") + self.t("3 done") # Trigger the GC to clear up IDs - self.t('next') + self.t("next") # Set the dependencies - self.t('1 modify depends:%s,%s' % (self.child1_uuid, - self.child2_uuid)) + self.t("1 modify depends:%s,%s" % (self.child1_uuid, self.child2_uuid)) # TODO - test dependency.confirmation config variable @@ -385,6 +385,7 @@ class Test1481(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/diag.test.py b/test/diag.test.py index 60aab3c39..1c9a824a8 100755 --- a/test/diag.test.py +++ b/test/diag.test.py @@ -29,6 +29,7 @@ import sys import os import platform import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -38,11 +39,11 @@ from basetest import Task, TestCase class TestDiagnostics(TestCase): def setUp(self): self.t = Task() - self.t.config("editor", "edlin") + self.t.config("editor", "edlin") @unittest.skipIf( - getattr(platform, 'dist', None) == None or 'xenial' == platform.dist()[-1], - 'Skipping diagnostics test on Ubuntu 16.04, as it lacks full C++17 support' + getattr(platform, "dist", None) == None or "xenial" == platform.dist()[-1], + "Skipping diagnostics test on Ubuntu 16.04, as it lacks full C++17 support", ) def test_diagnostics(self): """Task diag output, so we can monitor platforms""" @@ -60,6 +61,7 @@ class TestDiagnostics(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/diag_color.test.py b/test/diag_color.test.py index edbf069ee..c82305e78 100755 --- a/test/diag_color.test.py +++ b/test/diag_color.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -69,6 +70,7 @@ class TestDiagColor(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/dom.test.cpp b/test/dom.test.cpp index 34a815476..2a031e835 100644 --- a/test/dom.test.cpp +++ b/test/dom.test.cpp @@ -27,31 +27,23 @@ #include // cmake.h include header must come first -#include #include #include +#include //////////////////////////////////////////////////////////////////////////////// -bool providerString (const std::string& path, Variant& var) -{ - if (path == "name") - { - var = Variant ("value"); +bool providerString(const std::string& path, Variant& var) { + if (path == "name") { + var = Variant("value"); return true; - } - else if (path == "name.next") - { - var = Variant ("value.next"); + } else if (path == "name.next") { + var = Variant("value.next"); return true; - } - else if (path == "foo") - { - var = Variant ("bar"); + } else if (path == "foo") { + var = Variant("bar"); return true; - } - else if (path == "name.size") - { - var = Variant (6); + } else if (path == "name.size") { + var = Variant(6); return true; } @@ -59,40 +51,39 @@ bool providerString (const std::string& path, Variant& var) } //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (12); +int main(int, char**) { + UnitTest t(12); DOM dom; - t.is (dom.count (), 0, "DOM empty count is zero"); + t.is(dom.count(), 0, "DOM empty count is zero"); - dom.addSource ("name", &providerString); - dom.addSource ("name.next", &providerString); - dom.addSource ("name.size", &providerString); - dom.addSource ("foo", &providerString); - t.diag (dom.dump ()); - t.is (dom.count (), 4, "DOM now contains 4 nodes"); + dom.addSource("name", &providerString); + dom.addSource("name.next", &providerString); + dom.addSource("name.size", &providerString); + dom.addSource("foo", &providerString); + t.diag(dom.dump()); + t.is(dom.count(), 4, "DOM now contains 4 nodes"); - t.ok (dom.valid ("name"), "DOM 'name' valid"); - t.ok (dom.valid ("name.next"), "DOM 'name.next' valid"); - t.ok (dom.valid ("name.size"), "DOM 'name.size' valid"); - t.ok (dom.valid ("foo"), "DOM 'foo' valid"); - t.notok (dom.valid ("missing"), "DOM 'missing' not valid"); + t.ok(dom.valid("name"), "DOM 'name' valid"); + t.ok(dom.valid("name.next"), "DOM 'name.next' valid"); + t.ok(dom.valid("name.size"), "DOM 'name.size' valid"); + t.ok(dom.valid("foo"), "DOM 'foo' valid"); + t.notok(dom.valid("missing"), "DOM 'missing' not valid"); - auto v = dom.get ("name"); - t.is (v.get_string (), "value", "DOM get 'name' --> 'value'"); + auto v = dom.get("name"); + t.is(v.get_string(), "value", "DOM get 'name' --> 'value'"); - v = dom.get ("name.next"); - t.is (v.get_string (), "value.next", "DOM get 'name.next' --> 'value.next'"); + v = dom.get("name.next"); + t.is(v.get_string(), "value.next", "DOM get 'name.next' --> 'value.next'"); - v = dom.get ("name.size"); - t.is (v.get_integer (), 6, "DOM get 'name.size' --> 6"); + v = dom.get("name.size"); + t.is(v.get_integer(), 6, "DOM get 'name.size' --> 6"); - v = dom.get ("foo"); - t.is (v.get_string (), "bar", "DOM get 'name.size' --> 6"); + v = dom.get("foo"); + t.is(v.get_string(), "bar", "DOM get 'name.size' --> 6"); - v = dom.get ("missing"); - t.is (v.get_string (), "", "DOM get 'missing' --> ''"); + v = dom.get("missing"); + t.is(v.get_string(), "", "DOM get 'missing' --> ''"); return 0; } diff --git a/test/dom2.test.py b/test/dom2.test.py index 85025c0f0..0cd2403c0 100755 --- a/test/dom2.test.py +++ b/test/dom2.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -52,199 +53,201 @@ class TestDOM(TestCase): cls.t("3 annotate note") # Add task containing UDA attributes - cls.t("add ticket task " - "ticketdate:2015-09-04 " - "ticketest:hour " - "ticketnote:comment " - "ticketnum:47") + cls.t( + "add ticket task " + "ticketdate:2015-09-04 " + "ticketest:hour " + "ticketnote:comment " + "ticketnum:47" + ) def test_dom_no_ref(self): - """ DOM missing reference """ + """DOM missing reference""" code, out, err = self.t.runError("_get") self.assertEqual("No DOM reference specified.\n", err) def test_dom_bad_ref(self): - """ DOM bad reference """ + """DOM bad reference""" code, out, err = self.t.runError("_get donkey") self.assertEqual("'donkey' is not a DOM reference.\n", err) def test_dom_task_ref(self): - """ DOM reference to other task """ + """DOM reference to other task""" code, out, err = self.t("_get 2.due") self.assertIn("2011-09-01T00:00:00", out) def test_dom_cli_ref(self): - """ DOM reference to current command line """ + """DOM reference to current command line""" code, out, err = self.t("_get 3.wait") self.assertIn("2011-09-01T00:00:00", out) def test_dom_id_uuid_roundtrip(self): - """ DOM id/uuid roundtrip """ + """DOM id/uuid roundtrip""" code, out, err = self.t("_get 1.uuid") uuid = out.strip() code, out, err = self.t("_get {0}.id".format(uuid)) self.assertEqual("1\n", out) def test_dom_fail(self): - """ DOM lookup of missing item """ + """DOM lookup of missing item""" code, out, err = self.t("_get 5.description") self.assertEqual("\n", out) def test_dom_tags(self): - """ DOM 3.tags """ + """DOM 3.tags""" code, out, err = self.t("_get 3.tags") self.assertEqual("tag1,tag2\n", out) def test_dom_tags_tag1(self): - """ DOM 3.tags.tag1 """ + """DOM 3.tags.tag1""" code, out, err = self.t("_get 3.tags.tag1") self.assertEqual("tag1\n", out) def test_dom_tags_OVERDUE(self): - """ DOM 3.tags.OVERDUE """ + """DOM 3.tags.OVERDUE""" code, out, err = self.t("_get 3.tags.OVERDUE") self.assertEqual("OVERDUE\n", out) def test_dom_due_year(self): - """ DOM 3.due.year """ + """DOM 3.due.year""" code, out, err = self.t("_get 3.due.year") self.assertEqual("2011\n", out) def test_dom_due_month(self): - """ DOM 3.due.month """ + """DOM 3.due.month""" code, out, err = self.t("_get 3.due.month") self.assertEqual("9\n", out) def test_dom_due_day(self): - """ DOM 3.due.day """ + """DOM 3.due.day""" code, out, err = self.t("_get 3.due.day") self.assertEqual("1\n", out) def test_dom_due_week(self): - """ DOM 3.due.week """ + """DOM 3.due.week""" code, out, err = self.t("_get 3.due.week") self.assertEqual("35\n", out) def test_dom_due_weekday(self): - """ DOM 3.due.weekday """ + """DOM 3.due.weekday""" code, out, err = self.t("_get 3.due.weekday") self.assertEqual("4\n", out) def test_dom_due_hour(self): - """ DOM 3.due.hour """ + """DOM 3.due.hour""" code, out, err = self.t("_get 3.due.hour") self.assertEqual("0\n", out) def test_dom_due_minute(self): - """ DOM 3.due.minute """ + """DOM 3.due.minute""" code, out, err = self.t("_get 3.due.minute") self.assertEqual("0\n", out) def test_dom_due_second(self): - """ DOM 3.due.second """ + """DOM 3.due.second""" code, out, err = self.t("_get 3.due.second") self.assertEqual("0\n", out) def test_dom_annotation_count_1(self): - """ DOM 1.annotation.count """ + """DOM 1.annotation.count""" code, out, err = self.t("_get 1.annotations.count") self.assertEqual("0\n", out) def test_dom_annotation_count_3(self): - """ DOM 3.annotation.count """ + """DOM 3.annotation.count""" code, out, err = self.t("_get 3.annotations.count") self.assertEqual("1\n", out) def test_dom_annotation_entry(self): - """ DOM 3.annotations.1.entry """ + """DOM 3.annotations.1.entry""" code, out, err = self.t("_get 3.annotations.1.entry") self.assertRegex(out, r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}") def test_dom_annotation_entry_second(self): - """ DOM 3.annotations.1.entry """ + """DOM 3.annotations.1.entry""" code, out, err = self.t("_get 3.annotations.1.entry.second") self.assertRegex(out, r"\d{1,2}") def test_dom_annotation_description(self): - """ DOM 3.annotations.1.description """ + """DOM 3.annotations.1.description""" code, out, err = self.t("_get 3.annotations.1.description") self.assertIn("note\n", out) def test_dom_system_version(self): - """ DOM system.version """ + """DOM system.version""" code, out, err = self.t("_get system.version") self.assertEqual(code, 0) self.assertRegex(out, r"\d\.\d+\.\d+") def test_dom_system_os(self): - """ DOM system.os """ + """DOM system.os""" code, out, err = self.t("_get system.os") self.assertEqual(code, 0) self.assertEqual(len(out) > 4, True) self.assertNotIn("", out) def test_dom_tw_program(self): - """ DOM tw.program """ + """DOM tw.program""" code, out, err = self.t("_get tw.program") self.assertEqual(code, 0) self.assertIn("task", out) def test_dom_tw_args(self): - """ DOM tw.args """ + """DOM tw.args""" code, out, err = self.t("_get tw.args") self.assertEqual(code, 0) self.assertIn("task _get tw.args", out) def test_dom_tw_width(self): - """ DOM tw.width """ + """DOM tw.width""" code, out, err = self.t("_get tw.width") self.assertEqual(code, 0) self.assertRegex(out, r"\d+") def test_dom_tw_height(self): - """ DOM tw.height """ + """DOM tw.height""" code, out, err = self.t("_get tw.height") self.assertEqual(code, 0) self.assertRegex(out, r"\d+") def test_dom_tw_version(self): - """ DOM tw.version """ + """DOM tw.version""" code, out, err = self.t("_get tw.version") self.assertEqual(code, 0) self.assertRegex(out, r"\d\.\d+\.\d+") def test_dom_context_program(self): - """ DOM context.program """ + """DOM context.program""" code, out, err = self.t("_get context.program") self.assertEqual(code, 0) self.assertIn("task", out) def test_dom_context_args(self): - """ DOM context.args """ + """DOM context.args""" code, out, err = self.t("_get context.args") self.assertEqual(code, 0) self.assertIn("task _get context.args", out) def test_dom_context_width(self): - """ DOM context.width """ + """DOM context.width""" code, out, err = self.t("_get context.width") self.assertEqual(code, 0) self.assertRegex(out, r"\d+") def test_dom_context_height(self): - """ DOM context.height """ + """DOM context.height""" code, out, err = self.t("_get context.height") self.assertEqual(code, 0) self.assertRegex(out, r"\d+") def test_dom_rc_name(self): - """ DOM rc.dateformat """ + """DOM rc.dateformat""" code, out, err = self.t("_get rc.dateformat") self.assertEqual(code, 0) self.assertIn("YMD", out) def test_dom_rc_missing(self): - """ DOM rc.missing """ + """DOM rc.missing""" code, out, err = self.t("_get rc.missing") self.assertEqual("\n", out) @@ -274,10 +277,11 @@ class TestDOM(TestCase): self.assertIn("2015-09-04T00:00:00", out) def test_dom_uda_date_year(self): - """ DOM 3.due.year """ + """DOM 3.due.year""" code, out, err = self.t("_get 4.ticketdate.year") self.assertEqual("2015\n", out) + class TestDOMSync(TestCase): """ This class verifies that the 'tw.syncneeded' DOM reference properly @@ -288,14 +292,14 @@ class TestDOMSync(TestCase): self.t = Task() def test_dom_tw_syncneeded_false(self): - """ DOM tw.syncneeded --> false """ + """DOM tw.syncneeded --> false""" code, out, err = self.t("_get tw.syncneeded") self.assertEqual(code, 0) self.assertIn("0", out) self.assertNotIn("1k", out) def test_dom_tw_syncneeded_true(self): - """ DOM tw.syncneeded --> true """ + """DOM tw.syncneeded --> true""" self.t("add foo") code, out, err = self.t("_get tw.syncneeded") self.assertEqual(code, 0) @@ -339,83 +343,83 @@ class TestDOMDirectReferencesOnAddition(TestCase): cls.t("1 annotate Second annotation") def test_dom_reference_due(self): - """ DOM reference on due in add command """ + """DOM reference on due in add command""" self.t("add test_due due:1.due") latest = self.t.latest - self.assertEqual("test_due", latest['description']) - self.assertEqual("20150901T080000Z", latest['due']) + self.assertEqual("test_due", latest["description"]) + self.assertEqual("20150901T080000Z", latest["due"]) def test_dom_reference_project(self): - """ DOM reference on project in add command """ + """DOM reference on project in add command""" self.t("add test_project project:1.project") latest = self.t.latest - self.assertEqual("test_project", latest['description']) - self.assertEqual("baseproject", latest['project']) + self.assertEqual("test_project", latest["description"]) + self.assertEqual("baseproject", latest["project"]) def test_dom_reference_tags_all(self): - """ DOM reference on tags in add command """ + """DOM reference on tags in add command""" self.t("add test_tags_all tags:1.tags") latest = self.t.latest - self.assertEqual("test_tags_all", latest['description']) - self.assertEqual(["tag1","tag2"], latest['tags']) + self.assertEqual("test_tags_all", latest["description"]) + self.assertEqual(["tag1", "tag2"], latest["tags"]) def test_dom_reference_tags_single(self): - """ DOM reference on specific tag in add command """ + """DOM reference on specific tag in add command""" self.t("add test_tags_single tags:1.tags.tag1") latest = self.t.latest - self.assertEqual("test_tags_single", latest['description']) - self.assertEqual(["tag1"], latest['tags']) + self.assertEqual("test_tags_single", latest["description"]) + self.assertEqual(["tag1"], latest["tags"]) def test_dom_reference_annotation(self): - """ DOM reference on annotation description in add command """ + """DOM reference on annotation description in add command""" self.t("add description:1.annotations.2.description") latest = self.t.latest - self.assertEqual("Second annotation", latest['description']) + self.assertEqual("Second annotation", latest["description"]) def test_dom_reference_numeric_uda(self): - """ DOM reference on numeric UDA in add command """ + """DOM reference on numeric UDA in add command""" self.t("add test_numeric_uda ticketnum:1.ticketnum") latest = self.t.latest - self.assertEqual("test_numeric_uda", latest['description']) - self.assertEqual(42, latest['ticketnum']) + self.assertEqual("test_numeric_uda", latest["description"]) + self.assertEqual(42, latest["ticketnum"]) def test_dom_reference_date_uda(self): - """ DOM reference on date UDA in add command """ + """DOM reference on date UDA in add command""" self.t("add test_date_uda ticketdate:1.ticketdate") latest = self.t.latest - self.assertEqual("test_date_uda", latest['description']) - self.assertEqual("20150903T080000Z", latest['ticketdate']) + self.assertEqual("test_date_uda", latest["description"]) + self.assertEqual("20150903T080000Z", latest["ticketdate"]) def test_dom_reference_string_uda(self): - """ DOM reference on string UDA in add command """ + """DOM reference on string UDA in add command""" self.t("add test_string_uda ticketnote:1.ticketnote") latest = self.t.latest - self.assertEqual("test_string_uda", latest['description']) - self.assertEqual("This is awesome", latest['ticketnote']) + self.assertEqual("test_string_uda", latest["description"]) + self.assertEqual("This is awesome", latest["ticketnote"]) def test_dom_reference_string_value_uda(self): - """ DOM reference on string with limited values UDA in add command """ + """DOM reference on string with limited values UDA in add command""" self.t("add test_string_value_uda ticketflag:1.ticketflag") latest = self.t.latest - self.assertEqual("test_string_value_uda", latest['description']) - self.assertEqual("B", latest['ticketflag']) + self.assertEqual("test_string_value_uda", latest["description"]) + self.assertEqual("B", latest["ticketflag"]) def test_dom_reference_duration_uda(self): - """ DOM reference on duration UDA in add command """ + """DOM reference on duration UDA in add command""" self.t("add test_duration_uda ticketest:1.ticketest") latest = self.t.latest - self.assertEqual("test_duration_uda", latest['description']) - self.assertEqual("PT1H", latest['ticketest']) + self.assertEqual("test_duration_uda", latest["description"]) + self.assertEqual("PT1H", latest["ticketest"]) class TestDOMDirectReferencesFiltering(TestCase): @@ -455,44 +459,44 @@ class TestDOMDirectReferencesFiltering(TestCase): cls.t("add non matching task") def test_dom_filter_reference_due(self): - """ DOM reference on due in filter """ + """DOM reference on due in filter""" result = self.t.export_one("due:1.due") - self.assertEqual("matching task", result['description']) + self.assertEqual("matching task", result["description"]) def test_dom_filter_reference_project(self): - """ DOM reference on project in filter """ + """DOM reference on project in filter""" result = self.t.export_one("project:1.project") - self.assertEqual("matching task", result['description']) + self.assertEqual("matching task", result["description"]) def test_dom_filter_reference_tags_all(self): - """ DOM reference on tags in filter """ + """DOM reference on tags in filter""" result = self.t.export_one("tags:1.tags") - self.assertEqual("matching task", result['description']) + self.assertEqual("matching task", result["description"]) def test_dom_filter_reference_numeric_uda(self): - """ DOM reference on numeric UDA in filter """ + """DOM reference on numeric UDA in filter""" result = self.t.export_one("ticketnum:1.ticketnum") - self.assertEqual("matching task", result['description']) + self.assertEqual("matching task", result["description"]) def test_dom_filter_reference_date_uda(self): - """ DOM reference on date UDA in filter """ + """DOM reference on date UDA in filter""" result = self.t.export_one("ticketdate:1.ticketdate") - self.assertEqual("matching task", result['description']) + self.assertEqual("matching task", result["description"]) def test_dom_filter_reference_string_uda(self): - """ DOM reference on string UDA in filter """ + """DOM reference on string UDA in filter""" result = self.t.export_one("ticketnote:1.ticketnote") - self.assertEqual("matching task", result['description']) + self.assertEqual("matching task", result["description"]) def test_dom_filter_reference_string_value_uda(self): - """ DOM reference on string with limited values UDA in filter """ + """DOM reference on string with limited values UDA in filter""" result = self.t.export_one("ticketflag:1.ticketflag") - self.assertEqual("matching task", result['description']) + self.assertEqual("matching task", result["description"]) def test_dom_filter_reference_duration_uda(self): - """ DOM reference on duration UDA in filter """ + """DOM reference on duration UDA in filter""" result = self.t.export_one("ticketest:1.ticketest") - self.assertEqual("matching task", result['description']) + self.assertEqual("matching task", result["description"]) class TestBug1300(TestCase): @@ -501,18 +505,17 @@ class TestBug1300(TestCase): cls.t = Task() def test_dom_exit_status_good(self): - """1300: If the DOM recognizes a reference, it should return '0' - """ + """1300: If the DOM recognizes a reference, it should return '0'""" self.t("_get context.program") def test_dom_exit_status_bad(self): - """1300: If the DOM does not recognize a reference, it should return '1' - """ + """1300: If the DOM does not recognize a reference, it should return '1'""" self.t.runError("_get XYZ") if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/due.test.py b/test/due.test.py index 80943dfc4..c78f4688b 100755 --- a/test/due.test.py +++ b/test/due.test.py @@ -29,6 +29,7 @@ import sys import os import unittest from datetime import datetime, timedelta + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -138,6 +139,7 @@ class TestBug2519(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/duplicate.test.py b/test/duplicate.test.py index 86c66695f..d1bef3049 100755 --- a/test/duplicate.test.py +++ b/test/duplicate.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -64,7 +65,10 @@ class TestDuplication(TestCase): def test_duplication_showing_uuid(self): """Verify duplicate can show uuid""" code, out, err = self.t("1 duplicate rc.verbose:new-uuid") - self.assertRegex(out, "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") + self.assertRegex( + out, + "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}", + ) class TestDuplication2(TestCase): @@ -75,7 +79,7 @@ class TestDuplication2(TestCase): def test_duplication_recurrence(self): """Verify that recurring tasks are properly duplicated""" self.t("add R due:tomorrow recur:weekly") - self.t("list") # To force handleRecurrence(). + self.t("list") # To force handleRecurrence(). code, out, err = self.t("1 duplicate") self.assertIn("The duplicated task is too", out) @@ -83,7 +87,7 @@ class TestDuplication2(TestCase): code, out, err = self.t("2 duplicate") self.assertIn("The duplicated task is not", out) - self.t("list") # To force handleRecurrence(). + self.t("list") # To force handleRecurrence(). code, out, err = self.t("1 export") self.assertIn('"status":"recurring"', out) @@ -105,6 +109,7 @@ class TestDuplication2(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/edit.test.py b/test/edit.test.py index 92378378d..57891f732 100755 --- a/test/edit.test.py +++ b/test/edit.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -86,7 +87,9 @@ class TestTaskEdit(TestCase): self.t.config("uda.uorphan.type", "string") self.t.config("uda.uorphan.label", "uorphan") - self.t("add foo project:P +tag priority:H start:now due:eom wait:eom scheduled:eom recur:P1M until:eoy udate:now uduration:1day ustring:Hi unumeric:42 uorphan:Annie") + self.t( + "add foo project:P +tag priority:H start:now due:eom wait:eom scheduled:eom recur:P1M until:eoy udate:now uduration:1day ustring:Hi unumeric:42 uorphan:Annie" + ) self.t("1 annotate bar", input="n\n") # Make the orphan. @@ -99,6 +102,7 @@ class TestTaskEdit(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/encoding.test.py b/test/encoding.test.py index b00632a07..13b4973c6 100755 --- a/test/encoding.test.py +++ b/test/encoding.test.py @@ -29,6 +29,7 @@ import sys import os import re import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -77,6 +78,7 @@ class TestUtf8(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/enpassant.test.py b/test/enpassant.test.py index 3f28d76a8..b68c0639a 100755 --- a/test/enpassant.test.py +++ b/test/enpassant.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -54,19 +55,23 @@ class TestEnpassantMultiple(BaseTestEnpassant): code, out, err = self.t((id, "info")) self.assertRegex( - out, "Status +Completed", + out, + "Status +Completed", msg="enpassant {0} status change".format(id), ) self.assertRegex( - out, "Priority +H", + out, + "Priority +H", msg="enpassant {0} priority change".format(id), ) self.assertRegex( - out, "Tags +tag", + out, + "Tags +tag", msg="enpassant {0} tag change".format(id), ) self.assertRegex( - out, "Description +{0}".format(desc), + out, + "Description +{0}".format(desc), msg="enpassant {0} description change".format(id), ) @@ -94,28 +99,33 @@ class TestEnpassant(BaseTestEnpassant): def perform_action(self, action): self.t(("1", action, "oneanno")) code, out, err = self.t("1 info") - self.assertRegex(out, "Description +one\n[0-9: -]+ oneanno", - msg="{0} enpassant annotation".format(action)) + self.assertRegex( + out, + "Description +one\n[0-9: -]+ oneanno", + msg="{0} enpassant annotation".format(action), + ) self.t(("2", action, "/two/TWO/")) code, out, err = self.t("2 info") - self.assertRegex(out, "Description +TWO", - msg="{0} enpassant modify".format(action)) + self.assertRegex( + out, "Description +TWO", msg="{0} enpassant modify".format(action) + ) self.t(("3", action, "+threetag")) code, out, err = self.t("3 info") - self.assertRegex(out, "Tags +threetag", - msg="{0} enpassant tag".format(action)) + self.assertRegex(out, "Tags +threetag", msg="{0} enpassant tag".format(action)) self.t(("4", action, "pri:H")) code, out, err = self.t("4 info") - self.assertRegex(out, "Priority +H", - msg="{0} enpassant priority".format(action)) + self.assertRegex( + out, "Priority +H", msg="{0} enpassant priority".format(action) + ) self.t(("5", action, "pro:PROJ")) code, out, err = self.t("5 info") - self.assertRegex(out, "Project +PROJ", - msg="{0} enpassant project".format(action)) + self.assertRegex( + out, "Project +PROJ", msg="{0} enpassant project".format(action) + ) def test_done(self): """Test 'done' with en-passant changes""" @@ -138,6 +148,7 @@ class TestEnpassant(BaseTestEnpassant): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/eval.test.cpp b/test/eval.test.cpp index f5969bdb5..3875da5ac 100644 --- a/test/eval.test.cpp +++ b/test/eval.test.cpp @@ -27,16 +27,15 @@ #include // cmake.h include header must come first -#include #include #include +#include //////////////////////////////////////////////////////////////////////////////// // A few hard-coded symbols. -bool get (const std::string& name, Variant& value) -{ +bool get(const std::string& name, Variant& value) { if (name == "x") - value = Variant (true); + value = Variant(true); else return false; @@ -44,126 +43,125 @@ bool get (const std::string& name, Variant& value) } //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (52); +int main(int, char**) { + UnitTest t(52); Context context; Context::setContext(&context); // Test the source independently. Variant v; - t.notok (get ("-", v), "true <-- get(-)"); + t.notok(get("-", v), "true <-- get(-)"); - t.ok (get ("x", v), "true <-- get(x)"); - t.is (v.type (), Variant::type_boolean, "get(x) --> boolean"); - t.is (v.get_bool (), true, "get(x) --> true"); + t.ok(get("x", v), "true <-- get(x)"); + t.is(v.type(), Variant::type_boolean, "get(x) --> boolean"); + t.is(v.get_bool(), true, "get(x) --> true"); Eval e; - e.addSource (get); + e.addSource(get); Variant result; - e.evaluatePostfixExpression ("x", result); - t.is (result.type (), Variant::type_boolean, "postfix 'x' --> boolean"); - t.is (result.get_bool (), true, "postfix 'x' --> true"); + e.evaluatePostfixExpression("x", result); + t.is(result.type(), Variant::type_boolean, "postfix 'x' --> boolean"); + t.is(result.get_bool(), true, "postfix 'x' --> true"); - e.evaluatePostfixExpression ("pi", result); - t.is (result.type (), Variant::type_real, "postfix 'pi' --> real"); - t.is (result.get_real (), 3.141592, 0.00001, "postfix 'pi' --> 3.14159265"); + e.evaluatePostfixExpression("pi", result); + t.is(result.type(), Variant::type_real, "postfix 'pi' --> real"); + t.is(result.get_real(), 3.141592, 0.00001, "postfix 'pi' --> 3.14159265"); - e.evaluatePostfixExpression ("foo", result); - t.is (result.type (), Variant::type_string, "postfix 'foo' --> string"); - t.is (result.get_string (), "foo", "postfix 'foo' --> 'foo'"); + e.evaluatePostfixExpression("foo", result); + t.is(result.type(), Variant::type_string, "postfix 'foo' --> string"); + t.is(result.get_string(), "foo", "postfix 'foo' --> 'foo'"); // Simple infix arithmetic. - e.evaluateInfixExpression ("1+2", result); - t.is (result.type (), Variant::type_integer, "infix '1 + 2' --> integer"); - t.is (result.get_integer (), 3, "infix '1 + 2' --> 3"); + e.evaluateInfixExpression("1+2", result); + t.is(result.type(), Variant::type_integer, "infix '1 + 2' --> integer"); + t.is(result.get_integer(), 3, "infix '1 + 2' --> 3"); // Simple postfix arithmetic. - e.evaluatePostfixExpression ("1 2 +", result); - t.is (result.type (), Variant::type_integer, "postfix '1 2 +' --> integer"); - t.is (result.get_integer (), 3, "postfix '1 2 +' --> 3"); + e.evaluatePostfixExpression("1 2 +", result); + t.is(result.type(), Variant::type_integer, "postfix '1 2 +' --> integer"); + t.is(result.get_integer(), 3, "postfix '1 2 +' --> 3"); - e.evaluatePostfixExpression ("1 2 -", result); - t.is (result.type (), Variant::type_integer, "postfix '1 2 -' --> integer"); - t.is (result.get_integer (), -1, "postfix '1 2 -' --> -1"); + e.evaluatePostfixExpression("1 2 -", result); + t.is(result.type(), Variant::type_integer, "postfix '1 2 -' --> integer"); + t.is(result.get_integer(), -1, "postfix '1 2 -' --> -1"); - e.evaluatePostfixExpression ("2 3 *", result); - t.is (result.type (), Variant::type_integer, "postfix '2 3 *' --> integer"); - t.is (result.get_integer (), 6, "postfix '2 3 *' --> 6"); + e.evaluatePostfixExpression("2 3 *", result); + t.is(result.type(), Variant::type_integer, "postfix '2 3 *' --> integer"); + t.is(result.get_integer(), 6, "postfix '2 3 *' --> 6"); - e.evaluatePostfixExpression ("5 2 /", result); - t.is (result.type (), Variant::type_integer, "postfix '5 2 /' --> integer"); - t.is (result.get_integer (), 2, "postfix '5 2 /' --> 2"); + e.evaluatePostfixExpression("5 2 /", result); + t.is(result.type(), Variant::type_integer, "postfix '5 2 /' --> integer"); + t.is(result.get_integer(), 2, "postfix '5 2 /' --> 2"); - e.evaluatePostfixExpression ("5 2 /", result); - t.is (result.type (), Variant::type_integer, "postfix '5 2 *' --> integer"); - t.is (result.get_integer (), 2, "postfix '5 2 *' --> 2"); + e.evaluatePostfixExpression("5 2 /", result); + t.is(result.type(), Variant::type_integer, "postfix '5 2 *' --> integer"); + t.is(result.get_integer(), 2, "postfix '5 2 *' --> 2"); // Simple postfix unary operator. - e.evaluatePostfixExpression ("0 !", result); - t.is (result.type (), Variant::type_boolean, "postfix '0 !' --> boolean"); - t.is (result.get_bool (), true, "postfix '0 !' --> true"); + e.evaluatePostfixExpression("0 !", result); + t.is(result.type(), Variant::type_boolean, "postfix '0 !' --> boolean"); + t.is(result.get_bool(), true, "postfix '0 !' --> true"); - e.evaluatePostfixExpression ("1 !", result); - t.is (result.type (), Variant::type_boolean, "postfix '1 !' --> boolean"); - t.is (result.get_bool (), false, "postfix '1 !' --> false"); + e.evaluatePostfixExpression("1 !", result); + t.is(result.type(), Variant::type_boolean, "postfix '1 !' --> boolean"); + t.is(result.get_bool(), false, "postfix '1 !' --> false"); // Type promotion simple postfix arithmetic. - e.evaluatePostfixExpression ("1 2.3 +", result); - t.is (result.type (), Variant::type_real, "postfix '1 2.3 +' --> real"); - t.is (result.get_real (), 3.3, "postfix '1 2.3 +' --> 3.3"); + e.evaluatePostfixExpression("1 2.3 +", result); + t.is(result.type(), Variant::type_real, "postfix '1 2.3 +' --> real"); + t.is(result.get_real(), 3.3, "postfix '1 2.3 +' --> 3.3"); - e.evaluatePostfixExpression ("5 2.0 /", result); - t.is (result.type (), Variant::type_real, "postfix '5 2.0 /' --> integer"); - t.is (result.get_real (), 2.5, "postfix '5 2.0 /' --> 2.5"); + e.evaluatePostfixExpression("5 2.0 /", result); + t.is(result.type(), Variant::type_real, "postfix '5 2.0 /' --> integer"); + t.is(result.get_real(), 2.5, "postfix '5 2.0 /' --> 2.5"); // Simple logic. - e.evaluatePostfixExpression ("0 0 ||", result); - t.is (result.type (), Variant::type_boolean, "postfix '0 0 ||' --> boolean"); - t.is (result.get_bool (), false, "postfix '0 0 ||' --> false"); + e.evaluatePostfixExpression("0 0 ||", result); + t.is(result.type(), Variant::type_boolean, "postfix '0 0 ||' --> boolean"); + t.is(result.get_bool(), false, "postfix '0 0 ||' --> false"); - e.evaluatePostfixExpression ("0 1 ||", result); - t.is (result.type (), Variant::type_boolean, "postfix '0 1 ||' --> boolean"); - t.is (result.get_bool (), true, "postfix '0 1 ||' --> true"); + e.evaluatePostfixExpression("0 1 ||", result); + t.is(result.type(), Variant::type_boolean, "postfix '0 1 ||' --> boolean"); + t.is(result.get_bool(), true, "postfix '0 1 ||' --> true"); - e.evaluatePostfixExpression ("1 0 ||", result); - t.is (result.type (), Variant::type_boolean, "postfix '1 0 ||' --> boolean"); - t.is (result.get_bool (), true, "postfix '1 0 ||' --> true"); + e.evaluatePostfixExpression("1 0 ||", result); + t.is(result.type(), Variant::type_boolean, "postfix '1 0 ||' --> boolean"); + t.is(result.get_bool(), true, "postfix '1 0 ||' --> true"); - e.evaluatePostfixExpression ("1 1 ||", result); - t.is (result.type (), Variant::type_boolean, "postfix '1 1 ||' --> boolean"); - t.is (result.get_bool (), true, "postfix '1 1 ||' --> true"); + e.evaluatePostfixExpression("1 1 ||", result); + t.is(result.type(), Variant::type_boolean, "postfix '1 1 ||' --> boolean"); + t.is(result.get_bool(), true, "postfix '1 1 ||' --> true"); - e.evaluateInfixExpression ("2*3+1", result); - t.is (result.type (), Variant::type_integer, "infix '2*3+1' --> integer"); - t.is (result.get_integer (), 7, "infix '2*3+1' --> 7"); + e.evaluateInfixExpression("2*3+1", result); + t.is(result.type(), Variant::type_integer, "infix '2*3+1' --> integer"); + t.is(result.get_integer(), 7, "infix '2*3+1' --> 7"); // TW-1254 - Unary minus support. - e.evaluateInfixExpression ("2- -3", result); - t.is (result.type (), Variant::type_integer, "infix '2- -3' --> integer"); - t.is (result.get_integer (), 5, "infix '2- -3' --> 5"); + e.evaluateInfixExpression("2- -3", result); + t.is(result.type(), Variant::type_integer, "infix '2- -3' --> integer"); + t.is(result.get_integer(), 5, "infix '2- -3' --> 5"); - //e.debug (); - e.evaluateInfixExpression ("!false", result); - t.is (result.type (), Variant::type_boolean, "infix '!false' --> boolean"); - t.is (result.get_bool (), true, "infix '!false' --> true"); + // e.debug (); + e.evaluateInfixExpression("!false", result); + t.is(result.type(), Variant::type_boolean, "infix '!false' --> boolean"); + t.is(result.get_bool(), true, "infix '!false' --> true"); - e.evaluateInfixExpression ("!true", result); - t.is (result.type (), Variant::type_boolean, "infix '!true' --> boolean"); - t.is (result.get_bool (), false, "infix '!true' --> false"); + e.evaluateInfixExpression("!true", result); + t.is(result.type(), Variant::type_boolean, "infix '!true' --> boolean"); + t.is(result.get_bool(), false, "infix '!true' --> false"); // _neg_ - e.evaluateInfixExpression ("- 1", result); - t.is (result.type (), Variant::type_integer, "infix '- 1' --> integer"); - t.is (result.get_integer (), -1, "infix '- 1' --> -1"); + e.evaluateInfixExpression("- 1", result); + t.is(result.type(), Variant::type_integer, "infix '- 1' --> integer"); + t.is(result.get_integer(), -1, "infix '- 1' --> -1"); - e.evaluateInfixExpression ("- 1.2", result); - t.is (result.type (), Variant::type_real, "infix '- 1.2' --> real"); - t.is (result.get_real (), -1.2, "infix '- 1.2' --> -1.2"); + e.evaluateInfixExpression("- 1.2", result); + t.is(result.type(), Variant::type_real, "infix '- 1.2' --> real"); + t.is(result.get_real(), -1.2, "infix '- 1.2' --> -1.2"); - e.evaluateInfixExpression ("- 2days", result); - t.is (result.type (), Variant::type_duration, "infix '- 2days' --> duration"); - t.is (result.get_duration (), -86400*2, "infix '- 2days' --> -86400 * 2"); + e.evaluateInfixExpression("- 2days", result); + t.is(result.type(), Variant::type_duration, "infix '- 2days' --> duration"); + t.is(result.get_duration(), -86400 * 2, "infix '- 2days' --> -86400 * 2"); return 0; } diff --git a/test/exec.test.py b/test/exec.test.py index 394e1b025..3cad4e32c 100755 --- a/test/exec.test.py +++ b/test/exec.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase @@ -63,8 +64,10 @@ class TestBug1414(TestCase): code, out, err = self.t() self.assertIn("hello", out) + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/export.test.py b/test/export.test.py index c3637fbb2..777bcf227 100755 --- a/test/export.test.py +++ b/test/export.test.py @@ -45,7 +45,7 @@ DATETIME_FORMAT = "%Y%m%dT%H%M%SZ" class TestExportCommand(TestCase): def setUp(self): self.t = Task() - self.t('add test') + self.t("add test") def export(self, id): code, out, err = self.t(("{0}".format(id), "rc.json.array=off", "export")) @@ -91,62 +91,62 @@ class TestExportCommand(TestCase): self.assertEqual(value, expected_value) def test_export_status(self): - self.assertString(self.export(1)['status'], "pending") + self.assertString(self.export(1)["status"], "pending") def test_export_uuid(self): - self.assertString(self.export(1)['uuid'], UUID_REGEXP, regexp=True) + self.assertString(self.export(1)["uuid"], UUID_REGEXP, regexp=True) def test_export_entry(self): - self.assertTimestamp(self.export(1)['entry']) + self.assertTimestamp(self.export(1)["entry"]) def test_export_description(self): - self.assertString(self.export(1)['description'], "test") + self.assertString(self.export(1)["description"], "test") def test_export_start(self): - self.t('1 start') - self.assertTimestamp(self.export(1)['start']) + self.t("1 start") + self.assertTimestamp(self.export(1)["start"]) def test_export_end(self): - self.t('1 start') + self.t("1 start") self.t.faketime("+5s") # After a task is "done" or "deleted", it does not have an ID by which # to filter it anymore. Add a tag to work around this. - self.t('1 done +workaround') - self.assertTimestamp(self.export('+workaround')['end']) + self.t("1 done +workaround") + self.assertTimestamp(self.export("+workaround")["end"]) def test_export_due(self): - self.t('1 modify due:today') - self.assertTimestamp(self.export(1)['due']) + self.t("1 modify due:today") + self.assertTimestamp(self.export(1)["due"]) def test_export_wait(self): - self.t('1 modify wait:tomorrow') - self.assertTimestamp(self.export(1)['wait']) + self.t("1 modify wait:tomorrow") + self.assertTimestamp(self.export(1)["wait"]) def test_export_modified(self): - self.assertTimestamp(self.export(1)['modified']) + self.assertTimestamp(self.export(1)["modified"]) def test_export_scheduled(self): - self.t('1 modify schedule:tomorrow') - self.assertTimestamp(self.export(1)['scheduled']) + self.t("1 modify schedule:tomorrow") + self.assertTimestamp(self.export(1)["scheduled"]) def test_export_recur(self): - self.t('1 modify recur:daily due:today') - self.assertString(self.export(1)['recur'], "daily") + self.t("1 modify recur:daily due:today") + self.assertString(self.export(1)["recur"], "daily") def test_export_project(self): - self.t('1 modify project:Home') - self.assertString(self.export(1)['project'], "Home") + self.t("1 modify project:Home") + self.assertString(self.export(1)["project"], "Home") def test_export_priority(self): - self.t('1 modify priority:H') - self.assertString(self.export(1)['priority'], "H") + self.t("1 modify priority:H") + self.assertString(self.export(1)["priority"], "H") def test_export_depends(self): - self.t(('add', 'everything depends on me task')) - self.t(('add', 'wrong, everything depends on me task')) - self.t('1 modify depends:2,3') + self.t(("add", "everything depends on me task")) + self.t(("add", "wrong, everything depends on me task")) + self.t("1 modify depends:2,3") - deps = self.export(1)['depends'] + deps = self.export(1)["depends"] self.assertType(deps, list) self.assertEqual(len(deps), 2) @@ -154,30 +154,30 @@ class TestExportCommand(TestCase): self.assertString(uuid, UUID_REGEXP, regexp=True) def test_export_urgency(self): - self.t('add urgent task +urgent') + self.t("add urgent task +urgent") # Urgency can be either integer or float - self.assertNumeric(self.export(1)['urgency']) + self.assertNumeric(self.export(1)["urgency"]) def test_export_numeric_uda(self): - self.t.config('uda.estimate.type', 'numeric') - self.t('add estimate:42 test numeric uda') - self.assertNumeric(self.export('2')['estimate'], 42) + self.t.config("uda.estimate.type", "numeric") + self.t("add estimate:42 test numeric uda") + self.assertNumeric(self.export("2")["estimate"], 42) def test_export_string_uda(self): - self.t.config('uda.estimate.type', 'string') - self.t('add estimate:big test string uda') - self.assertString(self.export('2')['estimate'], 'big') + self.t.config("uda.estimate.type", "string") + self.t("add estimate:big test string uda") + self.assertString(self.export("2")["estimate"], "big") def test_export_datetime_uda(self): - self.t.config('uda.estimate.type', 'date') - self.t('add estimate:eom test date uda') - self.assertTimestamp(self.export('2')['estimate']) + self.t.config("uda.estimate.type", "date") + self.t("add estimate:eom test date uda") + self.assertTimestamp(self.export("2")["estimate"]) def test_export_duration_uda(self): - self.t.config('uda.estimate.type', 'duration') - self.t('add estimate:month test duration uda') - self.assertString(self.export('2')['estimate'], 'P30D') + self.t.config("uda.estimate.type", "duration") + self.t("add estimate:month test duration uda") + self.assertString(self.export("2")["estimate"], "P30D") class TestExportCommandLimit(TestCase): @@ -186,8 +186,8 @@ class TestExportCommandLimit(TestCase): def test_export_obeys_limit(self): """Verify that 'task export limit:1' is obeyed""" - self.t('add one') - self.t('add two') + self.t("add one") + self.t("add two") code, out, err = self.t("/o/ limit:1 export") self.assertIn("one", out) @@ -196,6 +196,7 @@ class TestExportCommandLimit(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/feature.559.test.py b/test/feature.559.test.py index 42b1bec25..c0c5afa8f 100755 --- a/test/feature.559.test.py +++ b/test/feature.559.test.py @@ -29,6 +29,7 @@ import sys import os import re import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -67,11 +68,14 @@ class TestFeature559(TestCase): code, out, err = self.t.runError("rc.data.location=locationdoesnotexist list") self.assertNotIn("footask", out) self.assertNotIn("Error", out) - self.assertRegex(err, re.compile("Could not.+unable to open database file", re.DOTALL)) + self.assertRegex( + err, re.compile("Could not.+unable to open database file", re.DOTALL) + ) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/feature.default.project.test.py b/test/feature.default.project.test.py index dc2cce7fc..558b081fe 100755 --- a/test/feature.default.project.test.py +++ b/test/feature.default.project.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -35,8 +36,8 @@ from basetest import Task, TestCase class TestDefaultProject(TestCase): - """Bug 1023: rc.default.project gets applied during modify, and should not - """ + """Bug 1023: rc.default.project gets applied during modify, and should not""" + def setUp(self): self.t = Task() @@ -45,8 +46,7 @@ class TestDefaultProject(TestCase): self.t.config("default.project", self.default_project) def test_with_project(self): - """default.project not applied when specified nor on attribute removal - """ + """default.project not applied when specified nor on attribute removal""" self.set_default_project() self.t("add foobar project:garden") @@ -152,7 +152,7 @@ class TestDefaultProject(TestCase): self.set_default_project() DESC = "foobar" - self.t(('add', 'recur:daily', 'due:today', DESC)) + self.t(("add", "recur:daily", "due:today", DESC)) self.t() # Ensure creation of recurring children code, out, err = self.t("1 info") @@ -172,8 +172,10 @@ class TestDefaultProject(TestCase): try: id = int(id) except ValueError: - raise ValueError("Unexpected output when running 'task count', " - "expected int, got '{0}'".format(id)) + raise ValueError( + "Unexpected output when running 'task count', " + "expected int, got '{0}'".format(id) + ) else: # parent task is not considered when counting id = str(id + 1) @@ -188,7 +190,7 @@ class TestDefaultProject(TestCase): """no project is applied on recurring tasks""" # NOTE - reported on TW-1279 DESC = "foobar" - self.t(('add', 'recur:daily', 'due:today', DESC)) + self.t(("add", "recur:daily", "due:today", DESC)) code, out, err = self.t() self.assertIn(DESC, out) @@ -204,13 +206,12 @@ class TestDefaultProject(TestCase): self.assertNotIn("Project", out) def test_recurring_with_project_and_default_project(self): - """default.project is not applied to children if parent has a project - """ + """default.project is not applied to children if parent has a project""" # NOTE - reported on TW-1279 self.set_default_project() DESC = "foobar" - self.t(('add', 'recur:daily', 'due:today', 'project:HELLO', DESC)) + self.t(("add", "recur:daily", "due:today", "project:HELLO", DESC)) code, out, err = self.t() self.assertIn(DESC, out) @@ -227,6 +228,7 @@ class TestDefaultProject(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/feature.print.empty.columns.test.py b/test/feature.print.empty.columns.test.py index 66281e017..e68654a08 100755 --- a/test/feature.print.empty.columns.test.py +++ b/test/feature.print.empty.columns.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -39,7 +40,6 @@ class TestPrintEmptyColumns(TestCase): """Executed before each test in the class""" self.t = Task() - def test_empty_columns_feature(self): """Verify rc.print.empty.columns:yes shows more nothing than rc.print.empty.columns:no""" self.t("add one") @@ -64,6 +64,7 @@ class TestPrintEmptyColumns(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/feature.recurrence.test.py b/test/feature.recurrence.test.py index 0f50da486..ea48bc724 100755 --- a/test/feature.recurrence.test.py +++ b/test/feature.recurrence.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase @@ -67,6 +68,7 @@ class TestRecurrenceProblems(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/feedback.test.py b/test/feedback.test.py index de652da48..6ab97eefd 100755 --- a/test/feedback.test.py +++ b/test/feedback.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -69,6 +70,7 @@ class TestFeature1013(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/filter.test.py b/test/filter.test.py index 6c39b3906..47e32eccd 100755 --- a/test/filter.test.py +++ b/test/filter.test.py @@ -29,6 +29,7 @@ import sys import os import unittest import datetime + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -353,7 +354,7 @@ class TestFilterDue(TestCase): def setUp(self): self.t = Task() - self.t.config("due", "4") + self.t.config("due", "4") self.t.config("dateformat", "m/d/Y") just = datetime.datetime.now() + datetime.timedelta(days=3) @@ -441,7 +442,9 @@ class TestEmptyFilter(TestCase): self.t("add foo") self.t("add bar") - code, out, err = self.t.runError("modify rc.allow.empty.filter=yes rc.confirmation=no priority:H") + code, out, err = self.t.runError( + "modify rc.allow.empty.filter=yes rc.confirmation=no priority:H" + ) self.assertIn("Command prevented from running.", err) def test_empty_filter_error(self): @@ -451,7 +454,10 @@ class TestEmptyFilter(TestCase): self.t("add bar") code, out, err = self.t.runError("modify rc.allow.empty.filter=no priority:H") - self.assertIn("You did not specify a filter, and with the 'allow.empty.filter' value, no action is taken.", err) + self.assertIn( + "You did not specify a filter, and with the 'allow.empty.filter' value, no action is taken.", + err, + ) class TestFilterPrefix(TestCase): @@ -461,86 +467,86 @@ class TestFilterPrefix(TestCase): cls.t = Task() cls.t.config("verbose", "nothing") - cls.t('add project:foo.uno priority:H +tag "one foo"' ) - cls.t('add project:foo.dos priority:H "two"' ) - cls.t('add project:foo.tres "three"' ) - cls.t('add project:bar.uno priority:H "four"' ) - cls.t('add project:bar.dos +tag "five"' ) - cls.t('add project:bar.tres "six foo"' ) + cls.t('add project:foo.uno priority:H +tag "one foo"') + cls.t('add project:foo.dos priority:H "two"') + cls.t('add project:foo.tres "three"') + cls.t('add project:bar.uno priority:H "four"') + cls.t('add project:bar.dos +tag "five"') + cls.t('add project:bar.tres "six foo"') cls.t('add project:bazuno "seven bar foo"') cls.t('add project:bazdos "eight bar foo"') def test_list_all(self): """No filter shows all tasks.""" - code, out, err = self.t('list') - self.assertIn('one', out) - self.assertIn('two', out) - self.assertIn('three', out) - self.assertIn('four', out) - self.assertIn('five', out) - self.assertIn('six', out) - self.assertIn('seven', out) - self.assertIn('eight', out) + code, out, err = self.t("list") + self.assertIn("one", out) + self.assertIn("two", out) + self.assertIn("three", out) + self.assertIn("four", out) + self.assertIn("five", out) + self.assertIn("six", out) + self.assertIn("seven", out) + self.assertIn("eight", out) def test_list_project_foo(self): """Filter on project name.""" - code, out, err = self.t('list project:foo') - self.assertIn('one', out) - self.assertIn('two', out) - self.assertIn('three', out) - self.assertNotIn('four', out) - self.assertNotIn('five', out) - self.assertNotIn('six', out) - self.assertNotIn('seven', out) - self.assertNotIn('eight', out) + code, out, err = self.t("list project:foo") + self.assertIn("one", out) + self.assertIn("two", out) + self.assertIn("three", out) + self.assertNotIn("four", out) + self.assertNotIn("five", out) + self.assertNotIn("six", out) + self.assertNotIn("seven", out) + self.assertNotIn("eight", out) def test_list_project_not_foo(self): """Filter on not project name.""" - code, out, err = self.t('list project.not:foo') - self.assertNotIn('one', out) - self.assertNotIn('two', out) - self.assertNotIn('three', out) - self.assertIn('four', out) - self.assertIn('five', out) - self.assertIn('six', out) - self.assertIn('seven', out) - self.assertIn('eight', out) + code, out, err = self.t("list project.not:foo") + self.assertNotIn("one", out) + self.assertNotIn("two", out) + self.assertNotIn("three", out) + self.assertIn("four", out) + self.assertIn("five", out) + self.assertIn("six", out) + self.assertIn("seven", out) + self.assertIn("eight", out) def test_list_project_startswith_bar(self): """Filter on project name start.""" - code, out, err = self.t('list project.startswith:bar') - self.assertNotIn('one', out) - self.assertNotIn('two', out) - self.assertNotIn('three', out) - self.assertIn('four', out) - self.assertIn('five', out) - self.assertIn('six', out) - self.assertNotIn('seven', out) - self.assertNotIn('eight', out) + code, out, err = self.t("list project.startswith:bar") + self.assertNotIn("one", out) + self.assertNotIn("two", out) + self.assertNotIn("three", out) + self.assertIn("four", out) + self.assertIn("five", out) + self.assertIn("six", out) + self.assertNotIn("seven", out) + self.assertNotIn("eight", out) def test_list_project_ba(self): """Filter on project partial match.""" - code, out, err = self.t('list project:ba') - self.assertNotIn('one', out) - self.assertNotIn('two', out) - self.assertNotIn('three', out) - self.assertIn('four', out) - self.assertIn('five', out) - self.assertIn('six', out) - self.assertIn('seven', out) - self.assertIn('eight', out) + code, out, err = self.t("list project:ba") + self.assertNotIn("one", out) + self.assertNotIn("two", out) + self.assertNotIn("three", out) + self.assertIn("four", out) + self.assertIn("five", out) + self.assertIn("six", out) + self.assertIn("seven", out) + self.assertIn("eight", out) def test_list_description_has_foo(self): """Filter on description pattern.""" - code, out, err = self.t('list description.has:foo') - self.assertIn('one', out) - self.assertNotIn('two', out) - self.assertNotIn('three', out) - self.assertNotIn('four', out) - self.assertNotIn('five', out) - self.assertIn('six', out) - self.assertIn('seven', out) - self.assertIn('eight', out) + code, out, err = self.t("list description.has:foo") + self.assertIn("one", out) + self.assertNotIn("two", out) + self.assertNotIn("three", out) + self.assertNotIn("four", out) + self.assertNotIn("five", out) + self.assertIn("six", out) + self.assertIn("seven", out) + self.assertIn("eight", out) class TestBug480B(TestCase): @@ -675,7 +681,7 @@ class TestBug1600(TestCase): self.assertNotIn("foobar2", out) def test_filter_question_in_descriptions(self): - """filter - description contains ? """ + """filter - description contains ?""" self.t("add foobar1") self.t("add foo?bar") @@ -688,7 +694,7 @@ class TestBug1600(TestCase): self.assertNotIn("foobar1", out) def test_filter_brackets_in_descriptions(self): - """filter - description contains [] """ + """filter - description contains []""" self.t("add [foobar1]") self.t("add [foobar2]") @@ -707,9 +713,9 @@ class TestBug1656(TestCase): def test_report_filter_parenthesized(self): """default report filter parenthesized""" - self.t('add task1 +work') - self.t('add task2 +work') - self.t('1 done') + self.t("add task1 +work") + self.t("add task2 +work") + self.t("1 done") # Sanity check, next does not display completed tasks code, out, err = self.t("next") @@ -747,19 +753,19 @@ class TestHasHasnt(TestCase): def test_has_hasnt(self): """Verify the 'has' and 'hasnt' attribute modifiers""" - self.t("add foo") # 1 - self.t("add foo") # 2 + self.t("add foo") # 1 + self.t("add foo") # 2 self.t("2 annotate bar") - self.t("add foo") # 3 + self.t("add foo") # 3 self.t("3 annotate bar") self.t("3 annotate baz") - self.t("add bar") # 4 - self.t("add bar") # 5 + self.t("add bar") # 4 + self.t("add bar") # 5 self.t("5 annotate foo") - self.t("add bar") # 6 + self.t("add bar") # 6 self.t("6 annotate foo") self.t("6 annotate baz") - self.t("add one") # 7 + self.t("add one") # 7 self.t("7 annotate two") self.t("7 annotate three") @@ -787,8 +793,8 @@ class TestBefore(TestCase): def setUpClass(cls): """Executed once before any test in the class""" cls.t = Task() - cls.t('add foo entry:2008-12-22 start:2008-12-22') - cls.t('add bar entry:2009-04-17 start:2009-04-17') + cls.t("add foo entry:2008-12-22 start:2008-12-22") + cls.t("add bar entry:2009-04-17 start:2009-04-17") def test_correctly_recorded_start(self): """Verify start dates properly recorded""" @@ -840,14 +846,14 @@ class TestBy(TestCase): self.t = Task() def test_by_eoy_includes_eoy(self): - """ Verify by-end-of-year includes task due *at* end-of-year """ + """Verify by-end-of-year includes task due *at* end-of-year""" self.t("add zero due:eoy") code, out, err = self.t("due.by:eoy") self.assertIn("zero", out) def test_by_tomorrow_includes_tomorrow(self): - """ Verify that by-tomorrow also includes tomorrow itself """ + """Verify that by-tomorrow also includes tomorrow itself""" self.t.faketime("2021-07-16 21:00:00") self.t("add zero due:2021-07-17") @@ -855,7 +861,7 @@ class TestBy(TestCase): self.assertIn("zero", out) def test_by_yesterday_does_not_include_today(self): - """ Verify that by-yesterday does not include today """ + """Verify that by-yesterday does not include today""" self.t("add zero") code, out, err = self.t.runError("entry.by:yesterday") @@ -869,8 +875,8 @@ class Test1424(TestCase): def test_1824_days(self): """1424: Check that due:1824d works""" - self.t('add foo due:1824d') - code, out, err = self.t('_get 1.due.year') + self.t("add foo due:1824d") + code, out, err = self.t("_get 1.due.year") # NOTE This test has a possible race condition when run "during" EOY. # If Taskwarrior is executed at 23:59:59 on new year's eve and the # python code below runs at 00:00:00 on new year's day, the two will @@ -881,8 +887,8 @@ class Test1424(TestCase): def test_3648_days(self): """1424: Check that due:3648d works""" - self.t('add foo due:3648d') - code, out, err = self.t('_get 1.due.year') + self.t("add foo due:3648d") + code, out, err = self.t("_get 1.due.year") # NOTE This test has a possible race condition when run "during" EOY. # If Taskwarrior is executed at 23:59:59 on new year's eve and the # python code below runs at 00:00:00 on new year's day, the two will @@ -897,22 +903,22 @@ class Test1424(TestCase): class Test1452(TestCase): def setUp(self): self.t = Task() - self.t('add task') - self.task_uuid = self.t.export_one()['uuid'] + self.t("add task") + self.task_uuid = self.t.export_one()["uuid"] def test_get_task_by_uuid_with_prefix(self): """1452: Tries to filter task simply by its uuid, using uuid: prefix.""" - output = self.t.export_one('uuid:%s' % self.task_uuid) + output = self.t.export_one("uuid:%s" % self.task_uuid) # Sanity check it is the correct one - self.assertEqual(output['uuid'], self.task_uuid) + self.assertEqual(output["uuid"], self.task_uuid) def test_get_task_by_uuid_without_prefix(self): """1452: Tries to filter task simply by its uuid, without using uuid: prefix.""" output = self.t.export_one(self.task_uuid) # Sanity check it is the correct one - self.assertEqual(output['uuid'], self.task_uuid) + self.assertEqual(output["uuid"], self.task_uuid) class TestBug1456(TestCase): @@ -935,22 +941,22 @@ class TestBug1456(TestCase): class Test1468(TestCase): def setUp(self): self.t = Task() - self.t('add project:home buy milk') - self.t('add project:home mow the lawn') + self.t("add project:home buy milk") + self.t("add project:home mow the lawn") def test_single_attribute_filter(self): """1468: Single attribute filter (project:home)""" - code, out, err = self.t('list project:home') + code, out, err = self.t("list project:home") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('buy milk', out) - self.assertIn('mow the lawn', out) + self.assertIn("buy milk", out) + self.assertIn("mow the lawn", out) def test_attribute_and_search_filter(self): """1468: Attribute and implicit search filter (project:home /lawn/)""" - code, out, err = self.t('list project:home /lawn/') + code, out, err = self.t("list project:home /lawn/") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertNotIn('buy milk', out) - self.assertIn('mow the lawn', out) + self.assertNotIn("buy milk", out) + self.assertIn("mow the lawn", out) class TestBug1521(TestCase): @@ -1021,19 +1027,19 @@ class Test1634(TestCase): self.t = Task() # Setup some tasks due on 2015-07-07 - self.t('add due:2015-07-07T00:00:00 ON1') - self.t('add due:2015-07-07T14:34:56 ON2') - self.t('add due:2015-07-07T23:59:59 ON3') + self.t("add due:2015-07-07T00:00:00 ON1") + self.t("add due:2015-07-07T14:34:56 ON2") + self.t("add due:2015-07-07T23:59:59 ON3") # Setup some tasks not due on 2015-07-07 - self.t('add due:2015-07-06T23:59:59 OFF4') - self.t('add due:2015-07-08T00:00:00 OFF5') - self.t('add due:2015-07-08T00:00:01 OFF6') - self.t('add due:2015-07-06T00:00:00 OFF7') + self.t("add due:2015-07-06T23:59:59 OFF4") + self.t("add due:2015-07-08T00:00:00 OFF5") + self.t("add due:2015-07-08T00:00:01 OFF6") + self.t("add due:2015-07-06T00:00:00 OFF7") def test_due_match_not_exact(self): """1634: Test that due: matches any task that date.""" - code, out, err = self.t('due:2015-07-07 minimal') + code, out, err = self.t("due:2015-07-07 minimal") # Asswer that only tasks ON the date are listed. self.assertIn("ON1", out) @@ -1048,7 +1054,7 @@ class Test1634(TestCase): def test_due_not_match_not_exact(self): """1634: Test that due.not: does not match any task that date.""" - code, out, err = self.t('due.not:2015-07-07 minimal') + code, out, err = self.t("due.not:2015-07-07 minimal") # Assert that task ON the date are not listed. self.assertNotIn("ON1", out) @@ -1072,14 +1078,18 @@ class TestBug1915(TestCase): def test_complex_and_or_query_variant_one(self): """1915: Make sure parser handles complex and-or queries correctly (1)""" - code, out, err = self.t("rc.verbose:nothing '(project:A or project:B) and status:pending' all") + code, out, err = self.t( + "rc.verbose:nothing '(project:A or project:B) and status:pending' all" + ) self.assertIn("thingA", out) self.assertIn("thingB", out) self.assertNotIn("thingC", out) def test_complex_and_or_query_variant_two(self): """1915: Make sure parser handles complex and-or queries correctly (2)""" - code, out, err = self.t("rc.verbose:nothing '( project:A or project:B ) and status:pending' all") + code, out, err = self.t( + "rc.verbose:nothing '( project:A or project:B ) and status:pending' all" + ) self.assertIn("thingA", out) self.assertIn("thingB", out) self.assertNotIn("thingC", out) @@ -1087,7 +1097,9 @@ class TestBug1915(TestCase): @unittest.expectedFailure def test_complex_and_or_query_variant_three(self): """1915: Make sure parser handles complex and-or queries correctly (3)""" - code, out, err = self.t("rc.verbose:nothing 'status:pending and (project:A or project:B)' all") + code, out, err = self.t( + "rc.verbose:nothing 'status:pending and (project:A or project:B)' all" + ) self.assertIn("thingA", out) self.assertIn("thingB", out) self.assertNotIn("thingC", out) @@ -1095,28 +1107,36 @@ class TestBug1915(TestCase): @unittest.expectedFailure def test_complex_and_or_query_variant_four(self): """1915: Make sure parser handles complex and-or queries correctly (4)""" - code, out, err = self.t("rc.verbose:nothing 'status:pending and ( project:A or project:B )' all") + code, out, err = self.t( + "rc.verbose:nothing 'status:pending and ( project:A or project:B )' all" + ) self.assertIn("thingA", out) self.assertIn("thingB", out) self.assertNotIn("thingC", out) def test_complex_and_or_query_variant_five(self): """1915: Make sure parser handles complex and-or queries correctly (5)""" - code, out, err = self.t("rc.verbose:nothing status:pending and '(project:A or project:B)' all") + code, out, err = self.t( + "rc.verbose:nothing status:pending and '(project:A or project:B)' all" + ) self.assertIn("thingA", out) self.assertIn("thingB", out) self.assertNotIn("thingC", out) def test_complex_and_or_query_variant_six(self): """1915: Make sure parser handles complex and-or queries correctly (6)""" - code, out, err = self.t("rc.verbose:nothing status:pending and '( project:A or project:B )' all") + code, out, err = self.t( + "rc.verbose:nothing status:pending and '( project:A or project:B )' all" + ) self.assertIn("thingA", out) self.assertIn("thingB", out) self.assertNotIn("thingC", out) def test_complex_and_or_query_variant_seven(self): """1915: Make sure parser handles complex and-or queries correctly (7)""" - code, out, err = self.t("rc.verbose:nothing status:pending and \\( project:A or project:B \\) all") + code, out, err = self.t( + "rc.verbose:nothing status:pending and \\( project:A or project:B \\) all" + ) self.assertIn("thingA", out) self.assertIn("thingB", out) self.assertNotIn("thingC", out) @@ -1124,7 +1144,9 @@ class TestBug1915(TestCase): @unittest.expectedFailure def test_complex_and_or_query_variant_eight(self): """1915: Make sure parser handles complex and-or queries correctly (8)""" - code, out, err = self.t("rc.verbose:nothing status:pending and \\(project:A or project:B\\) all") + code, out, err = self.t( + "rc.verbose:nothing status:pending and \\(project:A or project:B\\) all" + ) self.assertIn("thingA", out) self.assertIn("thingB", out) self.assertNotIn("thingC", out) @@ -1136,11 +1158,11 @@ class Test2577(TestCase): def test_filtering_for_datetime_like(self): """2577: Check that filtering for datetime-like project names works""" - self.t('add one pro:sat') # looks like "saturday" - self.t('add two pro:whatever') + self.t("add one pro:sat") # looks like "saturday" + self.t("add two pro:whatever") # This should not fail (fails on 2.5.3) - code, out, err = self.t('pro:sat') + code, out, err = self.t("pro:sat") # Assert expected output, but the crucial part of this test is success # of the call above @@ -1149,6 +1171,7 @@ class Test2577(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/fontunderline.test.py b/test/fontunderline.test.py index 8b2b4c11e..626d6e1c0 100755 --- a/test/fontunderline.test.py +++ b/test/fontunderline.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -57,40 +58,57 @@ class TestUnderline(TestCase): # * When isatty (fileno (stdout)) is false, color is automatically disabled. def test_nocolor_noforce_nounderline(self): - code, out, err = self.t("1 info rc.color:off rc._forcecolor:off rc.fontunderline:off") + code, out, err = self.t( + "1 info rc.color:off rc._forcecolor:off rc.fontunderline:off" + ) self.assertIn("--------", out) def test_nocolor_noforce_underline(self): - code, out, err = self.t("1 info rc.color:off rc._forcecolor:off rc.fontunderline:on") + code, out, err = self.t( + "1 info rc.color:off rc._forcecolor:off rc.fontunderline:on" + ) self.assertIn("--------", out) def test_nocolor_force_nounderline(self): - code, out, err = self.t("1 info rc.color:off rc._forcecolor:on rc.fontunderline:off") + code, out, err = self.t( + "1 info rc.color:off rc._forcecolor:on rc.fontunderline:off" + ) self.assertIn("--------", out) def test_nocolor_force_underline(self): - code, out, err = self.t("1 info rc.color:off rc._forcecolor:on rc.fontunderline:on") + code, out, err = self.t( + "1 info rc.color:off rc._forcecolor:on rc.fontunderline:on" + ) self.assertNotIn("--------", out) def test_color_noforce_nounderline(self): - code, out, err = self.t("1 info rc.color:on rc._forcecolor:off rc.fontunderline:off") + code, out, err = self.t( + "1 info rc.color:on rc._forcecolor:off rc.fontunderline:off" + ) self.assertIn("--------", out) def test_color_noforce_underline(self): - code, out, err = self.t("1 info rc.color:on rc._forcecolor:off rc.fontunderline:on") + code, out, err = self.t( + "1 info rc.color:on rc._forcecolor:off rc.fontunderline:on" + ) self.assertIn("--------", out) def test_color_force_nounderline(self): - code, out, err = self.t("1 info rc.color:on rc._forcecolor:on rc.fontunderline:off") + code, out, err = self.t( + "1 info rc.color:on rc._forcecolor:on rc.fontunderline:off" + ) self.assertIn("--------", out) def test_color_force_underline(self): - code, out, err = self.t("1 info rc.color:on rc._forcecolor:on rc.fontunderline:on") + code, out, err = self.t( + "1 info rc.color:on rc._forcecolor:on rc.fontunderline:on" + ) self.assertNotIn("--------", out) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/format.test.py b/test/format.test.py index 997aa452f..b1cf99c95 100755 --- a/test/format.test.py +++ b/test/format.test.py @@ -29,6 +29,7 @@ import sys import os import unittest import math + # Ensure python finds the local simpletap and basetest modules sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -61,11 +62,11 @@ class TestCountdown(TestCase): def test_countdown_up(self): """Verify countdown sorting: ascending""" - self.t.config("report.up.description", "countdown+ report") - self.t.config("report.up.columns", "id,due.countdown,description") - self.t.config("report.up.labels", "ID,Countdown,Description") - self.t.config("report.up.filter", "status:pending") - self.t.config("report.up.sort", "due+") + self.t.config("report.up.description", "countdown+ report") + self.t.config("report.up.columns", "id,due.countdown,description") + self.t.config("report.up.labels", "ID,Countdown,Description") + self.t.config("report.up.filter", "status:pending") + self.t.config("report.up.sort", "due+") code, out, err = self.t("up") self.assertRegex(out, " one\n.+ two\n") @@ -85,11 +86,11 @@ class TestCountdown(TestCase): def test_countdown_down(self): """Verify countdown sorting: descending""" - self.t.config("report.down.description", "countdown- report") - self.t.config("report.down.columns", "id,due.countdown,description") - self.t.config("report.down.labels", "ID,Countdown,Description") - self.t.config("report.down.filter", "status:pending") - self.t.config("report.down.sort", "due-") + self.t.config("report.down.description", "countdown- report") + self.t.config("report.down.columns", "id,due.countdown,description") + self.t.config("report.down.labels", "ID,Countdown,Description") + self.t.config("report.down.filter", "status:pending") + self.t.config("report.down.sort", "due-") code, out, err = self.t("down") self.assertRegex(out, " fifteen\n.+ fourteen\n") @@ -143,7 +144,9 @@ class TestBug101(TestCase): self.short_description = "A_task_description_" # Generate long string - self.long_description = self.short_description * int(math.ceil(float(self.width)/len(self.short_description))) + self.long_description = self.short_description * int( + math.ceil(float(self.width) / len(self.short_description)) + ) def test_short_no_count(self): """101: Check short description with no annotations""" @@ -164,7 +167,7 @@ class TestBug101(TestCase): """101: Check long description with no annotations""" self.t(("add", self.long_description)) code, out, err = self.t("bug101") - expected = self.long_description[:(self.width - 3)] + "..." + expected = self.long_description[: (self.width - 3)] + "..." self.assertIn(expected, out) def test_long_with_count(self): @@ -172,7 +175,7 @@ class TestBug101(TestCase): self.t(("add", self.long_description)) self.t("1 annotate 'A task annotation'") code, out, err = self.t("bug101") - expected = self.long_description[:(self.width - 7)] + "... [1]" + expected = self.long_description[: (self.width - 7)] + "... [1]" self.assertIn(expected, out) def test_long_with_double_digit_count(self): @@ -181,12 +184,13 @@ class TestBug101(TestCase): for i in range(10): self.t("1 annotate 'A task annotation'") code, out, err = self.t("bug101") - expected = self.long_description[:(self.width - 8)] + "... [10]" + expected = self.long_description[: (self.width - 8)] + "... [10]" self.assertIn(expected, out) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/gc.test.py b/test/gc.test.py index 36681964b..2b3c0577e 100755 --- a/test/gc.test.py +++ b/test/gc.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -72,6 +73,7 @@ class TestGC(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/helpers.test.py b/test/helpers.test.py index 8dbc27c28..b28c8f6c8 100755 --- a/test/helpers.test.py +++ b/test/helpers.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -42,10 +43,10 @@ class TestZshAttributes(TestCase): def test_zsh_attributes_helper(self): """Ensure the _zshattributes command returns the expected format""" code, out, err = self.t("_zshattributes") - for line in out.split('\n'): - if line != '': - fields = line.split(':') - self.assertEqual(fields[0], fields[1]) + for line in out.split("\n"): + if line != "": + fields = line.split(":") + self.assertEqual(fields[0], fields[1]) class TestZshCompletion(TestCase): @@ -69,6 +70,7 @@ class TestAliasesCompletion(TestCase): """Aliases should be listed by '_aliases' not '_commands' or '_zshcommands' reported as bug 1043 """ + def setUp(self): self.t = Task() self.t.config("alias.samplealias", "long") @@ -131,6 +133,7 @@ class TestBug956(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/history.test.py b/test/history.test.py index 04f46b005..e2c5a7c40 100755 --- a/test/history.test.py +++ b/test/history.test.py @@ -28,11 +28,13 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase + class TestHistoryDaily(TestCase): def setUp(self): """Executed before each test in the class""" @@ -66,6 +68,7 @@ class TestHistoryDaily(TestCase): self.assertRegex(out, r"2015\s+January\s+2\s+\++X+\s") self.assertRegex(out, r"\s+February\s+2\s+\++X+\-+") + class TestHistoryWeekly(TestCase): def setUp(self): """Executed before each test in the class""" @@ -171,8 +174,10 @@ class TestHistoryAnnual(TestCase): self.assertRegex(out, r"2014\s+\++X+\s") self.assertRegex(out, r"2015\s+\++X+\-+") + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/hooks.env.test.py b/test/hooks.env.test.py index ea9d91593..14eda273d 100755 --- a/test/hooks.env.test.py +++ b/test/hooks.env.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -42,28 +43,30 @@ class TestHooksOnLaunch(TestCase): def test_onlaunch_builtin_env(self): """on-launch-env - a well-behaved, successful, on-launch hook that echoes its env.""" - hookname = 'on-launch-good-env' + hookname = "on-launch-good-env" self.t.hooks.add_default(hookname, log=True) - code, out, err = self.t("version") # Arbitrary command that generates output. + code, out, err = self.t("version") # Arbitrary command that generates output. hook = self.t.hooks[hookname] hook.assertTriggeredCount(1) hook.assertExitcode(0) logs = hook.get_logs() - taskenv = {k:v for k, v in (line.split(":", 1) for line in logs["output"]["msgs"])} + taskenv = { + k: v for k, v in (line.split(":", 1) for line in logs["output"]["msgs"]) + } - self.assertEqual('api' in taskenv, True, 'api:...') - self.assertEqual('args' in taskenv, True, 'args:...') - self.assertEqual('command' in taskenv, True, 'command:...') - self.assertEqual('rc' in taskenv, True, 'rc:...') - self.assertEqual('data' in taskenv, True, 'data:...') - self.assertEqual('version' in taskenv, True, 'version:...') + self.assertEqual("api" in taskenv, True, "api:...") + self.assertEqual("args" in taskenv, True, "args:...") + self.assertEqual("command" in taskenv, True, "command:...") + self.assertEqual("rc" in taskenv, True, "rc:...") + self.assertEqual("data" in taskenv, True, "data:...") + self.assertEqual("version" in taskenv, True, "version:...") def test_onlaunch_builtin_env_diag(self): """Verify that 'diagnostics' can see hook details""" - hookname = 'on-launch-good-env' + hookname = "on-launch-good-env" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("diagnostics") @@ -71,7 +74,7 @@ class TestHooksOnLaunch(TestCase): def test_onlaunch_builtin_env_debug(self): """Verify that 'debug.hooks' shows hook details""" - hookname = 'on-launch-good-env' + hookname = "on-launch-good-env" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("version rc.debug.hooks:2") @@ -84,6 +87,7 @@ class TestHooksOnLaunch(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/hooks.on-add.test.py b/test/hooks.on-add.test.py index c63553752..011ebd86f 100755 --- a/test/hooks.on-add.test.py +++ b/test/hooks.on-add.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -42,7 +43,7 @@ class TestHooksOnAdd(TestCase): def test_onadd_builtin_accept(self): """on-add-accept - a well-behaved, successful, on-add hook.""" - hookname = 'on-add-accept' + hookname = "on-add-accept" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add foo") @@ -59,7 +60,7 @@ class TestHooksOnAdd(TestCase): def test_onadd_builtin_accept_modify(self): """on-add-accept-modify - a well-behaved, successful, on-add hook, that modifies the added task.""" - hookname = 'on-add-modify' + hookname = "on-add-modify" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add teh foo") @@ -76,7 +77,7 @@ class TestHooksOnAdd(TestCase): def test_onadd_builtin_reject(self): """on-add-reject - a well-behaved, failing, on-add hook.""" - hookname = 'on-add-reject' + hookname = "on-add-reject" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t.runError("add foo") @@ -94,7 +95,7 @@ class TestHooksOnAdd(TestCase): def test_onadd_builtin_misbehave1(self): """on-add-misbehave1 - does not consume input.""" - hookname = 'on-add-misbehave1' + hookname = "on-add-misbehave1" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t.runError("add foo") @@ -108,7 +109,7 @@ class TestHooksOnAdd(TestCase): def test_onadd_builtin_misbehave2(self): """on-add-misbehave2 - does not emit JSON.""" - hookname = 'on-add-misbehave2' + hookname = "on-add-misbehave2" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t.runError("add foo") @@ -120,7 +121,7 @@ class TestHooksOnAdd(TestCase): def test_onadd_builtin_misbehave3(self): """on-add-misbehave3 - emits additional JSON.""" - hookname = 'on-add-misbehave3' + hookname = "on-add-misbehave3" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t.runError("add foo") @@ -132,7 +133,7 @@ class TestHooksOnAdd(TestCase): def test_onadd_builtin_misbehave4(self): """on-add-misbehave4 - emits different task JSON.""" - hookname = 'on-add-misbehave4' + hookname = "on-add-misbehave4" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t.runError("add foo") @@ -147,11 +148,11 @@ class TestHooksOnAdd(TestCase): def test_onadd_builtin_misbehave5(self): """on-add-misbehave5 - emits syntactically wrong JSON.""" - hookname = 'on-add-misbehave5' + hookname = "on-add-misbehave5" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t.runError("add foo") - self.assertIn("Hook Error: JSON syntax error in: {\"}", err) + self.assertIn('Hook Error: JSON syntax error in: {"}', err) hook = self.t.hooks[hookname] hook.assertTriggeredCount(1) @@ -163,11 +164,14 @@ class TestHooksOnAdd(TestCase): def test_onadd_builtin_misbehave6(self): """on-add-misbehave6 - emits incomplete JSON.""" - hookname = 'on-add-misbehave6' + hookname = "on-add-misbehave6" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t.runError("add foo") - self.assertIn("Hook Error: JSON Object missing 'uuid' attribute from hook script: on-add-misbehave6", err) + self.assertIn( + "Hook Error: JSON Object missing 'uuid' attribute from hook script: on-add-misbehave6", + err, + ) hook = self.t.hooks[hookname] hook.assertTriggeredCount(1) @@ -176,8 +180,10 @@ class TestHooksOnAdd(TestCase): logs = hook.get_logs() self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK") + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/hooks.on-exit.test.py b/test/hooks.on-exit.test.py index fe7e05b30..5673395ec 100755 --- a/test/hooks.on-exit.test.py +++ b/test/hooks.on-exit.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -42,7 +43,7 @@ class TestHooksOnExit(TestCase): def test_onexit_builtin_good(self): """on-exit-good - a well-behaved, successful, on-exit hook.""" - hookname = 'on-exit-good' + hookname = "on-exit-good" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("version") @@ -57,7 +58,7 @@ class TestHooksOnExit(TestCase): def test_onexit_builtin_good_gets_changed_tasks(self): """on-exit-good - a well-behaved, successful, on-exit hook.""" - hookname = 'on-exit-good' + hookname = "on-exit-good" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add foo") @@ -73,7 +74,7 @@ class TestHooksOnExit(TestCase): def test_onexit_builtin_bad(self): """on-exit-bad - a well-behaved, failing, on-exit hook.""" - hookname = 'on-exit-bad' + hookname = "on-exit-bad" self.t.hooks.add_default(hookname, log=True) # Failing hook should prevent processing. @@ -89,7 +90,7 @@ class TestHooksOnExit(TestCase): def test_onexit_builtin_misbehave1(self): """on-exit-misbehave1 - Does not consume input.""" - hookname = 'on-exit-misbehave1' + hookname = "on-exit-misbehave1" self.t.hooks.add_default(hookname, log=True) # Failing hook should prevent processing. @@ -105,7 +106,7 @@ class TestHooksOnExit(TestCase): def test_onexit_builtin_misbehave2(self): """on-exit-misbehave2 - Emits unexpected JSON.""" - hookname = 'on-exit-misbehave2' + hookname = "on-exit-misbehave2" self.t.hooks.add_default(hookname, log=True) # Failing hook should prevent processing. @@ -119,8 +120,10 @@ class TestHooksOnExit(TestCase): logs = hook.get_logs() self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK") + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/hooks.on-launch.test.py b/test/hooks.on-launch.test.py index b89098d2e..731486d47 100755 --- a/test/hooks.on-launch.test.py +++ b/test/hooks.on-launch.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -42,7 +43,7 @@ class TestHooksOnLaunch(TestCase): def test_onlaunch_builtin_good(self): """on-launch-good - a well-behaved, successful, on-launch hook.""" - hookname = 'on-launch-good' + hookname = "on-launch-good" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("version") @@ -57,7 +58,7 @@ class TestHooksOnLaunch(TestCase): def test_onlaunch_builtin_bad(self): """on-launch-bad - a well-behaved, failing, on-launch hook.""" - hookname = 'on-launch-bad' + hookname = "on-launch-bad" self.t.hooks.add_default(hookname, log=True) # Failing hook should prevent processing. @@ -73,7 +74,7 @@ class TestHooksOnLaunch(TestCase): def test_onlaunch_builtin_misbehave1(self): """on-launch-misbehave1 - Hook kills itself.""" - hookname = 'on-launch-misbehave1' + hookname = "on-launch-misbehave1" self.t.hooks.add_default(hookname, log=True) # Failing hook should prevent processing. @@ -89,7 +90,7 @@ class TestHooksOnLaunch(TestCase): def test_onlaunch_builtin_misbehave2(self): """on-launch-misbehave2 - Hook emits unexpected JSON.""" - hookname = 'on-launch-misbehave2' + hookname = "on-launch-misbehave2" self.t.hooks.add_default(hookname, log=True) # Failing hook should prevent processing. @@ -103,8 +104,10 @@ class TestHooksOnLaunch(TestCase): logs = hook.get_logs() self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK") + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/hooks.on-modify.test.py b/test/hooks.on-modify.test.py index 2f8fea56e..e835eeb9a 100755 --- a/test/hooks.on-modify.test.py +++ b/test/hooks.on-modify.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -42,7 +43,7 @@ class TestHooksOnModify(TestCase): def test_onmodify_builtin_accept(self): """on-modify-accept - a well-behaved, successful, on-modify hook.""" - hookname = 'on-modify-accept' + hookname = "on-modify-accept" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add foo") @@ -61,7 +62,7 @@ class TestHooksOnModify(TestCase): def test_onmodify_builtin_reject(self): """on-modify-reject - a well-behaved, failing, on-modify hook.""" - hookname = 'on-modify-reject' + hookname = "on-modify-reject" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add foo") @@ -76,7 +77,7 @@ class TestHooksOnModify(TestCase): def test_onmodify_builtin_misbehave2(self): """on-modify-misbehave2 - does not emit JSON.""" - hookname = 'on-modify-misbehave2' + hookname = "on-modify-misbehave2" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add foo") @@ -92,7 +93,7 @@ class TestHooksOnModify(TestCase): def test_onmodify_builtin_misbehave3(self): """on-modify-misbehave3 - emits additional JSON.""" - hookname = 'on-modify-misbehave3' + hookname = "on-modify-misbehave3" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add foo") @@ -108,7 +109,7 @@ class TestHooksOnModify(TestCase): def test_onmodify_builtin_misbehave4(self): """on-modify-misbehave4 - emits different task JSON.""" - hookname = 'on-modify-misbehave4' + hookname = "on-modify-misbehave4" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add foo") @@ -124,12 +125,12 @@ class TestHooksOnModify(TestCase): def test_onmodify_builtin_misbehave5(self): """on-modify-misbehave5 - emits syntactically wrong JSON.""" - hookname = 'on-modify-misbehave5' + hookname = "on-modify-misbehave5" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add foo") code, out, err = self.t.runError("1 modify +tag") - self.assertIn("Hook Error: JSON syntax error in: {\"}", err) + self.assertIn('Hook Error: JSON syntax error in: {"}', err) hook = self.t.hooks[hookname] hook.assertTriggeredCount(1) @@ -140,12 +141,15 @@ class TestHooksOnModify(TestCase): def test_onmodify_builtin_misbehave6(self): """on-modify-misbehave6 - emits incomplete JSON.""" - hookname = 'on-modify-misbehave6' + hookname = "on-modify-misbehave6" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add foo") code, out, err = self.t.runError("1 modify +tag") - self.assertIn("Hook Error: JSON Object missing 'uuid' attribute from hook script: on-modify-misbehave6", err) + self.assertIn( + "Hook Error: JSON Object missing 'uuid' attribute from hook script: on-modify-misbehave6", + err, + ) hook = self.t.hooks[hookname] hook.assertTriggeredCount(1) @@ -156,7 +160,7 @@ class TestHooksOnModify(TestCase): def test_onmodify_revert_changes(self): """on-modify-revert - revert all user modifications.""" - hookname = 'on-modify-revert' + hookname = "on-modify-revert" self.t.hooks.add_default(hookname, log=True) code, out, err = self.t("add foo") @@ -172,8 +176,10 @@ class TestHooksOnModify(TestCase): hook.assertTriggeredCount(1) hook.assertExitcode(0) + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/hyphenate.test.py b/test/hyphenate.test.py index 33aa05a91..c2c7466a8 100755 --- a/test/hyphenate.test.py +++ b/test/hyphenate.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -39,8 +40,8 @@ class TestHyphenation(TestCase): """Executed before each test in the class""" self.t = Task() self.t.config("defaultwidth", "20") - self.t.config("detection", "0") - self.t.config("verbose", "nothing") + self.t.config("detection", "0") + self.t.config("verbose", "nothing") def test_hyphenation_on_space(self): """Split on space instead of hyphenating""" @@ -54,6 +55,7 @@ class TestHyphenation(TestCase): code, out, err = self.t("ls") self.assertIn(" 1 AAAAAAAAAABBBBBB-\n", out) + class TestBug804(TestCase): def setUp(self): """Executed before each test in the class""" @@ -61,10 +63,10 @@ class TestBug804(TestCase): def test_hyphenation(self): """Verify hyphenation is controllable""" - self.t.config("print.empty.columns", "1") - self.t.config("report.unittest.labels", "ID,Project,Pri,Description") + self.t.config("print.empty.columns", "1") + self.t.config("report.unittest.labels", "ID,Project,Pri,Description") self.t.config("report.unittest.columns", "id,project,priority,description") - self.t.config("report.unittest.filter", "status:pending") + self.t.config("report.unittest.filter", "status:pending") # Setup: Add a tasks, annotate with long word. self.t("add one") @@ -83,6 +85,7 @@ class TestBug804(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/ids.test.py b/test/ids.test.py index cf810056f..4dc30935e 100755 --- a/test/ids.test.py +++ b/test/ids.test.py @@ -29,6 +29,7 @@ import sys import os import tempfile import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -42,9 +43,9 @@ class TestIDs(TestCase): self.t = Task() self.t("add one +A +B") - self.t("add two +A" ) + self.t("add two +A") self.t("add three +A +B") - self.t("add four" ) + self.t("add four") self.t("add five +A +B") def test_ids_count_A(self): @@ -86,7 +87,8 @@ class TestIDs(TestCase): """_zshuuids +A""" code, out, err = self.t("_zshuuids +A") self.assertRegex( - out, "{0}:one\n{0}:two\n{0}:three\n{0}:five".format(UUID_REGEXP)) + out, "{0}:one\n{0}:two\n{0}:three\n{0}:five".format(UUID_REGEXP) + ) def test_ids_ranges(self): """Verify consecutive IDs are compressed into a range""" @@ -107,8 +109,8 @@ class TestIDMisParse(TestCase): def test_parse_numbers_as_ids_not_patterns(self): """Verify that numbers are parsed as IDs""" - self.t("add 2 two") # ID 1 - self.t("add 1 one") # ID 2 + self.t("add 2 two") # ID 1 + self.t("add 1 one") # ID 2 self.t("add 3 three") # ID 3 code, out, err = self.t("2 ls rc.verbose:nothing") @@ -124,11 +126,13 @@ class TestIDRangeParsing(TestCase): def generate_tasks(self, n): """Generates n tasks for testing purposes""" - with tempfile.NamedTemporaryFile(mode='w') as f: - f.write('\n'.join([f'{{"description": "test task {i+1}"}}' for i in range(n)])) + with tempfile.NamedTemporaryFile(mode="w") as f: + f.write( + "\n".join([f'{{"description": "test task {i+1}"}}' for i in range(n)]) + ) f.flush() # use a long timeout here, because import is quite slow - code, out, err = self.t(f'import {f.name}', timeout=100) + code, out, err = self.t(f"import {f.name}", timeout=100) def test_single_digit_range(self): """Test that parsing single digit ID range works""" @@ -167,6 +171,7 @@ class TestIDRangeParsing(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/import.test.py b/test/import.test.py index 8fcc9366a..7771345a3 100755 --- a/test/import.test.py +++ b/test/import.test.py @@ -29,6 +29,7 @@ import sys import os import unittest import json + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -179,7 +180,7 @@ class TestImport(TestCase): def test_import_newlines_whitespace(self): """JSON array with whitespace before and after names and values""" _data = """[ -{ "uuid":"a0000000-a000-a000-a000-a00000000000" , "description" : "zero" ,"project":"A", "status":"pending","entry":"1234567889" } , +{ "uuid":"a0000000-a000-a000-a000-a00000000000" , "description" : "zero" ,"project":"A", "status":"pending","entry":"1234567889" } , { "uuid":"a1111111-a111-a111-a111-a11111111111","description":"one","project":"B","status":"pending","entry":"1234567889"}, {"uuid":"a2222222-a222-a222-a222-a22222222222","description":"two","status":"completed","entry":"1234524689","end":"1234524690" } ]""" code, out, err = self.t("import", input=_data) @@ -210,15 +211,20 @@ class TestImport(TestCase): self.t("import", input=_data) _t = self.t.export("a0000000-a000-a000-a000-a00000000000")[0] - for _uuid in ["a1111111-a111-a111-a111-a11111111111","a2222222-a222-a222-a222-a22222222222"]: + for _uuid in [ + "a1111111-a111-a111-a111-a11111111111", + "a2222222-a222-a222-a222-a22222222222", + ]: self.assertTrue((_t["depends"][0] == _uuid) or (_t["depends"][1] == _uuid)) def test_import_same_task_twice(self): """Test import same task twice""" - _data = """{"uuid":"a1111111-a222-a333-a444-a55555555555","description":"data4"}""" + _data = ( + """{"uuid":"a1111111-a222-a333-a444-a55555555555","description":"data4"}""" + ) self.t("import", input=_data) code, out1, err = self.t("export") - self.t.faketime('+1s') + self.t.faketime("+1s") self.t("import", input=_data) code, out2, err = self.t("export") self.assertEqual(out1, out2) @@ -230,7 +236,9 @@ class TestImport(TestCase): {"uuid":"a0000000-a000-a000-a000-a00000000000","description":"second description"} ]""" _, _, err = self.t("import", input=_data) - self.assertIn("Input contains UUID 'a0000000-a000-a000-a000-a00000000000' 2 times", err) + self.assertIn( + "Input contains UUID 'a0000000-a000-a000-a000-a00000000000' 2 times", err + ) class TestImportExportRoundtrip(TestCase): @@ -239,8 +247,8 @@ class TestImportExportRoundtrip(TestCase): self.t2 = Task() for client in (self.t1, self.t2): - client.config("dateformat", "m/d/Y") - client.config("verbose", "0") + client.config("dateformat", "m/d/Y") + client.config("verbose", "0") client.config("defaultwidth", "100") def _validate_data(self, client): @@ -277,7 +285,7 @@ class TestImportValidate(TestCase): def test_import_empty_json(self): """Verify empty JSON is caught""" - j = '{}' + j = "{}" code, out, err = self.t.runError("import", input=j) self.assertIn("A task must have a description.", err) @@ -343,6 +351,7 @@ class TestBug1441(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/info.test.py b/test/info.test.py index 89679b8bd..4d6945936 100755 --- a/test/info.test.py +++ b/test/info.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -59,7 +60,9 @@ class TestInfoCommand(TestCase): self.t.config("urgency.user.keyword.foo.coefficient", "1.0") self.t.config("urgency.uda.u_one.coefficient", "1.0") - self.t("add foo project:P +tag priority:H start:now due:eom wait:eom scheduled:eom recur:P1M until:eoy u_one:now u_two:1day") + self.t( + "add foo project:P +tag priority:H start:now due:eom wait:eom scheduled:eom recur:P1M until:eoy u_one:now u_two:1day" + ) self.t("1 annotate bar", input="n\n") code, out, err = self.t("1 info") @@ -88,7 +91,10 @@ class TestInfoCommand(TestCase): self.assertIn("YEAR", out) self.assertIn("UDA", out) - self.assertRegex(out, r"UUID\s+[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") + self.assertRegex( + out, + r"UUID\s+[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}", + ) self.assertRegex(out, r"Urgency\s+\d+(\.\d+)?") self.assertRegex(out, r"Priority\s+H") @@ -105,6 +111,7 @@ class TestInfoCommand(TestCase): self.assertRegex(out, r"U_ONE\s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}") self.assertRegex(out, r"U_TWO\s+P1D") + class TestBug425(TestCase): def setUp(self): self.t = Task() @@ -120,6 +127,7 @@ class TestBug425(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/lexer.test.cpp b/test/lexer.test.cpp index 43a8af3c0..cac825f17 100644 --- a/test/lexer.test.cpp +++ b/test/lexer.test.cpp @@ -27,574 +27,975 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include #include #include +#include +#include +#include + +#include +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ +int main(int, char**) { #ifdef PRODUCT_TASKWARRIOR - UnitTest t (1255); + UnitTest t(1255); #else - UnitTest t (1235); + UnitTest t(1235); #endif // Use same Datetime/Duraiton configuration as Context∴:staticInitialization. - Datetime::isoEnabled = true; - Datetime::standaloneDateEnabled = false; - Datetime::standaloneTimeEnabled = false; + Datetime::isoEnabled = true; + Datetime::standaloneDateEnabled = false; + Datetime::standaloneTimeEnabled = false; Duration::standaloneSecondsEnabled = false; - std::vector > tokens; + std::vector> tokens; std::string token; Lexer::Type type; // Feed in some attributes and types, so that the Lexer knows what a DOM // reference is. - Lexer::attributes["due"] = "date"; - Lexer::attributes["tags"] = "string"; + Lexer::attributes["due"] = "date"; + Lexer::attributes["tags"] = "string"; Lexer::attributes["description"] = "string"; // static bool Lexer::isBoundary (int, int); - t.ok (Lexer::isBoundary (' ', 'a'), "' ' --> 'a' = isBoundary"); - t.ok (Lexer::isBoundary ('a', ' '), "'a' --> ' ' = isBoundary"); - t.ok (Lexer::isBoundary (' ', '+'), "' ' --> '+' = isBoundary"); - t.ok (Lexer::isBoundary (' ', ','), "' ' --> ',' = isBoundary"); - t.notok (Lexer::isBoundary ('3', '4'), "'3' --> '4' = isBoundary"); - t.ok (Lexer::isBoundary ('(', '('), "'(' --> '(' = isBoundary"); - t.notok (Lexer::isBoundary ('r', 'd'), "'r' --> 'd' = isBoundary"); + t.ok(Lexer::isBoundary(' ', 'a'), "' ' --> 'a' = isBoundary"); + t.ok(Lexer::isBoundary('a', ' '), "'a' --> ' ' = isBoundary"); + t.ok(Lexer::isBoundary(' ', '+'), "' ' --> '+' = isBoundary"); + t.ok(Lexer::isBoundary(' ', ','), "' ' --> ',' = isBoundary"); + t.notok(Lexer::isBoundary('3', '4'), "'3' --> '4' = isBoundary"); + t.ok(Lexer::isBoundary('(', '('), "'(' --> '(' = isBoundary"); + t.notok(Lexer::isBoundary('r', 'd'), "'r' --> 'd' = isBoundary"); // static bool Lexer::wasQuoted (const std::string&); - t.notok (Lexer::wasQuoted (""), "'' --> !wasQuoted"); - t.notok (Lexer::wasQuoted ("foo"), "'foo' --> !wasQuoted"); - t.ok (Lexer::wasQuoted ("a b"), "'a b' --> wasQuoted"); - t.ok (Lexer::wasQuoted ("(a)"), "'(a)' --> wasQuoted"); + t.notok(Lexer::wasQuoted(""), "'' --> !wasQuoted"); + t.notok(Lexer::wasQuoted("foo"), "'foo' --> !wasQuoted"); + t.ok(Lexer::wasQuoted("a b"), "'a b' --> wasQuoted"); + t.ok(Lexer::wasQuoted("(a)"), "'(a)' --> wasQuoted"); // static bool Lexer::dequote (std::string&, const std::string& quotes = "'\""); token = "foo"; - Lexer::dequote (token); - t.is (token, "foo", "dequote foo --> foo"); + Lexer::dequote(token); + t.is(token, "foo", "dequote foo --> foo"); token = "'foo'"; - Lexer::dequote (token); - t.is (token, "foo", "dequote 'foo' --> foo"); + Lexer::dequote(token); + t.is(token, "foo", "dequote 'foo' --> foo"); token = "'o\\'clock'"; - Lexer::dequote (token); - t.is (token, "o\\'clock", "dequote 'o\\'clock' --> o\\'clock"); + Lexer::dequote(token); + t.is(token, "o\\'clock", "dequote 'o\\'clock' --> o\\'clock"); token = "abba"; - Lexer::dequote (token, "a"); - t.is (token, "bb", "dequote 'abba' (a) --> bb"); + Lexer::dequote(token, "a"); + t.is(token, "bb", "dequote 'abba' (a) --> bb"); // Should result in no tokens. - Lexer l0 (""); - t.notok (l0.token (token, type), "'' --> no tokens"); + Lexer l0(""); + t.notok(l0.token(token, type), "'' --> no tokens"); // Should result in no tokens. - Lexer l1 (" \t "); - t.notok (l1.token (token, type), "' \\t ' --> no tokens"); + Lexer l1(" \t "); + t.notok(l1.token(token, type), "' \\t ' --> no tokens"); // \u20ac = Euro symbol. - Lexer l2 (R"( one 'two \'three\''+456-(1.3*2 - 0x12) 1.2e-3.4 foo.bar and '\u20ac')"); + Lexer l2(R"( one 'two \'three\''+456-(1.3*2 - 0x12) 1.2e-3.4 foo.bar and '\u20ac')"); - tokens.clear (); - while (l2.token (token, type)) - { - std::cout << "# «" << token << "» " << Lexer::typeName (type) << "\n"; - tokens.emplace_back (token, type); + tokens.clear(); + while (l2.token(token, type)) { + std::cout << "# «" << token << "» " << Lexer::typeName(type) << "\n"; + tokens.emplace_back(token, type); } - t.is (tokens[0].first, "one", "tokens[0] = 'one'"); // 30 - t.is (Lexer::typeName (tokens[0].second), "identifier", "tokens[0] = identifier"); - t.is (tokens[1].first, "'two 'three''", "tokens[1] = 'two 'three''"); - t.is (Lexer::typeName (tokens[1].second), "string", "tokens[1] = string"); - t.is (tokens[2].first, "+", "tokens[2] = '+'"); - t.is (Lexer::typeName (tokens[2].second), "op", "tokens[2] = op"); - t.is (tokens[3].first, "456", "tokens[3] = '456'"); - t.is (Lexer::typeName (tokens[3].second), "number", "tokens[3] = number"); - t.is (tokens[4].first, "-", "tokens[4] = '-'"); - t.is (Lexer::typeName (tokens[4].second), "op", "tokens[4] = op"); - t.is (tokens[5].first, "(", "tokens[5] = '('"); // 40 - t.is (Lexer::typeName (tokens[5].second), "op", "tokens[5] = op"); - t.is (tokens[6].first, "1.3", "tokens[6] = '1.3'"); - t.is (Lexer::typeName (tokens[6].second), "number", "tokens[6] = number"); - t.is (tokens[7].first, "*", "tokens[7] = '*'"); - t.is (Lexer::typeName (tokens[7].second), "op", "tokens[7] = op"); - t.is (tokens[8].first, "2", "tokens[8] = '2'"); - t.is (Lexer::typeName (tokens[8].second), "number", "tokens[8] = number"); - t.is (tokens[9].first, "-", "tokens[9] = '-'"); - t.is (Lexer::typeName (tokens[9].second), "op", "tokens[9] = op"); - t.is (tokens[10].first, "0x12", "tokens[10] = '0x12'"); // 50 - t.is (Lexer::typeName (tokens[10].second), "hex", "tokens[10] = hex"); - t.is (tokens[11].first, ")", "tokens[11] = ')'"); - t.is (Lexer::typeName (tokens[11].second), "op", "tokens[11] = op"); - t.is (tokens[12].first, "1.2e-3.4", "tokens[12] = '1.2e-3.4'"); - t.is (Lexer::typeName (tokens[12].second), "number", "tokens[12] = number"); - t.is (tokens[13].first, "foo.bar", "tokens[13] = 'foo.bar'"); - t.is (Lexer::typeName (tokens[13].second), "identifier", "tokens[13] = identifier"); - t.is (tokens[14].first, "and", "tokens[14] = 'and'"); // 60 - t.is (Lexer::typeName (tokens[14].second), "op", "tokens[14] = op"); - t.is (tokens[15].first, "'€'", "tokens[15] = \\u20ac --> ''€''"); - t.is (Lexer::typeName (tokens[15].second), "string", "tokens[15] = string"); + t.is(tokens[0].first, "one", "tokens[0] = 'one'"); // 30 + t.is(Lexer::typeName(tokens[0].second), "identifier", "tokens[0] = identifier"); + t.is(tokens[1].first, "'two 'three''", "tokens[1] = 'two 'three''"); + t.is(Lexer::typeName(tokens[1].second), "string", "tokens[1] = string"); + t.is(tokens[2].first, "+", "tokens[2] = '+'"); + t.is(Lexer::typeName(tokens[2].second), "op", "tokens[2] = op"); + t.is(tokens[3].first, "456", "tokens[3] = '456'"); + t.is(Lexer::typeName(tokens[3].second), "number", "tokens[3] = number"); + t.is(tokens[4].first, "-", "tokens[4] = '-'"); + t.is(Lexer::typeName(tokens[4].second), "op", "tokens[4] = op"); + t.is(tokens[5].first, "(", "tokens[5] = '('"); // 40 + t.is(Lexer::typeName(tokens[5].second), "op", "tokens[5] = op"); + t.is(tokens[6].first, "1.3", "tokens[6] = '1.3'"); + t.is(Lexer::typeName(tokens[6].second), "number", "tokens[6] = number"); + t.is(tokens[7].first, "*", "tokens[7] = '*'"); + t.is(Lexer::typeName(tokens[7].second), "op", "tokens[7] = op"); + t.is(tokens[8].first, "2", "tokens[8] = '2'"); + t.is(Lexer::typeName(tokens[8].second), "number", "tokens[8] = number"); + t.is(tokens[9].first, "-", "tokens[9] = '-'"); + t.is(Lexer::typeName(tokens[9].second), "op", "tokens[9] = op"); + t.is(tokens[10].first, "0x12", "tokens[10] = '0x12'"); // 50 + t.is(Lexer::typeName(tokens[10].second), "hex", "tokens[10] = hex"); + t.is(tokens[11].first, ")", "tokens[11] = ')'"); + t.is(Lexer::typeName(tokens[11].second), "op", "tokens[11] = op"); + t.is(tokens[12].first, "1.2e-3.4", "tokens[12] = '1.2e-3.4'"); + t.is(Lexer::typeName(tokens[12].second), "number", "tokens[12] = number"); + t.is(tokens[13].first, "foo.bar", "tokens[13] = 'foo.bar'"); + t.is(Lexer::typeName(tokens[13].second), "identifier", "tokens[13] = identifier"); + t.is(tokens[14].first, "and", "tokens[14] = 'and'"); // 60 + t.is(Lexer::typeName(tokens[14].second), "op", "tokens[14] = op"); + t.is(tokens[15].first, "'€'", "tokens[15] = \\u20ac --> ''€''"); + t.is(Lexer::typeName(tokens[15].second), "string", "tokens[15] = string"); // Test for numbers that are no longer ISO-8601 dates. - Lexer l3 ("1 12 123 1234 12345 123456 1234567"); - tokens.clear (); - while (l3.token (token, type)) - { - std::cout << "# «" << token << "» " << Lexer::typeName (type) << "\n"; - tokens.emplace_back (token, type); + Lexer l3("1 12 123 1234 12345 123456 1234567"); + tokens.clear(); + while (l3.token(token, type)) { + std::cout << "# «" << token << "» " << Lexer::typeName(type) << "\n"; + tokens.emplace_back(token, type); } - t.is ((int)tokens.size (), 7, "7 tokens"); - t.is (tokens[0].first, "1", "tokens[0] == '1'"); - t.is ((int) tokens[0].second, (int) Lexer::Type::number, "tokens[0] == Type::number"); - t.is (tokens[1].first, "12", "tokens[1] == '12'"); - t.is ((int) tokens[1].second, (int) Lexer::Type::number, "tokens[1] == Type::number"); - t.is (tokens[2].first, "123", "tokens[2] == '123'"); - t.is ((int) tokens[2].second, (int) Lexer::Type::number, "tokens[2] == Type::number"); // 70 - t.is (tokens[3].first, "1234", "tokens[3] == '1234'"); - t.is ((int) tokens[3].second, (int) Lexer::Type::number, "tokens[3] == Type::number"); - t.is (tokens[4].first, "12345", "tokens[4] == '12345'"); - t.is ((int) tokens[4].second, (int) Lexer::Type::number, "tokens[4] == Type::number"); - t.is (tokens[5].first, "123456", "tokens[5] == '123456'"); - t.is ((int) tokens[5].second, (int) Lexer::Type::number, "tokens[5] == Type::number"); - t.is (tokens[6].first, "1234567", "tokens[6] == '1234567'"); - t.is ((int) tokens[6].second, (int) Lexer::Type::number, "tokens[6] == Type::number"); + t.is((int)tokens.size(), 7, "7 tokens"); + t.is(tokens[0].first, "1", "tokens[0] == '1'"); + t.is((int)tokens[0].second, (int)Lexer::Type::number, "tokens[0] == Type::number"); + t.is(tokens[1].first, "12", "tokens[1] == '12'"); + t.is((int)tokens[1].second, (int)Lexer::Type::number, "tokens[1] == Type::number"); + t.is(tokens[2].first, "123", "tokens[2] == '123'"); + t.is((int)tokens[2].second, (int)Lexer::Type::number, "tokens[2] == Type::number"); // 70 + t.is(tokens[3].first, "1234", "tokens[3] == '1234'"); + t.is((int)tokens[3].second, (int)Lexer::Type::number, "tokens[3] == Type::number"); + t.is(tokens[4].first, "12345", "tokens[4] == '12345'"); + t.is((int)tokens[4].second, (int)Lexer::Type::number, "tokens[4] == Type::number"); + t.is(tokens[5].first, "123456", "tokens[5] == '123456'"); + t.is((int)tokens[5].second, (int)Lexer::Type::number, "tokens[5] == Type::number"); + t.is(tokens[6].first, "1234567", "tokens[6] == '1234567'"); + t.is((int)tokens[6].second, (int)Lexer::Type::number, "tokens[6] == Type::number"); // void split (std::vector&, const std::string&); std::string unsplit = " ( A or B ) "; - std::vector items; - items = Lexer::split (unsplit); - t.is (items.size (), (size_t) 5, "split ' ( A or B ) '"); - t.is (items[0], "(", "split ' ( A or B ) ' -> [0] '('"); - t.is (items[1], "A", "split ' ( A or B ) ' -> [1] 'A'"); - t.is (items[2], "or", "split ' ( A or B ) ' -> [2] 'or'"); - t.is (items[3], "B", "split ' ( A or B ) ' -> [3] 'B'"); - t.is (items[4], ")", "split ' ( A or B ) ' -> [4] ')'"); + std::vector items; + items = Lexer::split(unsplit); + t.is(items.size(), (size_t)5, "split ' ( A or B ) '"); + t.is(items[0], "(", "split ' ( A or B ) ' -> [0] '('"); + t.is(items[1], "A", "split ' ( A or B ) ' -> [1] 'A'"); + t.is(items[2], "or", "split ' ( A or B ) ' -> [2] 'or'"); + t.is(items[3], "B", "split ' ( A or B ) ' -> [3] 'B'"); + t.is(items[4], ")", "split ' ( A or B ) ' -> [4] ')'"); // Test simple mode with contrived tokens that ordinarily split. unsplit = " +-* a+b 12.3e4 'c d'"; - items = Lexer::split (unsplit); - t.is (items.size (), (size_t) 8, "split ' +-* a+b 12.3e4 'c d''"); - t.is (items[0], "+", "split ' +-* a+b 12.3e4 'c d'' -> [0] '+'"); - t.is (items[1], "-", "split ' +-* a+b 12.3e4 'c d'' -> [1] '-'"); - t.is (items[2], "*", "split ' +-* a+b 12.3e4 'c d'' -> [2] '*'"); - t.is (items[3], "a", "split ' +-* a+b 12.3e4 'c d'' -> [3] 'a'"); - t.is (items[4], "+", "split ' +-* a+b 12.3e4 'c d'' -> [4] '+'"); - t.is (items[5], "b", "split ' +-* a+b 12.3e4 'c d'' -> [5] 'b'"); - t.is (items[6], "12.3e4", "split ' +-* a+b 12.3e4 'c d'' -> [6] '12.3e4'"); - t.is (items[7], "'c d'", "split ' +-* a+b 12.3e4 'c d'' -> [7] ''c d''"); + items = Lexer::split(unsplit); + t.is(items.size(), (size_t)8, "split ' +-* a+b 12.3e4 'c d''"); + t.is(items[0], "+", "split ' +-* a+b 12.3e4 'c d'' -> [0] '+'"); + t.is(items[1], "-", "split ' +-* a+b 12.3e4 'c d'' -> [1] '-'"); + t.is(items[2], "*", "split ' +-* a+b 12.3e4 'c d'' -> [2] '*'"); + t.is(items[3], "a", "split ' +-* a+b 12.3e4 'c d'' -> [3] 'a'"); + t.is(items[4], "+", "split ' +-* a+b 12.3e4 'c d'' -> [4] '+'"); + t.is(items[5], "b", "split ' +-* a+b 12.3e4 'c d'' -> [5] 'b'"); + t.is(items[6], "12.3e4", "split ' +-* a+b 12.3e4 'c d'' -> [6] '12.3e4'"); + t.is(items[7], "'c d'", "split ' +-* a+b 12.3e4 'c d'' -> [7] ''c d''"); - // static bool decomposePair (const std::string&, std::string&, std::string&, std::string&, std::string&); - // 2 * 4 * 2 * 5 = 80 tests. + // static bool decomposePair (const std::string&, std::string&, std::string&, std::string&, + // std::string&); 2 * 4 * 2 * 5 = 80 tests. std::string outName, outMod, outValue, outSep; - for (auto& name : {"name"}) - { - for (auto& mod : {"", "mod"}) - { - for (auto& sep : {":", "=", "::", ":="}) - { - for (auto& value : {"", "value", "a:b", "a::b", "a=b", "a:=b"}) - { - std::string input = std::string ("name") + (strlen (mod) ? "." : "") + mod + sep + value; - t.ok (Lexer::decomposePair (input, outName, outMod, outSep, outValue), "decomposePair '" + input + "' --> true"); - t.is (name, outName, " '" + input + "' --> name '" + name + "'"); - t.is (mod, outMod, " '" + input + "' --> mod '" + mod + "'"); - t.is (value, outValue, " '" + input + "' --> value '" + value + "'"); - t.is (sep, outSep, " '" + input + "' --> sep '" + sep + "'"); + for (auto& name : {"name"}) { + for (auto& mod : {"", "mod"}) { + for (auto& sep : {":", "=", "::", ":="}) { + for (auto& value : {"", "value", "a:b", "a::b", "a=b", "a:=b"}) { + std::string input = std::string("name") + (strlen(mod) ? "." : "") + mod + sep + value; + t.ok(Lexer::decomposePair(input, outName, outMod, outSep, outValue), + "decomposePair '" + input + "' --> true"); + t.is(name, outName, " '" + input + "' --> name '" + name + "'"); + t.is(mod, outMod, " '" + input + "' --> mod '" + mod + "'"); + t.is(value, outValue, " '" + input + "' --> value '" + value + "'"); + t.is(sep, outSep, " '" + input + "' --> sep '" + sep + "'"); } } } } - // static bool readWord (const std::string&, const std::string&, std::string::size_type&, std::string&); + // static bool readWord (const std::string&, const std::string&, std::string::size_type&, + // std::string&); std::string::size_type cursor = 0; std::string word; - t.ok (Lexer::readWord ("'one two'", "'\"", cursor, word), "readWord ''one two'' --> true"); - t.is (word, "'one two'", " word '" + word + "'"); - t.is ((int)cursor, 9, " cursor"); + t.ok(Lexer::readWord("'one two'", "'\"", cursor, word), "readWord ''one two'' --> true"); + t.is(word, "'one two'", " word '" + word + "'"); + t.is((int)cursor, 9, " cursor"); // Unterminated quoted string is invalid. cursor = 0; - t.notok (Lexer::readWord ("'one", "'\"", cursor, word), "readWord ''one' --> false"); + t.notok(Lexer::readWord("'one", "'\"", cursor, word), "readWord ''one' --> false"); // static bool readWord (const std::string&, std::string::size_type&, std::string&); cursor = 0; - t.ok (Lexer::readWord ("input", cursor, word), "readWord 'input' --> true"); - t.is (word, "input", " word '" + word + "'"); - t.is ((int)cursor, 5, " cursor"); + t.ok(Lexer::readWord("input", cursor, word), "readWord 'input' --> true"); + t.is(word, "input", " word '" + word + "'"); + t.is((int)cursor, 5, " cursor"); cursor = 0; - t.ok (Lexer::readWord ("one\\ two", cursor, word), "readWord 'one\\ two' --> true"); - t.is (word, "one two", " word '" + word + "'"); - t.is ((int)cursor, 8, " cursor"); + t.ok(Lexer::readWord("one\\ two", cursor, word), "readWord 'one\\ two' --> true"); + t.is(word, "one two", " word '" + word + "'"); + t.is((int)cursor, 8, " cursor"); cursor = 0; - t.ok (Lexer::readWord ("\\u20A43", cursor, word), "readWord '\\u20A43' --> true"); - t.is (word, "₤3", " word '" + word + "'"); - t.is ((int)cursor, 7, " cursor"); + t.ok(Lexer::readWord("\\u20A43", cursor, word), "readWord '\\u20A43' --> true"); + t.is(word, "₤3", " word '" + word + "'"); + t.is((int)cursor, 7, " cursor"); cursor = 0; - t.ok (Lexer::readWord ("U+20AC4", cursor, word), "readWord '\\u20AC4' --> true"); - t.is (word, "€4", " word '" + word + "'"); - t.is ((int)cursor, 7, " cursor"); + t.ok(Lexer::readWord("U+20AC4", cursor, word), "readWord '\\u20AC4' --> true"); + t.is(word, "€4", " word '" + word + "'"); + t.is((int)cursor, 7, " cursor"); std::string text = "one 'two' three\\ four"; cursor = 0; - t.ok (Lexer::readWord (text, cursor, word), R"(readWord "one 'two' three\ four" --> true)"); - t.is (word, "one", " word '" + word + "'"); + t.ok(Lexer::readWord(text, cursor, word), R"(readWord "one 'two' three\ four" --> true)"); + t.is(word, "one", " word '" + word + "'"); cursor++; - t.ok (Lexer::readWord (text, cursor, word), R"(readWord "one 'two' three\ four" --> true)"); - t.is (word, "'two'", " word '" + word + "'"); + t.ok(Lexer::readWord(text, cursor, word), R"(readWord "one 'two' three\ four" --> true)"); + t.is(word, "'two'", " word '" + word + "'"); cursor++; - t.ok (Lexer::readWord (text, cursor, word), R"(readWord "one 'two' three\ four" --> true)"); - t.is (word, "three four", " word '" + word + "'"); + t.ok(Lexer::readWord(text, cursor, word), R"(readWord "one 'two' three\ four" --> true)"); + t.is(word, "three four", " word '" + word + "'"); text = "one "; cursor = 0; - t.ok (Lexer::readWord (text, cursor, word), "readWord \"one \" --> true"); - t.is (word, "one", " word '" + word + "'"); + t.ok(Lexer::readWord(text, cursor, word), "readWord \"one \" --> true"); + t.is(word, "one", " word '" + word + "'"); // bool isLiteral (const std::string&, bool, bool); - Lexer l4 ("one.two"); - t.notok (l4.isLiteral("zero", false, false), "isLiteral 'one.two' --> false"); - t.ok (l4.isLiteral("one", false, false), "isLiteral 'one.two' --> 'one'"); - t.ok (l4.isLiteral(".", false, false), "isLiteral 'one.two' --> '.'"); - t.ok (l4.isLiteral("two", false, true), "isLiteral 'one.two' --> 'two'"); + Lexer l4("one.two"); + t.notok(l4.isLiteral("zero", false, false), "isLiteral 'one.two' --> false"); + t.ok(l4.isLiteral("one", false, false), "isLiteral 'one.two' --> 'one'"); + t.ok(l4.isLiteral(".", false, false), "isLiteral 'one.two' --> '.'"); + t.ok(l4.isLiteral("two", false, true), "isLiteral 'one.two' --> 'two'"); - Lexer l5 ("wonder"); - t.notok (l5.isLiteral ("wonderful", false, false), "isLiteral 'wonderful' != 'wonder' without abbreviation"); - t.ok (l5.isLiteral ("wonderful", true, false), "isLiteral 'wonderful' == 'wonder' with abbreviation"); + Lexer l5("wonder"); + t.notok(l5.isLiteral("wonderful", false, false), + "isLiteral 'wonderful' != 'wonder' without abbreviation"); + t.ok(l5.isLiteral("wonderful", true, false), + "isLiteral 'wonderful' == 'wonder' with abbreviation"); // bool isOneOf (const std::string&, bool, bool); - Lexer l6 ("Grumpy."); - std::vector dwarves = {"Sneezy", "Doc", "Bashful", "Grumpy", "Happy", "Sleepy", "Dopey"}; - t.notok (l6.isOneOf (dwarves, false, true), "isOneof ('Grumpy', true) --> false"); - t.ok (l6.isOneOf (dwarves, false, false), "isOneOf ('Grumpy', false) --> true"); + Lexer l6("Grumpy."); + std::vector dwarves = {"Sneezy", "Doc", "Bashful", "Grumpy", + "Happy", "Sleepy", "Dopey"}; + t.notok(l6.isOneOf(dwarves, false, true), "isOneof ('Grumpy', true) --> false"); + t.ok(l6.isOneOf(dwarves, false, false), "isOneOf ('Grumpy', false) --> true"); // static std::string::size_type commonLength (const std::string&, const std::string&); - t.is ((int)Lexer::commonLength ("", ""), 0, "commonLength '' : '' --> 0"); - t.is ((int)Lexer::commonLength ("a", "a"), 1, "commonLength 'a' : 'a' --> 1"); - t.is ((int)Lexer::commonLength ("abcde", "abcde"), 5, "commonLength 'abcde' : 'abcde' --> 5"); - t.is ((int)Lexer::commonLength ("abc", ""), 0, "commonLength 'abc' : '' --> 0"); - t.is ((int)Lexer::commonLength ("abc", "def"), 0, "commonLength 'abc' : 'def' --> 0"); - t.is ((int)Lexer::commonLength ("foobar", "foo"), 3, "commonLength 'foobar' : 'foo' --> 3"); - t.is ((int)Lexer::commonLength ("foo", "foobar"), 3, "commonLength 'foo' : 'foobar' --> 3"); + t.is((int)Lexer::commonLength("", ""), 0, "commonLength '' : '' --> 0"); + t.is((int)Lexer::commonLength("a", "a"), 1, "commonLength 'a' : 'a' --> 1"); + t.is((int)Lexer::commonLength("abcde", "abcde"), 5, "commonLength 'abcde' : 'abcde' --> 5"); + t.is((int)Lexer::commonLength("abc", ""), 0, "commonLength 'abc' : '' --> 0"); + t.is((int)Lexer::commonLength("abc", "def"), 0, "commonLength 'abc' : 'def' --> 0"); + t.is((int)Lexer::commonLength("foobar", "foo"), 3, "commonLength 'foobar' : 'foo' --> 3"); + t.is((int)Lexer::commonLength("foo", "foobar"), 3, "commonLength 'foo' : 'foobar' --> 3"); - // static std::string::size_type commonLength (const std::string&, std::string::size_type, const std::string&, std::string::size_type); - t.is ((int)Lexer::commonLength ("wonder", 0, "prowonderbread", 3), 6, "'wonder'+0 : 'prowonderbread'+3 --> 6"); + // static std::string::size_type commonLength (const std::string&, std::string::size_type, const + // std::string&, std::string::size_type); + t.is((int)Lexer::commonLength("wonder", 0, "prowonderbread", 3), 6, + "'wonder'+0 : 'prowonderbread'+3 --> 6"); - // Test all Lexer types. - #define NO {"",Lexer::Type::word} - struct - { +// Test all Lexer types. +#define NO {"", Lexer::Type::word} + struct { const char* input; - struct - { + struct { const char* token; Lexer::Type type; bool expfail_token = false; bool expfail_type = false; } results[5]; - } lexerTests[] = - { - // Pattern - { "/foo/", { { "/foo/", Lexer::Type::pattern }, NO, NO, NO, NO }, }, - { "/a\\/b/", { { "/a\\/b/", Lexer::Type::pattern }, NO, NO, NO, NO }, }, - { "/'/", { { "/'/", Lexer::Type::pattern }, NO, NO, NO, NO }, }, + } lexerTests[] = { + // Pattern + { + "/foo/", + {{"/foo/", Lexer::Type::pattern}, NO, NO, NO, NO}, + }, + { + "/a\\/b/", + {{"/a\\/b/", Lexer::Type::pattern}, NO, NO, NO, NO}, + }, + { + "/'/", + {{"/'/", Lexer::Type::pattern}, NO, NO, NO, NO}, + }, - // Substitution - { "/from/to/g", { { "/from/to/g", Lexer::Type::substitution }, NO, NO, NO, NO }, }, - { "/from/to/", { { "/from/to/", Lexer::Type::substitution }, NO, NO, NO, NO }, }, + // Substitution + { + "/from/to/g", + {{"/from/to/g", Lexer::Type::substitution}, NO, NO, NO, NO}, + }, + { + "/from/to/", + {{"/from/to/", Lexer::Type::substitution}, NO, NO, NO, NO}, + }, - // Tag - { "+tag", { { "+tag", Lexer::Type::tag }, NO, NO, NO, NO }, }, - { "-tag", { { "-tag", Lexer::Type::tag }, NO, NO, NO, NO }, }, - { "+@tag", { { "+@tag", Lexer::Type::tag }, NO, NO, NO, NO }, }, + // Tag + { + "+tag", + {{"+tag", Lexer::Type::tag}, NO, NO, NO, NO}, + }, + { + "-tag", + {{"-tag", Lexer::Type::tag}, NO, NO, NO, NO}, + }, + { + "+@tag", + {{"+@tag", Lexer::Type::tag}, NO, NO, NO, NO}, + }, - // Path - { "/long/path/to/file.txt", { { "/long/path/to/file.txt", Lexer::Type::path }, NO, NO, NO, NO }, }, + // Path + { + "/long/path/to/file.txt", + {{"/long/path/to/file.txt", Lexer::Type::path}, NO, NO, NO, NO}, + }, - // Word - { "1.foo.bar", { { "1.foo.bar", Lexer::Type::word }, NO, NO, NO, NO }, }, + // Word + { + "1.foo.bar", + {{"1.foo.bar", Lexer::Type::word}, NO, NO, NO, NO}, + }, - // Identifier - { "foo", { { "foo", Lexer::Type::identifier }, NO, NO, NO, NO }, }, - { "Çirçös", { { "Çirçös", Lexer::Type::identifier }, NO, NO, NO, NO }, }, - { "☺", { { "☺", Lexer::Type::identifier }, NO, NO, NO, NO }, }, - { "name", { { "name", Lexer::Type::identifier }, NO, NO, NO, NO }, }, - { "f1", { { "f1", Lexer::Type::identifier }, NO, NO, NO, NO }, }, - { "foo.bar", { { "foo.bar", Lexer::Type::identifier }, NO, NO, NO, NO }, }, - { "a1a1a1a1_a1a1_a1a1_a1a1_a1a1a1a1a1a1", { { "a1a1a1a1_a1a1_a1a1_a1a1_a1a1a1a1a1a1", Lexer::Type::identifier }, NO, NO, NO, NO }, }, + // Identifier + { + "foo", + {{"foo", Lexer::Type::identifier}, NO, NO, NO, NO}, + }, + { + "Çirçös", + {{"Çirçös", Lexer::Type::identifier}, NO, NO, NO, NO}, + }, + { + "☺", + {{"☺", Lexer::Type::identifier}, NO, NO, NO, NO}, + }, + { + "name", + {{"name", Lexer::Type::identifier}, NO, NO, NO, NO}, + }, + { + "f1", + {{"f1", Lexer::Type::identifier}, NO, NO, NO, NO}, + }, + { + "foo.bar", + {{"foo.bar", Lexer::Type::identifier}, NO, NO, NO, NO}, + }, + { + "a1a1a1a1_a1a1_a1a1_a1a1_a1a1a1a1a1a1", + {{"a1a1a1a1_a1a1_a1a1_a1a1_a1a1a1a1a1a1", Lexer::Type::identifier}, NO, NO, NO, NO}, + }, - // Word that starts wih 'or', which is an operator, but should be ignored. - { "ordinary", { { "ordinary", Lexer::Type::identifier }, NO, NO, NO, NO }, }, + // Word that starts wih 'or', which is an operator, but should be ignored. + { + "ordinary", + {{"ordinary", Lexer::Type::identifier}, NO, NO, NO, NO}, + }, - // DOM - { "due", { { "due", Lexer::Type::dom }, NO, NO, NO, NO }, }, - { "123.tags", { { "123.tags", Lexer::Type::dom }, NO, NO, NO, NO }, }, - { "123.tags.PENDING", { { "123.tags.PENDING", Lexer::Type::dom }, NO, NO, NO, NO }, }, - { "123.description", { { "123.description", Lexer::Type::dom }, NO, NO, NO, NO }, }, - { "123.annotations.1.description", { { "123.annotations.1.description", Lexer::Type::dom }, NO, NO, NO, NO }, }, - { "123.annotations.1.entry", { { "123.annotations.1.entry", Lexer::Type::dom }, NO, NO, NO, NO }, }, - { "123.annotations.1.entry.year", { { "123.annotations.1.entry.year", Lexer::Type::dom }, NO, NO, NO, NO }, }, - { "a360fc44-315c-4366-b70c-ea7e7520b749.due", { { "a360fc44-315c-4366-b70c-ea7e7520b749.due", Lexer::Type::dom }, NO, NO, NO, NO }, }, - { "12345678-1234-1234-1234-123456789012.due", { { "12345678-1234-1234-1234-123456789012.due", Lexer::Type::dom }, NO, NO, NO, NO }, }, - { "system.os", { { "system.os", Lexer::Type::dom }, NO, NO, NO, NO }, }, - { "rc.foo", { { "rc.foo", Lexer::Type::dom }, NO, NO, NO, NO }, }, + // DOM + { + "due", + {{"due", Lexer::Type::dom}, NO, NO, NO, NO}, + }, + { + "123.tags", + {{"123.tags", Lexer::Type::dom}, NO, NO, NO, NO}, + }, + { + "123.tags.PENDING", + {{"123.tags.PENDING", Lexer::Type::dom}, NO, NO, NO, NO}, + }, + { + "123.description", + {{"123.description", Lexer::Type::dom}, NO, NO, NO, NO}, + }, + { + "123.annotations.1.description", + {{"123.annotations.1.description", Lexer::Type::dom}, NO, NO, NO, NO}, + }, + { + "123.annotations.1.entry", + {{"123.annotations.1.entry", Lexer::Type::dom}, NO, NO, NO, NO}, + }, + { + "123.annotations.1.entry.year", + {{"123.annotations.1.entry.year", Lexer::Type::dom}, NO, NO, NO, NO}, + }, + { + "a360fc44-315c-4366-b70c-ea7e7520b749.due", + {{"a360fc44-315c-4366-b70c-ea7e7520b749.due", Lexer::Type::dom}, NO, NO, NO, NO}, + }, + { + "12345678-1234-1234-1234-123456789012.due", + {{"12345678-1234-1234-1234-123456789012.due", Lexer::Type::dom}, NO, NO, NO, NO}, + }, + { + "system.os", + {{"system.os", Lexer::Type::dom}, NO, NO, NO, NO}, + }, + { + "rc.foo", + {{"rc.foo", Lexer::Type::dom}, NO, NO, NO, NO}, + }, - // URL - { "http://example.com", { { "http://example.com", Lexer::Type::url }, NO, NO, NO, NO }, }, - { "https://foo.example.com", { { "https://foo.example.com", Lexer::Type::url }, NO, NO, NO, NO }, }, + // URL + { + "http://example.com", + {{"http://example.com", Lexer::Type::url}, NO, NO, NO, NO}, + }, + { + "https://foo.example.com", + {{"https://foo.example.com", Lexer::Type::url}, NO, NO, NO, NO}, + }, - // String - { "'one two'", { { "'one two'", Lexer::Type::string }, NO, NO, NO, NO }, }, - { "\"three\"", { { "\"three\"", Lexer::Type::string }, NO, NO, NO, NO }, }, - { "'\\''", { { "'''", Lexer::Type::string }, NO, NO, NO, NO }, }, - {R"("\"")", { {R"(""")", Lexer::Type::string }, NO, NO, NO, NO }, }, - { "\"\tfoo\t\"", { { "\"\tfoo\t\"", Lexer::Type::string }, NO, NO, NO, NO }, }, - {R"("\u20A43")", { { "\"₤3\"", Lexer::Type::string }, NO, NO, NO, NO }, }, - { "\"U+20AC4\"", { { "\"€4\"", Lexer::Type::string }, NO, NO, NO, NO }, }, + // String + { + "'one two'", + {{"'one two'", Lexer::Type::string}, NO, NO, NO, NO}, + }, + { + "\"three\"", + {{"\"three\"", Lexer::Type::string}, NO, NO, NO, NO}, + }, + { + "'\\''", + {{"'''", Lexer::Type::string}, NO, NO, NO, NO}, + }, + { + R"("\"")", + {{R"(""")", Lexer::Type::string}, NO, NO, NO, NO}, + }, + { + "\"\tfoo\t\"", + {{"\"\tfoo\t\"", Lexer::Type::string}, NO, NO, NO, NO}, + }, + { + R"("\u20A43")", + {{"\"₤3\"", Lexer::Type::string}, NO, NO, NO, NO}, + }, + { + "\"U+20AC4\"", + {{"\"€4\"", Lexer::Type::string}, NO, NO, NO, NO}, + }, - // Number - { "1", { { "1", Lexer::Type::number }, NO, NO, NO, NO }, }, - { "3.14", { { "3.14", Lexer::Type::number }, NO, NO, NO, NO }, }, - { "6.02217e23", { { "6.02217e23", Lexer::Type::number }, NO, NO, NO, NO }, }, - { "1.2e-3.4", { { "1.2e-3.4", Lexer::Type::number }, NO, NO, NO, NO }, }, - { "0x2f", { { "0x2f", Lexer::Type::hex }, NO, NO, NO, NO }, }, + // Number + { + "1", + {{"1", Lexer::Type::number}, NO, NO, NO, NO}, + }, + { + "3.14", + {{"3.14", Lexer::Type::number}, NO, NO, NO, NO}, + }, + { + "6.02217e23", + {{"6.02217e23", Lexer::Type::number}, NO, NO, NO, NO}, + }, + { + "1.2e-3.4", + {{"1.2e-3.4", Lexer::Type::number}, NO, NO, NO, NO}, + }, + { + "0x2f", + {{"0x2f", Lexer::Type::hex}, NO, NO, NO, NO}, + }, - // Set (1,2,4-7,9) - { "1,2", { { "1,2", Lexer::Type::set }, NO, NO, NO, NO }, }, - { "1-2", { { "1-2", Lexer::Type::set }, NO, NO, NO, NO }, }, - { "1-2,4", { { "1-2,4", Lexer::Type::set }, NO, NO, NO, NO }, }, - { "1-2,4,6-8", { { "1-2,4,6-8", Lexer::Type::set }, NO, NO, NO, NO }, }, - { "1-2,4,6-8,10-12", { { "1-2,4,6-8,10-12", Lexer::Type::set }, NO, NO, NO, NO }, }, + // Set (1,2,4-7,9) + { + "1,2", + {{"1,2", Lexer::Type::set}, NO, NO, NO, NO}, + }, + { + "1-2", + {{"1-2", Lexer::Type::set}, NO, NO, NO, NO}, + }, + { + "1-2,4", + {{"1-2,4", Lexer::Type::set}, NO, NO, NO, NO}, + }, + { + "1-2,4,6-8", + {{"1-2,4,6-8", Lexer::Type::set}, NO, NO, NO, NO}, + }, + { + "1-2,4,6-8,10-12", + {{"1-2,4,6-8,10-12", Lexer::Type::set}, NO, NO, NO, NO}, + }, - // Pair - { "name:value", { { "name:value", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "name=value", { { "name=value", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "name:=value", { { "name:=value", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "name.mod:value", { { "name.mod:value", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "name.mod=value", { { "name.mod=value", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "name:", { { "name:", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "name=", { { "name=", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "name.mod:", { { "name.mod:", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "name.mod=", { { "name.mod=", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "pro:'P 1'", { { "pro:'P 1'", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "rc:x", { { "rc:x", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "rc.name:value", { { "rc.name:value", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "rc.name=value", { { "rc.name=value", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "rc.name:=value", { { "rc.name:=value", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "due:='eow - 2d'", { { "due:='eow - 2d'", Lexer::Type::pair }, NO, NO, NO, NO }, }, - { "name:'foo\nbar'", { { "name:'foo\nbar'", Lexer::Type::pair }, NO, NO, NO, NO }, }, + // Pair + { + "name:value", + {{"name:value", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "name=value", + {{"name=value", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "name:=value", + {{"name:=value", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "name.mod:value", + {{"name.mod:value", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "name.mod=value", + {{"name.mod=value", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "name:", + {{"name:", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "name=", + {{"name=", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "name.mod:", + {{"name.mod:", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "name.mod=", + {{"name.mod=", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "pro:'P 1'", + {{"pro:'P 1'", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "rc:x", + {{"rc:x", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "rc.name:value", + {{"rc.name:value", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "rc.name=value", + {{"rc.name=value", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "rc.name:=value", + {{"rc.name:=value", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "due:='eow - 2d'", + {{"due:='eow - 2d'", Lexer::Type::pair}, NO, NO, NO, NO}, + }, + { + "name:'foo\nbar'", + {{"name:'foo\nbar'", Lexer::Type::pair}, NO, NO, NO, NO}, + }, - // Operator - complete set - { "^", { { "^", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "!", { { "!", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "_neg_", { { "_neg_", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "_pos_", { { "_pos_", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "_hastag_", { { "_hastag_", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "_notag_", { { "_notag_", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "*", { { "*", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "/", { { "/", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "%", { { "%", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "+", { { "+", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "-", { { "-", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "<=", { { "<=", Lexer::Type::op }, NO, NO, NO, NO }, }, - { ">=", { { ">=", Lexer::Type::op }, NO, NO, NO, NO }, }, - { ">", { { ">", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "<", { { "<", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "=", { { "=", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "==", { { "==", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "!=", { { "!=", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "!==", { { "!==", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "~", { { "~", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "!~", { { "!~", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "and", { { "and", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "or", { { "or", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "xor", { { "xor", Lexer::Type::op }, NO, NO, NO, NO }, }, - { "(", { { "(", Lexer::Type::op }, NO, NO, NO, NO }, }, - { ")", { { ")", Lexer::Type::op }, NO, NO, NO, NO }, }, + // Operator - complete set + { + "^", + {{"^", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "!", + {{"!", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "_neg_", + {{"_neg_", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "_pos_", + {{"_pos_", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "_hastag_", + {{"_hastag_", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "_notag_", + {{"_notag_", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "*", + {{"*", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "/", + {{"/", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "%", + {{"%", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "+", + {{"+", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "-", + {{"-", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "<=", + {{"<=", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + ">=", + {{">=", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + ">", + {{">", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "<", + {{"<", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "=", + {{"=", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "==", + {{"==", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "!=", + {{"!=", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "!==", + {{"!==", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "~", + {{"~", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "!~", + {{"!~", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "and", + {{"and", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "or", + {{"or", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "xor", + {{"xor", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + "(", + {{"(", Lexer::Type::op}, NO, NO, NO, NO}, + }, + { + ")", + {{")", Lexer::Type::op}, NO, NO, NO, NO}, + }, - // UUID - { "ffffffff-ffff-ffff-ffff-ffffffffffff", { { "ffffffff-ffff-ffff-ffff-ffffffffffff", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "0000000d-0000-0000-0000-000000000000", { { "0000000d-0000-0000-0000-000000000000", Lexer::Type::uuid, true, true }, NO, NO, NO, NO }, }, - { "00000000-0000-0000-0000-0000000", { { "00000000-0000-0000-0000-0000000", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "00000000-0000-0000-0000", { { "00000000-0000-0000-0000", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "00000000-0000-0000", { { "00000000-0000-0000", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "00000000-0000", { { "00000000-0000", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "00000000", { { "00000000", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "a360fc44-315c-4366-b70c-ea7e7520b749", { { "a360fc44-315c-4366-b70c-ea7e7520b749", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "a360fc44-315c-4366-b70c-ea7e752", { { "a360fc44-315c-4366-b70c-ea7e752", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "a360fc44-315c-4366-b70c", { { "a360fc44-315c-4366-b70c", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "a360fc44-315c-4366", { { "a360fc44-315c-4366", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "a360fc44-315c", { { "a360fc44-315c", Lexer::Type::uuid }, NO, NO, NO, NO }, }, - { "a360fc44", { { "a360fc44", Lexer::Type::uuid }, NO, NO, NO, NO }, }, + // UUID + { + "ffffffff-ffff-ffff-ffff-ffffffffffff", + {{"ffffffff-ffff-ffff-ffff-ffffffffffff", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "0000000d-0000-0000-0000-000000000000", + {{"0000000d-0000-0000-0000-000000000000", Lexer::Type::uuid, true, true}, NO, NO, NO, NO}, + }, + { + "00000000-0000-0000-0000-0000000", + {{"00000000-0000-0000-0000-0000000", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "00000000-0000-0000-0000", + {{"00000000-0000-0000-0000", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "00000000-0000-0000", + {{"00000000-0000-0000", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "00000000-0000", + {{"00000000-0000", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "00000000", + {{"00000000", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "a360fc44-315c-4366-b70c-ea7e7520b749", + {{"a360fc44-315c-4366-b70c-ea7e7520b749", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "a360fc44-315c-4366-b70c-ea7e752", + {{"a360fc44-315c-4366-b70c-ea7e752", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "a360fc44-315c-4366-b70c", + {{"a360fc44-315c-4366-b70c", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "a360fc44-315c-4366", + {{"a360fc44-315c-4366", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "a360fc44-315c", + {{"a360fc44-315c", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, + { + "a360fc44", + {{"a360fc44", Lexer::Type::uuid}, NO, NO, NO, NO}, + }, - // Date - { "2015-W01", { { "2015-W01", Lexer::Type::date }, NO, NO, NO, NO }, }, - { "2015-02-17", { { "2015-02-17", Lexer::Type::date }, NO, NO, NO, NO }, }, - { "2013-11-29T22:58:00Z", { { "2013-11-29T22:58:00Z", Lexer::Type::date }, NO, NO, NO, NO }, }, - { "20131129T225800Z", { { "20131129T225800Z", Lexer::Type::date }, NO, NO, NO, NO }, }, + // Date + { + "2015-W01", + {{"2015-W01", Lexer::Type::date}, NO, NO, NO, NO}, + }, + { + "2015-02-17", + {{"2015-02-17", Lexer::Type::date}, NO, NO, NO, NO}, + }, + { + "2013-11-29T22:58:00Z", + {{"2013-11-29T22:58:00Z", Lexer::Type::date}, NO, NO, NO, NO}, + }, + { + "20131129T225800Z", + {{"20131129T225800Z", Lexer::Type::date}, NO, NO, NO, NO}, + }, #ifdef PRODUCT_TASKWARRIOR - { "9th", { { "9th", Lexer::Type::date }, NO, NO, NO, NO }, }, - { "10th", { { "10th", Lexer::Type::date }, NO, NO, NO, NO }, }, - { "today", { { "today", Lexer::Type::date }, NO, NO, NO, NO }, }, + { + "9th", + {{"9th", Lexer::Type::date}, NO, NO, NO, NO}, + }, + { + "10th", + {{"10th", Lexer::Type::date}, NO, NO, NO, NO}, + }, + { + "today", + {{"today", Lexer::Type::date}, NO, NO, NO, NO}, + }, #endif - // Duration - { "year", { { "year", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "4weeks", { { "4weeks", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "PT23H", { { "PT23H", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "1second", { { "1second", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "1s", { { "1s", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "1minute", { { "1minute", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "2hour", { { "2hour", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "3 days", { { "3 days", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "4w", { { "4w", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "5mo", { { "5mo", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "6 years", { { "6 years", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "P1Y", { { "P1Y", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "PT1H", { { "PT1H", Lexer::Type::duration }, NO, NO, NO, NO }, }, - { "P1Y1M1DT1H1M1S", { { "P1Y1M1DT1H1M1S", Lexer::Type::duration }, NO, NO, NO, NO }, }, + // Duration + { + "year", + {{"year", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "4weeks", + {{"4weeks", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "PT23H", + {{"PT23H", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "1second", + {{"1second", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "1s", + {{"1s", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "1minute", + {{"1minute", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "2hour", + {{"2hour", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "3 days", + {{"3 days", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "4w", + {{"4w", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "5mo", + {{"5mo", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "6 years", + {{"6 years", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "P1Y", + {{"P1Y", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "PT1H", + {{"PT1H", Lexer::Type::duration}, NO, NO, NO, NO}, + }, + { + "P1Y1M1DT1H1M1S", + {{"P1Y1M1DT1H1M1S", Lexer::Type::duration}, NO, NO, NO, NO}, + }, - // Misc - { "--", { { "--", Lexer::Type::separator }, NO, NO, NO, NO }, }, + // Misc + { + "--", + {{"--", Lexer::Type::separator}, NO, NO, NO, NO}, + }, - // Expression - // due:eom-2w - // due < eom + 1w + 1d - // ( /pattern/ or 8ad2e3db-914d-4832-b0e6-72fa04f6e331,3b6218f9-726a-44fc-aa63-889ff52be442 ) - { "(1+2)", { { "(", Lexer::Type::op }, - { "1", Lexer::Type::number }, - { "+", Lexer::Type::op }, - { "2", Lexer::Type::number }, - { ")", Lexer::Type::op }, }, }, - { "description~pattern", { { "description", Lexer::Type::dom }, - { "~", Lexer::Type::op }, - { "pattern", Lexer::Type::identifier }, NO, NO }, }, - { "(+tag)", { { "(", Lexer::Type::op }, - { "+tag", Lexer::Type::tag }, - { ")", Lexer::Type::op }, NO, NO }, }, - { "(name:value)", { { "(", Lexer::Type::op }, - { "name:value", Lexer::Type::pair }, - { ")", Lexer::Type::op }, NO, NO }, }, + // Expression + // due:eom-2w + // due < eom + 1w + 1d + // ( /pattern/ or 8ad2e3db-914d-4832-b0e6-72fa04f6e331,3b6218f9-726a-44fc-aa63-889ff52be442 + // ) + { + "(1+2)", + { + {"(", Lexer::Type::op}, + {"1", Lexer::Type::number}, + {"+", Lexer::Type::op}, + {"2", Lexer::Type::number}, + {")", Lexer::Type::op}, + }, + }, + { + "description~pattern", + {{"description", Lexer::Type::dom}, + {"~", Lexer::Type::op}, + {"pattern", Lexer::Type::identifier}, + NO, + NO}, + }, + { + "(+tag)", + {{"(", Lexer::Type::op}, {"+tag", Lexer::Type::tag}, {")", Lexer::Type::op}, NO, NO}, + }, + { + "(name:value)", + {{"(", Lexer::Type::op}, + {"name:value", Lexer::Type::pair}, + {")", Lexer::Type::op}, + NO, + NO}, + }, }; - for (const auto& lexerTest : lexerTests) - { + for (const auto& lexerTest : lexerTests) { // The isolated test puts the input string directly into the Lexer. - Lexer isolated (lexerTest.input); + Lexer isolated(lexerTest.input); - for (const auto& result : lexerTest.results) - { - if (result.token[0]) - { + for (const auto& result : lexerTest.results) { + if (result.token[0]) { // Isolated: "" - t.ok (isolated.token (token, type), "Isolated Lexer::token(...) --> true"); - t.is (token, result.token, " token --> " + token, result.expfail_token); - t.is ((int)type, (int)result.type, " type --> Lexer::Type::" + Lexer::typeToString (type), result.expfail_type); + t.ok(isolated.token(token, type), "Isolated Lexer::token(...) --> true"); + t.is(token, result.token, " token --> " + token, result.expfail_token); + t.is((int)type, (int)result.type, " type --> Lexer::Type::" + Lexer::typeToString(type), + result.expfail_type); } } // The embedded test surrounds the input string with a space. - Lexer embedded (std::string (" ") + lexerTest.input + " "); + Lexer embedded(std::string(" ") + lexerTest.input + " "); - for (const auto& result : lexerTest.results) - { - if (result.token[0]) - { + for (const auto& result : lexerTest.results) { + if (result.token[0]) { // Embedded: "" - t.ok (embedded.token (token, type), "Embedded Lexer::token(...) --> true"); - t.is (token, result.token, " token --> " + token, result.expfail_token); - t.is ((int)type, (int)result.type, " type --> Lexer::Type::" + Lexer::typeToString (type), result.expfail_type); + t.ok(embedded.token(token, type), "Embedded Lexer::token(...) --> true"); + t.is(token, result.token, " token --> " + token, result.expfail_token); + t.is((int)type, (int)result.type, " type --> Lexer::Type::" + Lexer::typeToString(type), + result.expfail_type); } } } - t.is (Lexer::typeName (Lexer::Type::uuid), "uuid", "Lexer::typeName (Lexer::Type::uuid)"); - t.is (Lexer::typeName (Lexer::Type::number), "number", "Lexer::typeName (Lexer::Type::number)"); - t.is (Lexer::typeName (Lexer::Type::hex), "hex", "Lexer::typeName (Lexer::Type::hex)"); - t.is (Lexer::typeName (Lexer::Type::string), "string", "Lexer::typeName (Lexer::Type::string)"); - t.is (Lexer::typeName (Lexer::Type::url), "url", "Lexer::typeName (Lexer::Type::url)"); - t.is (Lexer::typeName (Lexer::Type::pair), "pair", "Lexer::typeName (Lexer::Type::pair)"); - t.is (Lexer::typeName (Lexer::Type::set), "set", "Lexer::typeName (Lexer::Type::set)"); - t.is (Lexer::typeName (Lexer::Type::separator), "separator", "Lexer::typeName (Lexer::Type::separator)"); - t.is (Lexer::typeName (Lexer::Type::tag), "tag", "Lexer::typeName (Lexer::Type::tag)"); - t.is (Lexer::typeName (Lexer::Type::path), "path", "Lexer::typeName (Lexer::Type::path)"); - t.is (Lexer::typeName (Lexer::Type::substitution), "substitution", "Lexer::typeName (Lexer::Type::substitution)"); - t.is (Lexer::typeName (Lexer::Type::pattern), "pattern", "Lexer::typeName (Lexer::Type::pattern)"); - t.is (Lexer::typeName (Lexer::Type::op), "op", "Lexer::typeName (Lexer::Type::op)"); - t.is (Lexer::typeName (Lexer::Type::dom), "dom", "Lexer::typeName (Lexer::Type::dom)"); - t.is (Lexer::typeName (Lexer::Type::identifier), "identifier", "Lexer::typeName (Lexer::Type::identifier)"); - t.is (Lexer::typeName (Lexer::Type::word), "word", "Lexer::typeName (Lexer::Type::word)"); - t.is (Lexer::typeName (Lexer::Type::date), "date", "Lexer::typeName (Lexer::Type::date)"); - t.is (Lexer::typeName (Lexer::Type::duration), "duration", "Lexer::typeName (Lexer::Type::duration)"); + t.is(Lexer::typeName(Lexer::Type::uuid), "uuid", "Lexer::typeName (Lexer::Type::uuid)"); + t.is(Lexer::typeName(Lexer::Type::number), "number", "Lexer::typeName (Lexer::Type::number)"); + t.is(Lexer::typeName(Lexer::Type::hex), "hex", "Lexer::typeName (Lexer::Type::hex)"); + t.is(Lexer::typeName(Lexer::Type::string), "string", "Lexer::typeName (Lexer::Type::string)"); + t.is(Lexer::typeName(Lexer::Type::url), "url", "Lexer::typeName (Lexer::Type::url)"); + t.is(Lexer::typeName(Lexer::Type::pair), "pair", "Lexer::typeName (Lexer::Type::pair)"); + t.is(Lexer::typeName(Lexer::Type::set), "set", "Lexer::typeName (Lexer::Type::set)"); + t.is(Lexer::typeName(Lexer::Type::separator), "separator", + "Lexer::typeName (Lexer::Type::separator)"); + t.is(Lexer::typeName(Lexer::Type::tag), "tag", "Lexer::typeName (Lexer::Type::tag)"); + t.is(Lexer::typeName(Lexer::Type::path), "path", "Lexer::typeName (Lexer::Type::path)"); + t.is(Lexer::typeName(Lexer::Type::substitution), "substitution", + "Lexer::typeName (Lexer::Type::substitution)"); + t.is(Lexer::typeName(Lexer::Type::pattern), "pattern", "Lexer::typeName (Lexer::Type::pattern)"); + t.is(Lexer::typeName(Lexer::Type::op), "op", "Lexer::typeName (Lexer::Type::op)"); + t.is(Lexer::typeName(Lexer::Type::dom), "dom", "Lexer::typeName (Lexer::Type::dom)"); + t.is(Lexer::typeName(Lexer::Type::identifier), "identifier", + "Lexer::typeName (Lexer::Type::identifier)"); + t.is(Lexer::typeName(Lexer::Type::word), "word", "Lexer::typeName (Lexer::Type::word)"); + t.is(Lexer::typeName(Lexer::Type::date), "date", "Lexer::typeName (Lexer::Type::date)"); + t.is(Lexer::typeName(Lexer::Type::duration), "duration", + "Lexer::typeName (Lexer::Type::duration)"); // std::string Lexer::lowerCase (const std::string& input) - t.is (Lexer::lowerCase (""), "", "Lexer::lowerCase '' -> ''"); - t.is (Lexer::lowerCase ("pre01_:POST"), "pre01_:post", "Lexer::lowerCase 'pre01_:POST' -> 'pre01_:post'"); + t.is(Lexer::lowerCase(""), "", "Lexer::lowerCase '' -> ''"); + t.is(Lexer::lowerCase("pre01_:POST"), "pre01_:post", + "Lexer::lowerCase 'pre01_:POST' -> 'pre01_:post'"); // std::string Lexer::commify (const std::string& data) - t.is (Lexer::commify (""), "", "Lexer::commify '' -> ''"); - t.is (Lexer::commify ("1"), "1", "Lexer::commify '1' -> '1'"); - t.is (Lexer::commify ("12"), "12", "Lexer::commify '12' -> '12'"); - t.is (Lexer::commify ("123"), "123", "Lexer::commify '123' -> '123'"); - t.is (Lexer::commify ("1234"), "1,234", "Lexer::commify '1234' -> '1,234'"); - t.is (Lexer::commify ("12345"), "12,345", "Lexer::commify '12345' -> '12,345'"); - t.is (Lexer::commify ("123456"), "123,456", "Lexer::commify '123456' -> '123,456'"); - t.is (Lexer::commify ("1234567"), "1,234,567", "Lexer::commify '1234567' -> '1,234,567'"); - t.is (Lexer::commify ("12345678"), "12,345,678", "Lexer::commify '12345678' -> '12,345,678'"); - t.is (Lexer::commify ("123456789"), "123,456,789", "Lexer::commify '123456789' -> '123,456,789'"); - t.is (Lexer::commify ("1234567890"), "1,234,567,890", "Lexer::commify '1234567890' -> '1,234,567,890'"); - t.is (Lexer::commify ("1.0"), "1.0", "Lexer::commify '1.0' -> '1.0'"); - t.is (Lexer::commify ("12.0"), "12.0", "Lexer::commify '12.0' -> '12.0'"); - t.is (Lexer::commify ("123.0"), "123.0", "Lexer::commify '123.0' -> '123.0'"); - t.is (Lexer::commify ("1234.0"), "1,234.0", "Lexer::commify '1234.0' -> '1,234.0'"); - t.is (Lexer::commify ("12345.0"), "12,345.0", "Lexer::commify '12345.0' -> '12,345.0'"); - t.is (Lexer::commify ("123456.0"), "123,456.0", "Lexer::commify '123456.0' -> '123,456.0'"); - t.is (Lexer::commify ("1234567.0"), "1,234,567.0", "Lexer::commify '1234567.0' -> '1,234,567.0'"); - t.is (Lexer::commify ("12345678.0"), "12,345,678.0", "Lexer::commify '12345678.0' -> '12,345,678.0'"); - t.is (Lexer::commify ("123456789.0"), "123,456,789.0", "Lexer::commify '123456789.0' -> '123,456,789.0'"); - t.is (Lexer::commify ("1234567890.0"), "1,234,567,890.0", "Lexer::commify '1234567890.0' -> '1,234,567,890.0'"); - t.is (Lexer::commify ("pre"), "pre", "Lexer::commify 'pre' -> 'pre'"); - t.is (Lexer::commify ("pre1234"), "pre1,234", "Lexer::commify 'pre1234' -> 'pre1,234'"); - t.is (Lexer::commify ("1234post"), "1,234post", "Lexer::commify '1234post' -> '1,234post'"); - t.is (Lexer::commify ("pre1234post"), "pre1,234post", "Lexer::commify 'pre1234post' -> 'pre1,234post'"); + t.is(Lexer::commify(""), "", "Lexer::commify '' -> ''"); + t.is(Lexer::commify("1"), "1", "Lexer::commify '1' -> '1'"); + t.is(Lexer::commify("12"), "12", "Lexer::commify '12' -> '12'"); + t.is(Lexer::commify("123"), "123", "Lexer::commify '123' -> '123'"); + t.is(Lexer::commify("1234"), "1,234", "Lexer::commify '1234' -> '1,234'"); + t.is(Lexer::commify("12345"), "12,345", "Lexer::commify '12345' -> '12,345'"); + t.is(Lexer::commify("123456"), "123,456", "Lexer::commify '123456' -> '123,456'"); + t.is(Lexer::commify("1234567"), "1,234,567", "Lexer::commify '1234567' -> '1,234,567'"); + t.is(Lexer::commify("12345678"), "12,345,678", "Lexer::commify '12345678' -> '12,345,678'"); + t.is(Lexer::commify("123456789"), "123,456,789", "Lexer::commify '123456789' -> '123,456,789'"); + t.is(Lexer::commify("1234567890"), "1,234,567,890", + "Lexer::commify '1234567890' -> '1,234,567,890'"); + t.is(Lexer::commify("1.0"), "1.0", "Lexer::commify '1.0' -> '1.0'"); + t.is(Lexer::commify("12.0"), "12.0", "Lexer::commify '12.0' -> '12.0'"); + t.is(Lexer::commify("123.0"), "123.0", "Lexer::commify '123.0' -> '123.0'"); + t.is(Lexer::commify("1234.0"), "1,234.0", "Lexer::commify '1234.0' -> '1,234.0'"); + t.is(Lexer::commify("12345.0"), "12,345.0", "Lexer::commify '12345.0' -> '12,345.0'"); + t.is(Lexer::commify("123456.0"), "123,456.0", "Lexer::commify '123456.0' -> '123,456.0'"); + t.is(Lexer::commify("1234567.0"), "1,234,567.0", "Lexer::commify '1234567.0' -> '1,234,567.0'"); + t.is(Lexer::commify("12345678.0"), "12,345,678.0", + "Lexer::commify '12345678.0' -> '12,345,678.0'"); + t.is(Lexer::commify("123456789.0"), "123,456,789.0", + "Lexer::commify '123456789.0' -> '123,456,789.0'"); + t.is(Lexer::commify("1234567890.0"), "1,234,567,890.0", + "Lexer::commify '1234567890.0' -> '1,234,567,890.0'"); + t.is(Lexer::commify("pre"), "pre", "Lexer::commify 'pre' -> 'pre'"); + t.is(Lexer::commify("pre1234"), "pre1,234", "Lexer::commify 'pre1234' -> 'pre1,234'"); + t.is(Lexer::commify("1234post"), "1,234post", "Lexer::commify '1234post' -> '1,234post'"); + t.is(Lexer::commify("pre1234post"), "pre1,234post", + "Lexer::commify 'pre1234post' -> 'pre1,234post'"); // std::string Lexer::trimLeft (const std::string& in, const std::string& t /*= " "*/) - t.is (Lexer::trimLeft (""), "", "Lexer::trimLeft '' -> ''"); - t.is (Lexer::trimLeft (" "), "", "Lexer::trimLeft ' ' -> ''"); - t.is (Lexer::trimLeft ("", " \t"), "", "Lexer::trimLeft '' -> ''"); - t.is (Lexer::trimLeft ("xxx"), "xxx", "Lexer::trimLeft 'xxx' -> 'xxx'"); - t.is (Lexer::trimLeft ("xxx", " \t"), "xxx", "Lexer::trimLeft 'xxx' -> 'xxx'"); - t.is (Lexer::trimLeft (" \t xxx \t "), "\t xxx \t ",R"(Lexer::trimLeft ' \t xxx \t ' -> '\t xxx \t ')"); - t.is (Lexer::trimLeft (" \t xxx \t ", " \t"), "xxx \t ", R"(Lexer::trimLeft ' \t xxx \t ' -> 'xxx \t ')"); + t.is(Lexer::trimLeft(""), "", "Lexer::trimLeft '' -> ''"); + t.is(Lexer::trimLeft(" "), "", "Lexer::trimLeft ' ' -> ''"); + t.is(Lexer::trimLeft("", " \t"), "", "Lexer::trimLeft '' -> ''"); + t.is(Lexer::trimLeft("xxx"), "xxx", "Lexer::trimLeft 'xxx' -> 'xxx'"); + t.is(Lexer::trimLeft("xxx", " \t"), "xxx", "Lexer::trimLeft 'xxx' -> 'xxx'"); + t.is(Lexer::trimLeft(" \t xxx \t "), "\t xxx \t ", + R"(Lexer::trimLeft ' \t xxx \t ' -> '\t xxx \t ')"); + t.is(Lexer::trimLeft(" \t xxx \t ", " \t"), "xxx \t ", + R"(Lexer::trimLeft ' \t xxx \t ' -> 'xxx \t ')"); // std::string Lexer::trimRight (const std::string& in, const std::string& t /*= " "*/) - t.is (Lexer::trimRight (""), "", "Lexer::trimRight '' -> ''"); - t.is (Lexer::trimRight (" "), "", "Lexer::trimRight ' ' -> ''"); - t.is (Lexer::trimRight ("", " \t"), "", "Lexer::trimRight '' -> ''"); - t.is (Lexer::trimRight ("xxx"), "xxx", "Lexer::trimRight 'xxx' -> 'xxx'"); - t.is (Lexer::trimRight ("xxx", " \t"), "xxx", "Lexer::trimRight 'xxx' -> 'xxx'"); - t.is (Lexer::trimRight (" \t xxx \t "), " \t xxx \t", R"(Lexer::trimRight ' \t xxx \t ' -> ' \t xxx \t')"); - t.is (Lexer::trimRight (" \t xxx \t ", " \t"), " \t xxx", R"(Lexer::trimRight ' \t xxx \t ' -> ' \t xxx')"); + t.is(Lexer::trimRight(""), "", "Lexer::trimRight '' -> ''"); + t.is(Lexer::trimRight(" "), "", "Lexer::trimRight ' ' -> ''"); + t.is(Lexer::trimRight("", " \t"), "", "Lexer::trimRight '' -> ''"); + t.is(Lexer::trimRight("xxx"), "xxx", "Lexer::trimRight 'xxx' -> 'xxx'"); + t.is(Lexer::trimRight("xxx", " \t"), "xxx", "Lexer::trimRight 'xxx' -> 'xxx'"); + t.is(Lexer::trimRight(" \t xxx \t "), " \t xxx \t", + R"(Lexer::trimRight ' \t xxx \t ' -> ' \t xxx \t')"); + t.is(Lexer::trimRight(" \t xxx \t ", " \t"), " \t xxx", + R"(Lexer::trimRight ' \t xxx \t ' -> ' \t xxx')"); // std::string Lexer::trim (const std::string& in, const std::string& t /*= " "*/) - t.is (Lexer::trim (""), "", "Lexer::trim '' -> ''"); - t.is (Lexer::trim (" "), "", "Lexer::trim ' ' -> ''"); - t.is (Lexer::trim ("", " \t"), "", "Lexer::trim '' -> ''"); - t.is (Lexer::trim ("xxx"), "xxx", "Lexer::trim 'xxx' -> 'xxx'"); - t.is (Lexer::trim ("xxx", " \t"), "xxx", "Lexer::trim 'xxx' -> 'xxx'"); - t.is (Lexer::trim (" \t xxx \t "), "\t xxx \t",R"(Lexer::trim ' \t xxx \t ' -> '\t xxx \t')"); - t.is (Lexer::trim (" \t xxx \t ", " \t"), "xxx", "Lexer::trim ' \\t xxx \\t ' -> 'xxx'"); + t.is(Lexer::trim(""), "", "Lexer::trim '' -> ''"); + t.is(Lexer::trim(" "), "", "Lexer::trim ' ' -> ''"); + t.is(Lexer::trim("", " \t"), "", "Lexer::trim '' -> ''"); + t.is(Lexer::trim("xxx"), "xxx", "Lexer::trim 'xxx' -> 'xxx'"); + t.is(Lexer::trim("xxx", " \t"), "xxx", "Lexer::trim 'xxx' -> 'xxx'"); + t.is(Lexer::trim(" \t xxx \t "), "\t xxx \t", R"(Lexer::trim ' \t xxx \t ' -> '\t xxx \t')"); + t.is(Lexer::trim(" \t xxx \t ", " \t"), "xxx", "Lexer::trim ' \\t xxx \\t ' -> 'xxx'"); return 0; } diff --git a/test/limit.test.py b/test/limit.test.py index 79baa0af0..63e505f7c 100755 --- a/test/limit.test.py +++ b/test/limit.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -101,6 +102,7 @@ class TestLimit(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/list.all.projects.test.py b/test/list.all.projects.test.py index 380118862..a9248646d 100755 --- a/test/list.all.projects.test.py +++ b/test/list.all.projects.test.py @@ -28,11 +28,13 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase + class TestListAllProjects(TestCase): @classmethod def setUpClass(cls): @@ -56,6 +58,7 @@ class TestListAllProjects(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/log.test.py b/test/log.test.py index 2a145023a..946a74a02 100755 --- a/test/log.test.py +++ b/test/log.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -64,7 +65,7 @@ class TestBug1575(TestCase): def test_spurious_whitespace_in_url(self): """1575: ensure that extra whitespace does not get inserted into a URL. - tw-1575: `task log` mangles URLs when quoted + tw-1575: `task log` mangles URLs when quoted """ self.t("log testing123 https://foo.example.com") @@ -74,6 +75,7 @@ class TestBug1575(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/logo.test.py b/test/logo.test.py index 1cc80aa35..9467d8e56 100755 --- a/test/logo.test.py +++ b/test/logo.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -52,6 +53,7 @@ class TestLogoCommand(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/math.test.py b/test/math.test.py index 62c05d41d..13d97562d 100755 --- a/test/math.test.py +++ b/test/math.test.py @@ -29,6 +29,7 @@ import sys import os import unittest from datetime import datetime + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -51,64 +52,66 @@ class TestMath(TestCase): cls.t("add three 'due:eoy-10days'") cls.t("add four due:'eoy - 10days'") cls.t("add five 'due:eoy - 10days'") - cls.t("add six 'due:{}-12-31T23:59:59 - 10days'".format (datetime.now().year)) + cls.t("add six 'due:{}-12-31T23:59:59 - 10days'".format(datetime.now().year)) def test_compact_unquoted(self): """compact unquoted""" - code, out, err = self.t('_get 1.due') + code, out, err = self.t("_get 1.due") self.assertEqual(out, self.when) def test_compact_value_quoted(self): """compact value quoted""" - code, out, err = self.t('_get 2.due') + code, out, err = self.t("_get 2.due") self.assertEqual(out, self.when) def test_compact_arg_quoted(self): """compact arg quoted""" - code, out, err = self.t('_get 3.due') + code, out, err = self.t("_get 3.due") self.assertEqual(out, self.when) def test_sparse_value_quoted(self): """sparse value quoted""" - code, out, err = self.t('_get 4.due') + code, out, err = self.t("_get 4.due") self.assertEqual(out, self.when) def test_sparse_arg_quoted(self): """sparse arg quoted""" - code, out, err = self.t('_get 5.due') + code, out, err = self.t("_get 5.due") self.assertEqual(out, self.when) def test_sparse_arg_quoted_literal(self): """sparse arg quoted literal""" - code, out, err = self.t('_get 6.due') + code, out, err = self.t("_get 6.due") self.assertEqual(out, self.when) + class TestBug851(TestCase): @classmethod def setUpClass(cls): """Executed once before any test in the class""" cls.t = Task() - cls.t('add past due:-2days') - cls.t('add future due:2days') + cls.t("add past due:-2days") + cls.t("add future due:2days") def setUp(self): """Executed before each test in the class""" def test_attribute_before_with_math(self): """851: Test due.before:now+1d""" - code, out, err = self.t('due.before:now+1day ls') + code, out, err = self.t("due.before:now+1day ls") self.assertIn("past", out) self.assertNotIn("future", out) def test_attribute_after_with_math(self): """851: Test due.after:now+1d""" - code, out, err = self.t('due.after:now+1day ls') + code, out, err = self.t("due.after:now+1day ls") self.assertNotIn("past", out) self.assertIn("future", out) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/modify.test.py b/test/modify.test.py index f61bfdad9..2a992e530 100755 --- a/test/modify.test.py +++ b/test/modify.test.py @@ -28,10 +28,12 @@ import sys import os import unittest + sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase + class TestBug1306(TestCase): def setUp(self): self.t = Task() @@ -42,6 +44,7 @@ class TestBug1306(TestCase): code, out, err = self.t("1 info") self.assertIn("PROJ", out) + class TestBug1763(TestCase): def setUp(self): self.t = Task() @@ -52,8 +55,10 @@ class TestBug1763(TestCase): code, out, err = self.t("1 modify due:") self.assertIn("Modified 0 tasks.", out) + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/nag.test.py b/test/nag.test.py index 0c16e0b1d..7b34da133 100755 --- a/test/nag.test.py +++ b/test/nag.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -76,16 +77,16 @@ class TestNagging(TestCase): def test_nagging_ready(self): """Verify that nagging occurs when there are READY tasks of higher urgency""" - self.t("add one") # low urgency - self.t("add two due:10days scheduled:yesterday") # medium urgency, ready + self.t("add one") # low urgency + self.t("add two due:10days scheduled:yesterday") # medium urgency, ready code, out, err = self.t("1 done") self.assertIn("NAG", err) def test_nagging_not_ready(self): """Verify that nagging does not occur when there are unREADY tasks of higher urgency""" - self.t("add one") # low urgency - self.t("add two due:10days scheduled:10days") # medium urgency, not ready + self.t("add one") # low urgency + self.t("add two due:10days scheduled:10days") # medium urgency, not ready code, out, err = self.t("1 done") self.assertNotIn("NAG", err) @@ -156,6 +157,7 @@ class TestNagging(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/news.test.py b/test/news.test.py index e2caf8a2a..7f1aa5dcf 100755 --- a/test/news.test.py +++ b/test/news.test.py @@ -63,6 +63,7 @@ class TestNewsNag(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/obfuscate.test.py b/test/obfuscate.test.py index a4e401258..5ff0e78f8 100755 --- a/test/obfuscate.test.py +++ b/test/obfuscate.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -74,6 +75,7 @@ class TestObfuscation(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/oldest.test.py b/test/oldest.test.py index ac840ba7e..593a4e041 100755 --- a/test/oldest.test.py +++ b/test/oldest.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -117,6 +118,7 @@ class TestOldestAndNewest(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/operators.test.py b/test/operators.test.py index 9e121acdb..fcf4554d2 100755 --- a/test/operators.test.py +++ b/test/operators.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -441,6 +442,7 @@ class TestOperatorsQuantity(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/overdue.test.py b/test/overdue.test.py index cec542267..ac98c9279 100755 --- a/test/overdue.test.py +++ b/test/overdue.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -55,6 +56,7 @@ class TestOverdue(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/partial.test.py b/test/partial.test.py index 8467e1ce2..193f567f2 100755 --- a/test/partial.test.py +++ b/test/partial.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -41,17 +42,18 @@ class TestPartialMatch(TestCase): def test_partial_date_match_spaced(self): """Partial match for dates: today = now --> true""" - code, out, err = self.t('calc today = now') - self.assertIn('true', out) + code, out, err = self.t("calc today = now") + self.assertIn("true", out) def test_exact_date_match_spaced(self): """Exact match for dates: today == now --> false""" - code, out, err = self.t('calc today == now') - self.assertIn('false', out) + code, out, err = self.t("calc today == now") + self.assertIn("false", out) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/prepend.test.py b/test/prepend.test.py index 97d57892a..2215ddbc2 100755 --- a/test/prepend.test.py +++ b/test/prepend.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -62,6 +63,7 @@ class TestPrepend(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/pri_sort.test.py b/test/pri_sort.test.py index 6e74a0519..25a19caf6 100755 --- a/test/pri_sort.test.py +++ b/test/pri_sort.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -110,6 +111,7 @@ class TestPrioritySorting(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/project.test.py b/test/project.test.py index 02d2d2880..c4718866d 100755 --- a/test/project.test.py +++ b/test/project.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -38,8 +39,10 @@ class TestProjects(TestCase): def setUp(self): self.t = Task() - self.STATUS = (r"The project '{0}' has changed\. " - r"Project '{0}' is {1} complete \({2} remaining\)\.") + self.STATUS = ( + r"The project '{0}' has changed\. " + r"Project '{0}' is {1} complete \({2} remaining\)\." + ) def test_project_summary_count(self): """'task projects' shouldn't consider deleted tasks in summary. @@ -58,46 +61,37 @@ class TestProjects(TestCase): """project status/progress is shown and is up-to-date""" code, out, err = self.t("add one pro:foo") - self.assertRegex(err, self.STATUS.format("foo", "0%", - "1 task")) + self.assertRegex(err, self.STATUS.format("foo", "0%", "1 task")) code, out, err = self.t("add two pro:foo") - self.assertRegex(err, self.STATUS.format("foo", "0%", - "2 of 2 tasks")) + self.assertRegex(err, self.STATUS.format("foo", "0%", "2 of 2 tasks")) code, out, err = self.t("add three pro:foo") - self.assertRegex(err, self.STATUS.format("foo", "0%", - "3 of 3 tasks")) + self.assertRegex(err, self.STATUS.format("foo", "0%", "3 of 3 tasks")) code, out, err = self.t("add four pro:foo") - self.assertRegex(err, self.STATUS.format("foo", "0%", - "4 of 4 tasks")) + self.assertRegex(err, self.STATUS.format("foo", "0%", "4 of 4 tasks")) code, out, err = self.t("1 done") - self.assertRegex(err, self.STATUS.format("foo", "25%", - "3 of 4 tasks")) + self.assertRegex(err, self.STATUS.format("foo", "25%", "3 of 4 tasks")) code, out, err = self.t("2 delete", input="y\n") - self.assertRegex(err, self.STATUS.format("foo", "33%", - "2 of 3 tasks")) + self.assertRegex(err, self.STATUS.format("foo", "33%", "2 of 3 tasks")) code, out, err = self.t("3 modify pro:bar") - self.assertRegex(err, self.STATUS.format("foo", "50%", - "1 of 2 tasks")) - self.assertRegex(err, self.STATUS.format("bar", "0%", - "1 task")) + self.assertRegex(err, self.STATUS.format("foo", "50%", "1 of 2 tasks")) + self.assertRegex(err, self.STATUS.format("bar", "0%", "1 task")) def test_project_spaces(self): """projects with spaces are handled correctly""" self.t("add hello pro:bob") code, out, err = self.t('1 mod pro:"foo bar"') - self.assertRegex(err, self.STATUS.format("foo bar", "0%", - "1 task")) + self.assertRegex(err, self.STATUS.format("foo bar", "0%", "1 task")) # Ensure filtering for project with spaces works code, out, err = self.t('pro:"foo bar" count') - self.assertEqual(out.strip(), '1') + self.assertEqual(out.strip(), "1") def test_project_spaces(self): """TW #2386: Filter for project:someday""" @@ -105,8 +99,8 @@ class TestProjects(TestCase): self.t("add hello pro:someday") # Ensure filtering for project with numeric date works - code, out, err = self.t('pro:someday count') - self.assertEqual(out.strip(), '1') + code, out, err = self.t("pro:someday count") + self.assertEqual(out.strip(), "1") def add_tasks(self): self.t("add testing project:existingParent") @@ -120,7 +114,7 @@ class TestProjects(TestCase): order = ( ".myProject ", ".myProject. ", - "abstractParent", # No space at EOL because this line in the summary ends here. + "abstractParent", # No space at EOL because this line in the summary ends here. " kid ", "existingParent ", " child ", @@ -136,8 +130,10 @@ class TestProjects(TestCase): self.assertTrue( lines[pos].startswith(proj), - msg=("Project '{0}' is not in line #{1} or has an unexpected " - "indentation.{2}".format(proj, pos, out)) + msg=( + "Project '{0}' is not in line #{1} or has an unexpected " + "indentation.{2}".format(proj, pos, out) + ), ) def test_project_indentation(self): @@ -321,7 +317,7 @@ class TestBug906(TestCase): def test_project_hierarchy_filter(self): """906: Test project hierarchy filters - Bug 906 + Bug 906 """ self.t("add zero") self.t("add one pro:a.b") @@ -355,7 +351,7 @@ class TestBug856(TestCase): def test_project_hierarchy_filter(self): """856: Test project.none: works - Bug 856: "task list project.none:" does not work. + Bug 856: "task list project.none:" does not work. """ self.t("add assigned project:X") self.t("add floating") @@ -364,7 +360,7 @@ class TestBug856(TestCase): self.assertIn("floating", out) self.assertNotIn("assigned", out) - code, out, err = self.t("project:\'\' ls") + code, out, err = self.t("project:'' ls") self.assertIn("floating", out) self.assertNotIn("assigned", out) @@ -380,7 +376,7 @@ class TestBug1511(TestCase): def test_project_hierarchy_filter(self): """1511: Test project:one-two can be added and queried - Bug 1511: Project titles not properly parsed if they contain hyphens + Bug 1511: Project titles not properly parsed if they contain hyphens """ self.t("add zero") self.t("add one project:two-three") @@ -396,7 +392,7 @@ class TestBug1455(TestCase): def test_project_hierarchy_filter(self): """1455: Test project:school) - Bug 1455: Filter parser does not properly handle parentheses in attributes + Bug 1455: Filter parser does not properly handle parentheses in attributes """ self.t("add zero") self.t("add one project:two)") @@ -431,16 +427,14 @@ class TestBug1267(TestCase): self.t = Task() def test_add_task_no_project_with_default(self): - """1267: Add a task without a project using direct rc change - """ + """1267: Add a task without a project using direct rc change""" project = "MakePudding" self.t("rc.default.project={0} add proj: 'Add cream'".format(project)) code, out, err = self.t("ls") self.assertNotIn(project, out) def test_add_task_no_project_with_default_rcfile(self): - """1267: Add a task without a project writing to rc file - """ + """1267: Add a task without a project writing to rc file""" project = "MakePudding" self.t.config("default.project", project) self.t("add proj: 'Add cream'") @@ -514,9 +508,7 @@ class TestBug1904(TestCase): self.t("add pro:a.b test2") def validate_order(self, out): - order = ("a", - " b", - "a-b") + order = ("a", " b", "a-b") lines = out.splitlines(True) # position where project names start on the lines list @@ -527,8 +519,10 @@ class TestBug1904(TestCase): self.assertTrue( lines[pos].startswith(proj), - msg=("Project '{0}' is not in line #{1} or has an unexpected " - "indentation.{2}".format(proj, pos, out)) + msg=( + "Project '{0}' is not in line #{1} or has an unexpected " + "indentation.{2}".format(proj, pos, out) + ), ) def test_project_eval(self): @@ -540,6 +534,7 @@ class TestBug1904(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/purge.test.py b/test/purge.test.py index ec6491fee..2518a8103 100755 --- a/test/purge.test.py +++ b/test/purge.test.py @@ -29,6 +29,7 @@ import sys import os import unittest import time + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -133,7 +134,7 @@ class TestDelete(TestCase): self.assertIn("Purged 4 tasks.", out) code, out, err = self.t("uuids") - self.assertEqual('\n', out) + self.assertEqual("\n", out) def test_purge_children_fail_pending(self): """Purge aborts if task has pending children""" @@ -154,7 +155,7 @@ class TestDelete(TestCase): # Check that nothing was purged code, out, err = self.t("count") - self.assertEqual('4\n', out) + self.assertEqual("4\n", out) def test_purge_children_fail_confirm(self): """Purge aborts if user does not agree with it affecting child tasks""" @@ -173,7 +174,7 @@ class TestDelete(TestCase): # Check that nothing was purged code, out, err = self.t("count") - self.assertEqual('4\n', out) + self.assertEqual("4\n", out) def test_purge_children(self): """Purge command removes dependencies on indirectly purged tasks""" @@ -202,8 +203,10 @@ class TestDelete(TestCase): dependencies = self.t("_get 1.depends")[1].strip() self.assertEqual("", dependencies) + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/quotes.test.py b/test/quotes.test.py index 95b057231..c17e229e6 100755 --- a/test/quotes.test.py +++ b/test/quotes.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -124,34 +125,35 @@ class TestBug1436(TestCase): def test_backslashes(self): """1436: Prove to the reader that backslashes are eaten twice (which means - two backslashes to one) once by Python, and once more by some mystery process - launch thing. + two backslashes to one) once by Python, and once more by some mystery process + launch thing. - This problem is entirely testing artifact, and not Taskwarrior. + This problem is entirely testing artifact, and not Taskwarrior. """ self.echo = Task(taskw=utils.binary_location("echo", USE_PATH=True)) # One level of backshashes gets eaten by bash # Verify with: $ echo xxx \\\\yyy zzz - code, out, err = self.echo(r"xxx \\\\yyy zzz") # Shows as 'xxx \\yyy zzz' - code, out, err = self.echo(r"xxx \\yyy zzz") # Shows as 'xxx \yyy zzz' - code, out, err = self.echo(r"xxx \yyy zzz") # Shows as 'xxx yyy zzz' + code, out, err = self.echo(r"xxx \\\\yyy zzz") # Shows as 'xxx \\yyy zzz' + code, out, err = self.echo(r"xxx \\yyy zzz") # Shows as 'xxx \yyy zzz' + code, out, err = self.echo(r"xxx \yyy zzz") # Shows as 'xxx yyy zzz' # If single quotes are used, the backslashes are not eaten # Verify with: $ echo xxx '\\\\yyy' zzz - code, out, err = self.echo(r"xxx '\\\\yyy' zzz") # Shows as 'xxx \\\\yyy zzz' - code, out, err = self.echo(r"xxx '\\yyy' zzz") # Shows as 'xxx \\yyy zzz' - code, out, err = self.echo(r"xxx '\yyy' zzz") # Shows as 'xxx \yyy zzz' + code, out, err = self.echo(r"xxx '\\\\yyy' zzz") # Shows as 'xxx \\\\yyy zzz' + code, out, err = self.echo(r"xxx '\\yyy' zzz") # Shows as 'xxx \\yyy zzz' + code, out, err = self.echo(r"xxx '\yyy' zzz") # Shows as 'xxx \yyy zzz' # If double quotes are used, the backslashes are eaten # Verify with: $ echo xxx "\\\\yyy" zzz - code, out, err = self.echo(r'xxx "\\\\yyy" zzz') # Shows as 'xxx \\\\yyy zzz' - code, out, err = self.echo(r'xxx "\\yyy" zzz') # Shows as 'xxx \\yyy zzz' - code, out, err = self.echo(r'xxx "\yyy" zzz') # Shows as 'xxx \yyy zzz' + code, out, err = self.echo(r'xxx "\\\\yyy" zzz') # Shows as 'xxx \\\\yyy zzz' + code, out, err = self.echo(r'xxx "\\yyy" zzz') # Shows as 'xxx \\yyy zzz' + code, out, err = self.echo(r'xxx "\yyy" zzz') # Shows as 'xxx \yyy zzz' if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/rc.override.test.py b/test/rc.override.test.py index 55e748ac8..ce83dacd2 100755 --- a/test/rc.override.test.py +++ b/test/rc.override.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -39,7 +40,7 @@ class TestOverride(TestCase): def setUp(self): """Executed before each test in the class""" self.t = Task() - self.t.config("regex", "0") + self.t.config("regex", "0") self.t.config("verbose", "nothing") def test_override(self): @@ -71,6 +72,7 @@ class TestRCSegfault(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/recurrence.test.py b/test/recurrence.test.py index d65a35c63..155eb9d1f 100755 --- a/test/recurrence.test.py +++ b/test/recurrence.test.py @@ -30,6 +30,7 @@ import os import re import time import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -41,12 +42,12 @@ class TestRecurrenceSorting(TestCase): def setUpClass(cls): """Executed once before any test in the class""" cls.t = Task() - cls.t.config("report.asc.columns", "id,recur,description") - cls.t.config("report.asc.sort", "recur+") - cls.t.config("report.asc.filter", "status:pending") + cls.t.config("report.asc.columns", "id,recur,description") + cls.t.config("report.asc.sort", "recur+") + cls.t.config("report.asc.filter", "status:pending") cls.t.config("report.desc.columns", "id,recur,description") - cls.t.config("report.desc.sort", "recur-") - cls.t.config("report.desc.filter", "status:pending") + cls.t.config("report.desc.sort", "recur-") + cls.t.config("report.desc.filter", "status:pending") cls.t("add one due:tomorrow recur:daily") cls.t("add two due:tomorrow recur:weekly") @@ -205,6 +206,7 @@ class TestRecurrenceTasks(TestCase): code, out, err = self.t("3 delete", input="y\n") self.assertIn("Deleted 1 task.", out) + class TestBug972(TestCase): def setUp(self): """972: Executed before each test in the class""" @@ -212,7 +214,7 @@ class TestBug972(TestCase): def test_interpretation_of_seven(self): """Bug 972: A recurrence period of "7" is interpreted as "7s", not "7d" - as intended. + as intended. """ code, out, err = self.t.runError("add one due:now recur:2") self.assertIn("The duration value '2' is not supported.", err) @@ -226,7 +228,7 @@ class TestDeletionRecurrence(TestCase): def test_delete_parent(self): """Delete a parent with child tasks""" self.t("add one due:eom recur:daily") - self.t("list") # GC/handleRecurrence + self.t("list") # GC/handleRecurrence code, out, err = self.t("1 delete", input="y\ny\n") self.assertIn("Deleted 2 tasks.", out) @@ -237,7 +239,7 @@ class TestDeletionRecurrence(TestCase): """Delete a child with sibling tasks""" self.t("add one due:eom recur:daily") self.t("list rc.recurrence.limit:5") - code, out, err = self.t("list rc.verbose:nothing") # GC/handleRecurrence + code, out, err = self.t("list rc.verbose:nothing") # GC/handleRecurrence self.assertEqual(out.count("one"), 5) code, out, err = self.t("2 delete", input="y\ny\n") @@ -252,7 +254,7 @@ class TestAppendPrependRecurrence(TestCase): def test_append_propagate(self): """Append and propagate""" self.t("add one due:eom recur:daily") - self.t("list rc.recurrence.limit:2") # GC/handleRecurrence + self.t("list rc.recurrence.limit:2") # GC/handleRecurrence code, out, err = self.t("2 append APP", input="y\n") self.assertIn("Appended 2 tasks.", out) @@ -260,7 +262,7 @@ class TestAppendPrependRecurrence(TestCase): def test_prepend_propagate(self): """Prepend and propagate""" self.t("add one due:eom recur:daily") - self.t("list rc.recurrence.limit:2") # GC/handleRecurrence + self.t("list rc.recurrence.limit:2") # GC/handleRecurrence code, out, err = self.t("2 prepend PRE", input="y\n") self.assertIn("Prepended 2 tasks.", out) @@ -359,11 +361,12 @@ class TestBug955(TestCase): self.assertIn("Deleted 2 tasks", out) code, out, err = self.t.runError("all status:recurring") - self.assertIn("No matches", err) + self.assertIn("No matches", err) code, out, err = self.t.runError("ls") self.assertIn("No matches", err) + class TestUpgradeToRecurring(TestCase): def setUp(self): """Executed before each test in the class""" @@ -383,6 +386,7 @@ class TestUpgradeToRecurring(TestCase): code, out, err = self.t.runError("1 modify recur:weekly") self.assertIn("You cannot specify a recurring task without a due date.", err) + class TestRecurrenceNotification(TestCase): def setUp(self): """Executed before each test in the class""" @@ -399,6 +403,7 @@ class TestRecurrenceNotification(TestCase): code, out, err = self.t("list") self.assertNotIn("Creating recurring task instance 'foo'", err) + class BaseTestBug360(TestCase): def setUp(self): """Executed before each test in the class""" @@ -407,10 +412,10 @@ class BaseTestBug360(TestCase): # This command forces a handleRecurrence() call to generate synthetic tasks. self.t("ls") + class TestBug360RemovalError(BaseTestBug360): def test_modify_recursive_project(self): - """360: Modifying a recursive task by adding project: also modifies parent - """ + """360: Modifying a recursive task by adding project: also modifies parent""" code, out, err = self.t("1 modify project:bar", input="y\n") expected = "Modified 2 tasks." @@ -419,8 +424,7 @@ class TestBug360RemovalError(BaseTestBug360): self.assertNotIn(expected, err) def test_cannot_remove_recurrence(self): - """360: Cannot remove recurrence from recurring task - """ + """360: Cannot remove recurrence from recurring task""" # TODO Removing recur: from a recurring task should also remove imask # and parent. @@ -432,8 +436,7 @@ class TestBug360RemovalError(BaseTestBug360): self.assertIn(expected, err) def test_cannot_remove_due_date(self): - """360: Cannot remove due date from recurring task - """ + """360: Cannot remove due date from recurring task""" # TODO Removing due: from a recurring task should also remove recur, # imask and parent code, out, err = self.t.runError("2 modify due:") @@ -470,6 +473,7 @@ class TestBug360AllowedChanges(BaseTestBug360): expected = "You cannot remove the due date from a recurring task." self.assertNotIn(expected, err) + class TestBug649(TestCase): def setUp(self): """Executed before each test in the class""" @@ -482,6 +486,7 @@ class TestBug649(TestCase): self.assertIn("is neither pending nor waiting", out) self.assertNotIn("Completed 1", out) + class TestBugC001(TestCase): def setUp(self): """Executed before each test in the class""" @@ -492,6 +497,7 @@ class TestBugC001(TestCase): code, out, err = self.t("add one due:tomorrow recur:daily") code, out, err = self.t("add two due:tomorrow recur:daily") + class TestBug839(TestCase): def setUp(self): """Executed before each test in the class""" @@ -501,7 +507,10 @@ class TestBug839(TestCase): """839: Verify that importing a legacy recurrence value is ok""" # use a recent timestamp to avoid slowly iterating over large number of tasks justnow = int(time.time()) - 120 - json = '{"description":"one","due":"%s","recur":"1m","status":"recurring","uuid":"ebeeab00-ccf8-464b-8b58-f7f2d606edfb"}' % justnow + json = ( + '{"description":"one","due":"%s","recur":"1m","status":"recurring","uuid":"ebeeab00-ccf8-464b-8b58-f7f2d606edfb"}' + % justnow + ) self.t("import -", input=json) code, out, err = self.t("list") @@ -519,75 +528,76 @@ class TestPeriod(TestCase): self.t = Task() def test_recurrence_periods(self): - """Verify recurrence period special-case support + """Verify recurrence period special-case support - Date getNextRecurrence (Date& current, std::string& period) + Date getNextRecurrence (Date& current, std::string& period) - Confirmed: - getNextRecurrence convertDuration - ----------------- --------------- - daily - day - weekly - sennight - biweekly - fortnight - monthly monthly - quarterly quarterly - semiannual semiannual - bimonthly bimonthly - biannual biannual - biyearly biyearly - annual - yearly - *m *m - *q *q - *d - *w - *y - """ + Confirmed: + getNextRecurrence convertDuration + ----------------- --------------- + daily + day + weekly + sennight + biweekly + fortnight + monthly monthly + quarterly quarterly + semiannual semiannual + bimonthly bimonthly + biannual biannual + biyearly biyearly + annual + yearly + *m *m + *q *q + *d + *w + *y + """ - self.t("add daily due:tomorrow recur:daily") - self.t("add 1day due:tomorrow recur:1day") - self.t("add weekly due:tomorrow recur:weekly") - self.t("add 1sennight due:tomorrow recur:1sennight") - self.t("add biweekly due:tomorrow recur:biweekly") - self.t("add fortnight due:tomorrow recur:fortnight") - self.t("add monthly due:tomorrow recur:monthly") - self.t("add quarterly due:tomorrow recur:quarterly") - self.t("add semiannual due:tomorrow recur:semiannual") - self.t("add bimonthly due:tomorrow recur:bimonthly") - self.t("add biannual due:tomorrow recur:biannual") - self.t("add biyearly due:tomorrow recur:biyearly") - self.t("add annual due:tomorrow recur:annual") - self.t("add yearly due:tomorrow recur:yearly") - self.t("add 2d due:tomorrow recur:2d") - self.t("add 2w due:tomorrow recur:2w") - self.t("add 2mo due:tomorrow recur:2mo") - self.t("add 2q due:tomorrow recur:2q") - self.t("add 2y due:tomorrow recur:2y") + self.t("add daily due:tomorrow recur:daily") + self.t("add 1day due:tomorrow recur:1day") + self.t("add weekly due:tomorrow recur:weekly") + self.t("add 1sennight due:tomorrow recur:1sennight") + self.t("add biweekly due:tomorrow recur:biweekly") + self.t("add fortnight due:tomorrow recur:fortnight") + self.t("add monthly due:tomorrow recur:monthly") + self.t("add quarterly due:tomorrow recur:quarterly") + self.t("add semiannual due:tomorrow recur:semiannual") + self.t("add bimonthly due:tomorrow recur:bimonthly") + self.t("add biannual due:tomorrow recur:biannual") + self.t("add biyearly due:tomorrow recur:biyearly") + self.t("add annual due:tomorrow recur:annual") + self.t("add yearly due:tomorrow recur:yearly") + self.t("add 2d due:tomorrow recur:2d") + self.t("add 2w due:tomorrow recur:2w") + self.t("add 2mo due:tomorrow recur:2mo") + self.t("add 2q due:tomorrow recur:2q") + self.t("add 2y due:tomorrow recur:2y") + + # Verify that the recurring task instances were created. One of each. + code, out, err = self.t("list") + self.assertIn(" daily ", out) + self.assertIn(" 1day ", out) + self.assertIn(" weekly ", out) + self.assertIn(" 1sennight ", out) + self.assertIn(" biweekly ", out) + self.assertIn(" fortnight ", out) + self.assertIn(" monthly ", out) + self.assertIn(" quarterly ", out) + self.assertIn(" semiannual ", out) + self.assertIn(" bimonthly ", out) + self.assertIn(" biannual ", out) + self.assertIn(" biyearly ", out) + self.assertIn(" annual ", out) + self.assertIn(" yearly ", out) + self.assertIn(" 2d ", out) + self.assertIn(" 2w ", out) + self.assertIn(" 2mo ", out) + self.assertIn(" 2q ", out) + self.assertIn(" 2y ", out) - # Verify that the recurring task instances were created. One of each. - code, out, err = self.t("list") - self.assertIn(" daily ", out); - self.assertIn(" 1day ", out); - self.assertIn(" weekly ", out); - self.assertIn(" 1sennight ", out); - self.assertIn(" biweekly ", out); - self.assertIn(" fortnight ", out); - self.assertIn(" monthly ", out); - self.assertIn(" quarterly ", out); - self.assertIn(" semiannual ", out); - self.assertIn(" bimonthly ", out); - self.assertIn(" biannual ", out); - self.assertIn(" biyearly ", out); - self.assertIn(" annual ", out); - self.assertIn(" yearly ", out); - self.assertIn(" 2d ", out); - self.assertIn(" 2w ", out); - self.assertIn(" 2mo ", out); - self.assertIn(" 2q ", out); - self.assertIn(" 2y ", out); class TestBugAnnual(TestCase): def setUp(self): @@ -596,11 +606,11 @@ class TestBugAnnual(TestCase): def test_annual_creep(self): """Verify 'annual' recurring tasks don't creep""" - self.t.config("dateformat", "YMD") - self.t.config("report.annual.labels", "ID,Due") + self.t.config("dateformat", "YMD") + self.t.config("report.annual.labels", "ID,Due") self.t.config("report.annual.columns", "id,due") - self.t.config("report.annual.filter", "status:pending") - self.t.config("report.annual.sort", "due+") + self.t.config("report.annual.filter", "status:pending") + self.t.config("report.annual.sort", "due+") # If a task is added with a due date ten years ago, with an annual recurrence, # then the synthetic tasks in between then and now have a due date that creeps. @@ -647,6 +657,7 @@ class TestBugAnnual(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/reports.test.py b/test/reports.test.py index 4a18c3bb5..b1d763ae7 100755 --- a/test/reports.test.py +++ b/test/reports.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -54,6 +55,7 @@ class TestReportCommand(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/scripts/test_macos.sh b/test/scripts/test_macos.sh index cc86ece07..b471a7e1c 100755 --- a/test/scripts/test_macos.sh +++ b/test/scripts/test_macos.sh @@ -14,4 +14,4 @@ pushd test make ./run_all -v cat all.log | grep 'not ok' -./problems \ No newline at end of file +./problems diff --git a/test/search.test.py b/test/search.test.py index 51474208e..6be972f86 100755 --- a/test/search.test.py +++ b/test/search.test.py @@ -29,6 +29,7 @@ import sys import os import unittest import platform + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -47,7 +48,7 @@ class TestSearch(TestCase): def test_plain_arg(self): """Verify plain args are interpreted as search terms - tw-1635: Running "task anystringatall" does not filter anything + tw-1635: Running "task anystringatall" does not filter anything """ code, out, err = self.t("one list") self.assertIn("one", out) @@ -65,6 +66,7 @@ class TestSearch(TestCase): self.assertIn("one", out) self.assertNotIn("two", out) + class Test1418(TestCase): def setUp(self): self.t = Task() @@ -140,6 +142,7 @@ class Test1418(TestCase): code, out, err = self.t("/foo\\+/") self.assertIn(description, out) + class TestBug1472(TestCase): @classmethod def setUpClass(cls): @@ -180,67 +183,74 @@ class TestBug1472(TestCase): class Test1469(TestCase): def setUp(self): self.t = Task() - self.t('add foo') + self.t("add foo") self.t('add "neue Badmöbel kaufen"') def test_implicit_search_sensitive_regex(self): - """1469: Implicit search, case sensitive, regex """ - code, out, err = self.t('list /möbel/ rc.search.case.sensitive=yes rc.regex=on') + """1469: Implicit search, case sensitive, regex""" + code, out, err = self.t("list /möbel/ rc.search.case.sensitive=yes rc.regex=on") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('möbel', out) - self.assertNotIn('foo', out) + self.assertIn("möbel", out) + self.assertNotIn("foo", out) def test_implicit_search_sensitive_noregex(self): - """1469: Implicit search, case sensitive, no regex """ - code, out, err = self.t('list /möbel/ rc.search.case.sensitive=yes rc.regex=off') + """1469: Implicit search, case sensitive, no regex""" + code, out, err = self.t( + "list /möbel/ rc.search.case.sensitive=yes rc.regex=off" + ) self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('möbel', out) - self.assertNotIn('foo', out) + self.assertIn("möbel", out) + self.assertNotIn("foo", out) - @unittest.skipIf('CYGWIN' in platform.system(), 'Skipping regex case-insensitive test for Cygwin') + @unittest.skipIf( + "CYGWIN" in platform.system(), "Skipping regex case-insensitive test for Cygwin" + ) def test_implicit_search_insensitive_regex(self): - """1469: Implicit search, case insensitive, regex """ - code, out, err = self.t('list /möbel/ rc.search.case.sensitive=no rc.regex=on') - self.assertEqual(0, code, - "Exit code was non-zero ({0})".format(code)) - self.assertIn('möbel', out) - self.assertNotIn('foo', out) + """1469: Implicit search, case insensitive, regex""" + code, out, err = self.t("list /möbel/ rc.search.case.sensitive=no rc.regex=on") + self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) + self.assertIn("möbel", out) + self.assertNotIn("foo", out) def test_implicit_search_insensitive_noregex(self): - """1469: Implicit search, case insensitive, no regex """ - code, out, err = self.t('list /möbel/ rc.search.case.sensitive=no rc.regex=off') + """1469: Implicit search, case insensitive, no regex""" + code, out, err = self.t("list /möbel/ rc.search.case.sensitive=no rc.regex=off") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('möbel', out) - self.assertNotIn('foo', out) + self.assertIn("möbel", out) + self.assertNotIn("foo", out) def test_explicit_search_sensitive_regex(self): - """1469: Explicit search, case sensitive, regex """ - code, out, err = self.t('list /möbel/ rc.search.case.sensitive=yes rc.regex=on') + """1469: Explicit search, case sensitive, regex""" + code, out, err = self.t("list /möbel/ rc.search.case.sensitive=yes rc.regex=on") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('möbel', out) - self.assertNotIn('foo', out) + self.assertIn("möbel", out) + self.assertNotIn("foo", out) def test_explicit_search_sensitive_noregex(self): - """1469: Explicit search, case sensitive, no regex """ - code, out, err = self.t('list /möbel/ rc.search.case.sensitive=yes rc.regex=off') + """1469: Explicit search, case sensitive, no regex""" + code, out, err = self.t( + "list /möbel/ rc.search.case.sensitive=yes rc.regex=off" + ) self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('möbel', out) - self.assertNotIn('foo', out) + self.assertIn("möbel", out) + self.assertNotIn("foo", out) - @unittest.skipIf('CYGWIN' in platform.system(), 'Skipping regex case-insensitive test for Cygwin') + @unittest.skipIf( + "CYGWIN" in platform.system(), "Skipping regex case-insensitive test for Cygwin" + ) def test_explicit_search_insensitive_regex(self): - """1469: Explicit search, case insensitive, regex """ - code, out, err = self.t('list /möbel/ rc.search.case.sensitive=no rc.regex=on') + """1469: Explicit search, case insensitive, regex""" + code, out, err = self.t("list /möbel/ rc.search.case.sensitive=no rc.regex=on") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('möbel', out) - self.assertNotIn('foo', out) + self.assertIn("möbel", out) + self.assertNotIn("foo", out) def test_explicit_search_insensitive_noregex(self): - """1469: Explicit search, case insensitive, no regex """ - code, out, err = self.t('list /möbel/ rc.search.case.sensitive=no rc.regex=off') + """1469: Explicit search, case insensitive, no regex""" + code, out, err = self.t("list /möbel/ rc.search.case.sensitive=no rc.regex=off") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('möbel', out) - self.assertNotIn('foo', out) + self.assertIn("möbel", out) + self.assertNotIn("foo", out) class TestBug1479(TestCase): @@ -272,6 +282,7 @@ class TestBug1479(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/sequence.test.py b/test/sequence.test.py index add63b362..2943fdac2 100755 --- a/test/sequence.test.py +++ b/test/sequence.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -57,7 +58,10 @@ class TestSequences(TestCase): """Test sequences in start/stop""" self.t("1,2 start") code, out, err = self.t("_get 1.start 2.start") - self.assertRegex(out, "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2} \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\n") + self.assertRegex( + out, + "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2} \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\n", + ) self.t("1,2 stop") code, out, err = self.t("_get 1.start 2.start") @@ -84,12 +88,15 @@ class TestSequences(TestCase): def test_sequence_annotate(self): """Test sequences in annotate""" self.t("1,2 annotate note") - code, out, err = self.t("_get 1.annotations.1.description 2.annotations.1.description") + code, out, err = self.t( + "_get 1.annotations.1.description 2.annotations.1.description" + ) self.assertEqual("note note\n", out) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/shell.test.py b/test/shell.test.py index 8c1853b18..1e4d212a5 100755 --- a/test/shell.test.py +++ b/test/shell.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -40,19 +41,20 @@ class TestFilterPrefix(TestCase): """Executed once before any test in the class""" cls.t = Task() cls.t.config("verbose", "nothing") - cls.t('add foo') + cls.t("add foo") def test_success(self): """Test successful search returns zero.""" - code, out, err = self.t('list /foo/') + code, out, err = self.t("list /foo/") def test_failure(self): """Test failed search returns non-zero.""" - code, out, err = self.t.runError('list /bar/') + code, out, err = self.t.runError("list /bar/") if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/show.test.py b/test/show.test.py index 11a091bf8..f3fdceb79 100755 --- a/test/show.test.py +++ b/test/show.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -68,7 +69,9 @@ class TestShowCommand(TestCase): """Verify show command lists all with no arg provided""" self.t.config("foo", "bar") code, out, err = self.t("show") - self.assertIn("Your .taskrc file contains these unrecognized variables:\n foo", out) + self.assertIn( + "Your .taskrc file contains these unrecognized variables:\n foo", out + ) class TestShowHelperCommand(TestCase): @@ -85,6 +88,7 @@ class TestShowHelperCommand(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/simpletap/CMakeLists.txt b/test/simpletap/CMakeLists.txt index 040a465d6..53303a1f5 100644 --- a/test/simpletap/CMakeLists.txt +++ b/test/simpletap/CMakeLists.txt @@ -1 +1 @@ -configure_file(__init__.py __init__.py COPYONLY) \ No newline at end of file +configure_file(__init__.py __init__.py COPYONLY) diff --git a/test/simpletap/__init__.py b/test/simpletap/__init__.py index 72b1fe690..4134f8f21 100644 --- a/test/simpletap/__init__.py +++ b/test/simpletap/__init__.py @@ -80,8 +80,7 @@ class TAPTestResult(unittest.result.TestResult): pass def _restoreStdout(self): - """Restore sys.stdout and sys.stderr, don't merge buffered output yet - """ + """Restore sys.stdout and sys.stderr, don't merge buffered output yet""" if self.buffer: sys.stdout = self._original_stdout sys.stderr = self._original_stderr @@ -99,12 +98,11 @@ class TAPTestResult(unittest.result.TestResult): else: stream.write("# " + line) - if not line.endswith('\n'): - stream.write('\n') + if not line.endswith("\n"): + stream.write("\n") def _mergeStdout(self): - """Merge buffered output with main streams - """ + """Merge buffered output with main streams""" if self.buffer: output = self._stdout_buffer.getvalue() @@ -154,25 +152,33 @@ class TAPTestResult(unittest.result.TestResult): if status: if status == "SKIP": - self.stream.writeln("{0} {1} - {2}: {3} # skip".format( - color("ok", "yellow"), self.testsRun, filename, desc) + self.stream.writeln( + "{0} {1} - {2}: {3} # skip".format( + color("ok", "yellow"), self.testsRun, filename, desc + ) ) elif status == "EXPECTED_FAILURE": - self.stream.writeln("{0} {1} - {2}: {3} # TODO".format( - color("not ok", "yellow"), self.testsRun, filename, desc) + self.stream.writeln( + "{0} {1} - {2}: {3} # TODO".format( + color("not ok", "yellow"), self.testsRun, filename, desc + ) ) elif status == "UNEXPECTED_SUCCESS": - self.stream.writeln("{0} {1} - {2}: {3} # FIXED".format( - color("not ok", "yellow"), self.testsRun, filename, desc) + self.stream.writeln( + "{0} {1} - {2}: {3} # FIXED".format( + color("not ok", "yellow"), self.testsRun, filename, desc + ) ) else: - self.stream.writeln("{0} {1} - {2}: {3}".format( - color("not ok", "red"), self.testsRun, filename, desc) + self.stream.writeln( + "{0} {1} - {2}: {3}".format( + color("not ok", "red"), self.testsRun, filename, desc + ) ) if exception_name: - self.stream.writeln("# {0}: {1}{2}:".format( - status, exception_name, trace_msg) + self.stream.writeln( + "# {0}: {1}{2}:".format(status, exception_name, trace_msg) ) else: self.stream.writeln("# {0}:".format(status)) @@ -185,8 +191,10 @@ class TAPTestResult(unittest.result.TestResult): line = line.replace("\\n", "\n# ") self.stream.writeln("#{0}{1}".format(padding, line)) else: - self.stream.writeln("{0} {1} - {2}: {3}".format( - color("ok", "green"), self.testsRun, filename, desc) + self.stream.writeln( + "{0} {1} - {2}: {3}".format( + color("ok", "green"), self.testsRun, filename, desc + ) ) # Flush all buffers to stdout @@ -223,6 +231,7 @@ class TAPTestRunner(unittest.runner.TextTestRunner): Inherits from TextTestRunner the default runner. """ + resultclass = TAPTestResult def __init__(self, stream=sys.stdout, *args, **kwargs): diff --git a/test/sorting.test.py b/test/sorting.test.py index 67799e0a4..f8927d04f 100755 --- a/test/sorting.test.py +++ b/test/sorting.test.py @@ -30,6 +30,7 @@ import os import re import unittest import time + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -43,6 +44,7 @@ class MetaTestSorting(MetaTest): Creates test_methods in the TestCase class dynamically named after the filter used. """ + @staticmethod def make_function(classname, *args, **kwargs): _filter, expectations = args @@ -82,123 +84,117 @@ class TestSorting(TestCase): TESTS = ( # Filter # Expected matches/outputs - # Single sort column. - ('priority-', ('(?:one.+four|four.+one).+two.+three.+zero',)), - ('priority+', ('zero.+three.+two.+(?:one.+four|four.+one)',)), - ('project-', ('(?:three.+four|four.+three).+two.+one.+zero',)), - ('project+', ('zero.+one.+two.+(?:three.+four|four.+three)',)), - ('start-', ('one.+zero', 'one.+two', 'one.+three', 'one.+four',)), - ('start+', ('one.+zero', 'one.+two', 'one.+three', 'one.+four',)), - ('due-', ('three.+(?:two.+four|four.+two).+one.+zero',)), - ('due+', ('one.+(?:two.+four|four.+two).+three.+zero',)), - ('description-', ('zero.+two.+three.+one.+four',)), - ('description+', ('four.+one.+three.+two.+zero',)), - + ("priority-", ("(?:one.+four|four.+one).+two.+three.+zero",)), + ("priority+", ("zero.+three.+two.+(?:one.+four|four.+one)",)), + ("project-", ("(?:three.+four|four.+three).+two.+one.+zero",)), + ("project+", ("zero.+one.+two.+(?:three.+four|four.+three)",)), + ( + "start-", + ( + "one.+zero", + "one.+two", + "one.+three", + "one.+four", + ), + ), + ( + "start+", + ( + "one.+zero", + "one.+two", + "one.+three", + "one.+four", + ), + ), + ("due-", ("three.+(?:two.+four|four.+two).+one.+zero",)), + ("due+", ("one.+(?:two.+four|four.+two).+three.+zero",)), + ("description-", ("zero.+two.+three.+one.+four",)), + ("description+", ("four.+one.+three.+two.+zero",)), # Two sort columns. - ('priority-,project-', ('four.+one.+two.+three.+zero',)), - ('priority-,project+', ('one.+four.+two.+three.+zero',)), - ('priority+,project-', ('zero.+three.+two.+four.+one',)), - ('priority+,project+', ('zero.+three.+two.+one.+four',)), - - ('priority-,start-', ('one.+four.+two.+three.+zero',)), - ('priority-,start+', ('one.+four.+two.+three.+zero',)), - ('priority+,start-', ('zero.+three.+two.+one.+four',)), - ('priority+,start+', ('zero.+three.+two.+one.+four',)), - - ('priority-,due-', ('four.+one.+two.+three.+zero',)), - ('priority-,due+', ('one.+four.+two.+three.+zero',)), - ('priority+,due-', ('zero.+three.+two.+four.+one',)), - ('priority+,due+', ('zero.+three.+two.+one.+four',)), - - ('priority-,description-', ('one.+four.+two.+three.+zero',)), - ('priority-,description+', ('four.+one.+two.+three.+zero',)), - ('priority+,description-', ('zero.+three.+two.+one.+four',)), - ('priority+,description+', ('zero.+three.+two.+four.+one',)), - - ('project-,priority-', ('four.+three.+two.+one.+zero',)), - ('project-,priority+', ('three.+four.+two.+one.+zero',)), - ('project+,priority-', ('zero.+one.+two.+four.+three',)), - ('project+,priority+', ('zero.+one.+two.+three.+four',)), - - ('project-,start-', ('three.+four.+two.+one.+zero',)), - ('project-,start+', ('(?:four.+three|three.+four).+two.+one.+zero',)), - ('project+,start-', ('zero.+one.+two.+three.+four',)), - ('project+,start+', ('zero.+one.+two.+(?:four.+three|three.+four)',)), - - ('project-,due-', ('three.+four.+two.+one.+zero',)), - ('project-,due+', ('four.+three.+two.+one.+zero',)), - ('project+,due-', ('zero.+one.+two.+three.+four',)), - ('project+,due+', ('zero.+one.+two.+four.+three',)), - - ('project-,description-', ('three.+four.+two.+one.+zero',)), - ('project-,description+', ('four.+three.+two.+one.+zero',)), - ('project+,description-', ('zero.+one.+two.+three.+four',)), - ('project+,description+', ('zero.+one.+two.+four.+three',)), - - ('start-,priority-', ('one.+four.+two.+three.+zero',)), - ('start-,priority+', ('one.+zero.+three.+two.+four',)), - ('start+,priority-', ('one.+four.+two.+three.+zero',)), - ('start+,priority+', ('one.+zero.+three.+two.+four',)), - - ('start-,project-', ('one.+(?:three.+four|four.+three).+two.+zero',)), - ('start-,project+', ('one.+zero.+two.+(?:three.+four|four.+three)',)), - ('start+,project-', ('one.+(?:three.+four|four.+three).+two.+zero',)), - ('start+,project+', ('one.+zero.+two.+(?:three.+four|four.+three)',)), - - ('start-,due-', ('one.+three.+(?:four.+two|two.+four).+zero',)), - ('start-,due+', ('one.+(?:four.+two|two.+four).+three.+zero',)), - ('start+,due-', ('one.+three.+(?:four.+two|two.+four).+zero',)), - ('start+,due+', ('one.+(?:four.+two|two.+four).+three.+zero',)), - - ('start-,description-', ('one.+zero.+two.+three.+four',)), - ('start-,description+', ('one.+four.+three.+two.+zero',)), - ('start+,description-', ('one.+zero.+two.+three.+four',)), - ('start+,description+', ('one.+four.+three.+two.+zero',)), - - ('due-,priority-', ('three.+four.+two.+one.+zero',)), - ('due-,priority+', ('three.+two.+four.+one.+zero',)), - ('due+,priority-', ('one.+four.+two.+three.+zero',)), - ('due+,priority+', ('one.+two.+four.+three.+zero',)), - - ('due-,project-', ('three.+four.+two.+one.+zero',)), - ('due-,project+', ('three.+two.+four.+one.+zero',)), - ('due+,project-', ('one.+four.+two.+three.+zero',)), - ('due+,project+', ('one.+two.+four.+three.+zero',)), - - ('due-,start-', ('three.+(?:four.+two|two.+four).+one.+zero',)), - ('due-,start+', ('three.+(?:four.+two|two.+four).+one.+zero',)), - ('due+,start-', ('one.+(?:four.+two|two.+four).+three.+zero',)), - ('due+,start+', ('one.+(?:four.+two|two.+four).+three.+zero',)), - - ('due-,description-', ('three.+two.+four.+one.+zero',)), - ('due-,description+', ('three.+four.+two.+one.+zero',)), - ('due+,description-', ('one.+two.+four.+three.+zero',)), - ('due+,description+', ('one.+four.+two.+three.+zero',)), - - ('description-,priority-', ('zero.+two.+three.+one.+four',)), - ('description-,priority+', ('zero.+two.+three.+one.+four',)), - ('description+,priority-', ('four.+one.+three.+two.+zero',)), - ('description+,priority+', ('four.+one.+three.+two.+zero',)), - - ('description-,project-', ('zero.+two.+three.+one.+four',)), - ('description-,project+', ('zero.+two.+three.+one.+four',)), - ('description+,project-', ('four.+one.+three.+two.+zero',)), - ('description+,project+', ('four.+one.+three.+two.+zero',)), - - ('description-,start-', ('zero.+two.+three.+one.+four',)), - ('description-,start+', ('zero.+two.+three.+one.+four',)), - ('description+,start-', ('four.+one.+three.+two.+zero',)), - ('description+,start+', ('four.+one.+three.+two.+zero',)), - - ('description-,due-', ('zero.+two.+three.+one.+four',)), - ('description-,due+', ('zero.+two.+three.+one.+four',)), - ('description+,due-', ('four.+one.+three.+two.+zero',)), - ('description+,due+', ('four.+one.+three.+two.+zero',)), - + ("priority-,project-", ("four.+one.+two.+three.+zero",)), + ("priority-,project+", ("one.+four.+two.+three.+zero",)), + ("priority+,project-", ("zero.+three.+two.+four.+one",)), + ("priority+,project+", ("zero.+three.+two.+one.+four",)), + ("priority-,start-", ("one.+four.+two.+three.+zero",)), + ("priority-,start+", ("one.+four.+two.+three.+zero",)), + ("priority+,start-", ("zero.+three.+two.+one.+four",)), + ("priority+,start+", ("zero.+three.+two.+one.+four",)), + ("priority-,due-", ("four.+one.+two.+three.+zero",)), + ("priority-,due+", ("one.+four.+two.+three.+zero",)), + ("priority+,due-", ("zero.+three.+two.+four.+one",)), + ("priority+,due+", ("zero.+three.+two.+one.+four",)), + ("priority-,description-", ("one.+four.+two.+three.+zero",)), + ("priority-,description+", ("four.+one.+two.+three.+zero",)), + ("priority+,description-", ("zero.+three.+two.+one.+four",)), + ("priority+,description+", ("zero.+three.+two.+four.+one",)), + ("project-,priority-", ("four.+three.+two.+one.+zero",)), + ("project-,priority+", ("three.+four.+two.+one.+zero",)), + ("project+,priority-", ("zero.+one.+two.+four.+three",)), + ("project+,priority+", ("zero.+one.+two.+three.+four",)), + ("project-,start-", ("three.+four.+two.+one.+zero",)), + ("project-,start+", ("(?:four.+three|three.+four).+two.+one.+zero",)), + ("project+,start-", ("zero.+one.+two.+three.+four",)), + ("project+,start+", ("zero.+one.+two.+(?:four.+three|three.+four)",)), + ("project-,due-", ("three.+four.+two.+one.+zero",)), + ("project-,due+", ("four.+three.+two.+one.+zero",)), + ("project+,due-", ("zero.+one.+two.+three.+four",)), + ("project+,due+", ("zero.+one.+two.+four.+three",)), + ("project-,description-", ("three.+four.+two.+one.+zero",)), + ("project-,description+", ("four.+three.+two.+one.+zero",)), + ("project+,description-", ("zero.+one.+two.+three.+four",)), + ("project+,description+", ("zero.+one.+two.+four.+three",)), + ("start-,priority-", ("one.+four.+two.+three.+zero",)), + ("start-,priority+", ("one.+zero.+three.+two.+four",)), + ("start+,priority-", ("one.+four.+two.+three.+zero",)), + ("start+,priority+", ("one.+zero.+three.+two.+four",)), + ("start-,project-", ("one.+(?:three.+four|four.+three).+two.+zero",)), + ("start-,project+", ("one.+zero.+two.+(?:three.+four|four.+three)",)), + ("start+,project-", ("one.+(?:three.+four|four.+three).+two.+zero",)), + ("start+,project+", ("one.+zero.+two.+(?:three.+four|four.+three)",)), + ("start-,due-", ("one.+three.+(?:four.+two|two.+four).+zero",)), + ("start-,due+", ("one.+(?:four.+two|two.+four).+three.+zero",)), + ("start+,due-", ("one.+three.+(?:four.+two|two.+four).+zero",)), + ("start+,due+", ("one.+(?:four.+two|two.+four).+three.+zero",)), + ("start-,description-", ("one.+zero.+two.+three.+four",)), + ("start-,description+", ("one.+four.+three.+two.+zero",)), + ("start+,description-", ("one.+zero.+two.+three.+four",)), + ("start+,description+", ("one.+four.+three.+two.+zero",)), + ("due-,priority-", ("three.+four.+two.+one.+zero",)), + ("due-,priority+", ("three.+two.+four.+one.+zero",)), + ("due+,priority-", ("one.+four.+two.+three.+zero",)), + ("due+,priority+", ("one.+two.+four.+three.+zero",)), + ("due-,project-", ("three.+four.+two.+one.+zero",)), + ("due-,project+", ("three.+two.+four.+one.+zero",)), + ("due+,project-", ("one.+four.+two.+three.+zero",)), + ("due+,project+", ("one.+two.+four.+three.+zero",)), + ("due-,start-", ("three.+(?:four.+two|two.+four).+one.+zero",)), + ("due-,start+", ("three.+(?:four.+two|two.+four).+one.+zero",)), + ("due+,start-", ("one.+(?:four.+two|two.+four).+three.+zero",)), + ("due+,start+", ("one.+(?:four.+two|two.+four).+three.+zero",)), + ("due-,description-", ("three.+two.+four.+one.+zero",)), + ("due-,description+", ("three.+four.+two.+one.+zero",)), + ("due+,description-", ("one.+two.+four.+three.+zero",)), + ("due+,description+", ("one.+four.+two.+three.+zero",)), + ("description-,priority-", ("zero.+two.+three.+one.+four",)), + ("description-,priority+", ("zero.+two.+three.+one.+four",)), + ("description+,priority-", ("four.+one.+three.+two.+zero",)), + ("description+,priority+", ("four.+one.+three.+two.+zero",)), + ("description-,project-", ("zero.+two.+three.+one.+four",)), + ("description-,project+", ("zero.+two.+three.+one.+four",)), + ("description+,project-", ("four.+one.+three.+two.+zero",)), + ("description+,project+", ("four.+one.+three.+two.+zero",)), + ("description-,start-", ("zero.+two.+three.+one.+four",)), + ("description-,start+", ("zero.+two.+three.+one.+four",)), + ("description+,start-", ("four.+one.+three.+two.+zero",)), + ("description+,start+", ("four.+one.+three.+two.+zero",)), + ("description-,due-", ("zero.+two.+three.+one.+four",)), + ("description-,due+", ("zero.+two.+three.+one.+four",)), + ("description+,due-", ("four.+one.+three.+two.+zero",)), + ("description+,due+", ("four.+one.+three.+two.+zero",)), # Four sort columns. - ('start+,project+,due+,priority+', ('one.+zero.+two.+four.+three',)), - ('project+,due+,priority+,start+', ('zero.+one.+two.+four.+three',)), + ("start+,project+,due+,priority+", ("one.+zero.+two.+four.+three",)), + ("project+,due+,priority+,start+", ("zero.+one.+two.+four.+three",)), ) @@ -243,8 +239,8 @@ class TestBug438(TestCase): ("entry-", ("one newer.+one older",)), ("start+", ("two older.+two newer",)), ("start-", ("two newer.+two older",)), - ("end+", ("three older.+three newer",)), - ("end-", ("three newer.+three older",)), + ("end+", ("three older.+three newer",)), + ("end-", ("three newer.+three older",)), } @@ -258,13 +254,17 @@ class TestSortNone(TestCase): self.t("add two") self.t("add three") code, out, err = self.t("_get 1.uuid 2.uuid 3.uuid") - uuid1, uuid2, uuid3 = out.strip().split(' ') - code, out, err = self.t("%s %s %s list rc.report.list.sort:none rc.report.list.columns:id,description rc.report.list.labels:id,desc" % (uuid2, uuid3, uuid1)) - self.assertRegex(out, ' 2 two\n 3 three\n 1 one') + uuid1, uuid2, uuid3 = out.strip().split(" ") + code, out, err = self.t( + "%s %s %s list rc.report.list.sort:none rc.report.list.columns:id,description rc.report.list.labels:id,desc" + % (uuid2, uuid3, uuid1) + ) + self.assertRegex(out, " 2 two\n 3 three\n 1 one") if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/special.test.py b/test/special.test.py index d0dab40dc..a1ed28130 100755 --- a/test/special.test.py +++ b/test/special.test.py @@ -28,24 +28,26 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase + class TestSpecialTags(TestCase): @classmethod def setUpClass(cls): """Executed once before any test in the class""" cls.t = Task() cls.t.config("color.keyword.red", "red") - cls.t.config("color.alternate", "") - cls.t.config("color.tagged", "") - cls.t.config("color.pri.H", "") - cls.t.config("color.completed", "") - cls.t.config("nag", "NAG") - cls.t.config("color", "1") - cls.t.config("_forcecolor", "1") + cls.t.config("color.alternate", "") + cls.t.config("color.tagged", "") + cls.t.config("color.pri.H", "") + cls.t.config("color.completed", "") + cls.t.config("nag", "NAG") + cls.t.config("color", "1") + cls.t.config("_forcecolor", "1") def test_nocolor(self): self.t("add should have no red +nocolor priority:H") @@ -63,6 +65,7 @@ class TestSpecialTags(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/start.test.py b/test/start.test.py index e3a1db57b..c87b58f36 100755 --- a/test/start.test.py +++ b/test/start.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -81,9 +82,9 @@ class TestStart(TestCase): def test_journal_annotations(self): """Verify journal start/stop annotations are used""" - self.t.config("journal.time", "1") + self.t.config("journal.time", "1") self.t.config("journal.time.start.annotation", "Nu kör vi") - self.t.config("journal.time.stop.annotation", "Nu stannar vi") + self.t.config("journal.time.stop.annotation", "Nu stannar vi") self.t("add one") self.t("1 start") @@ -97,7 +98,7 @@ class TestStart(TestCase): def test_start_remove_end(self): """Verify that starting a task removes end timestamp""" self.t("add one") - uuid = self.t('_get 1.uuid')[1].strip() + uuid = self.t("_get 1.uuid")[1].strip() self.t("1 done") task = self.t.export()[0] @@ -129,7 +130,7 @@ class TestActiveTaskHandling(TestCase): def test_start_nothing(self): """Verify error message when no tasks are specified""" - code, out, err = self.t.runError ("999 start") + code, out, err = self.t.runError("999 start") self.assertIn("No tasks specified.", err) def test_start_started(self): @@ -160,6 +161,7 @@ class TestFeature608(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/stats.test.py b/test/stats.test.py index 54d5da70b..1d2f32e9c 100755 --- a/test/stats.test.py +++ b/test/stats.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -58,6 +59,7 @@ class TestStatisticsCommand(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/substitute.test.py b/test/substitute.test.py index 475d1eb5d..845076b25 100755 --- a/test/substitute.test.py +++ b/test/substitute.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -107,6 +108,7 @@ class TestBug441(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/sugar.test.py b/test/sugar.test.py index 326ae4f03..742138593 100755 --- a/test/sugar.test.py +++ b/test/sugar.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -38,9 +39,9 @@ class TestSugar(TestCase): def setUp(self): """Executed before each test in the class""" self.t = Task() - self.t('add one') - self.t('add two') - self.t('add three') + self.t("add one") + self.t("add two") + self.t("add three") def test_empty_conjunction(self): """Test syntax that mathematicians find sane and expected""" @@ -70,6 +71,7 @@ class TestSugar(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/summary.test.py b/test/summary.test.py index d83666439..8464aa51a 100755 --- a/test/summary.test.py +++ b/test/summary.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -72,9 +73,7 @@ class TestBug1904(TestCase): self.t("add pro:a.b test2") def validate_order(self, out): - order = ("a-b", - "a", - " b") + order = ("a-b", "a", " b") lines = out.splitlines(True) # position where project names start on the lines list @@ -85,8 +84,10 @@ class TestBug1904(TestCase): self.assertTrue( lines[pos].startswith(proj), - msg=("Project '{0}' is not in line #{1} or has an unexpected " - "indentation.{2}".format(proj, pos, out)) + msg=( + "Project '{0}' is not in line #{1} or has an unexpected " + "indentation.{2}".format(proj, pos, out) + ), ) def test_project_eval(self): @@ -98,6 +99,7 @@ class TestBug1904(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/t.test.cpp b/test/t.test.cpp index e0e35c4c0..d56015263 100644 --- a/test/t.test.cpp +++ b/test/t.test.cpp @@ -27,90 +27,89 @@ #include // cmake.h include header must come first -#include #include +#include #include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest test (48); +int main(int, char**) { + UnitTest test(48); Context context; Context::setContext(&context); // Ensure environment has no influence. - unsetenv ("TASKDATA"); - unsetenv ("TASKRC"); + unsetenv("TASKDATA"); + unsetenv("TASKRC"); - test.is ((int)Task::textToStatus ("pending"), (int)Task::pending, "textToStatus pending"); - test.is ((int)Task::textToStatus ("completed"), (int)Task::completed, "textToStatus completed"); - test.is ((int)Task::textToStatus ("deleted"), (int)Task::deleted, "textToStatus deleted"); - test.is ((int)Task::textToStatus ("recurring"), (int)Task::recurring, "textToStatus recurring"); + test.is((int)Task::textToStatus("pending"), (int)Task::pending, "textToStatus pending"); + test.is((int)Task::textToStatus("completed"), (int)Task::completed, "textToStatus completed"); + test.is((int)Task::textToStatus("deleted"), (int)Task::deleted, "textToStatus deleted"); + test.is((int)Task::textToStatus("recurring"), (int)Task::recurring, "textToStatus recurring"); - test.is (Task::statusToText (Task::pending), "pending", "statusToText pending"); - test.is (Task::statusToText (Task::completed), "completed", "statusToText completed"); - test.is (Task::statusToText (Task::deleted), "deleted", "statusToText deleted"); - test.is (Task::statusToText (Task::recurring), "recurring", "statusToText recurring"); + test.is(Task::statusToText(Task::pending), "pending", "statusToText pending"); + test.is(Task::statusToText(Task::completed), "completed", "statusToText completed"); + test.is(Task::statusToText(Task::deleted), "deleted", "statusToText deleted"); + test.is(Task::statusToText(Task::recurring), "recurring", "statusToText recurring"); -/* + /* -TODO Task::composeCSV -TODO Task::composeYAML -TODO Task::id -TODO Task::*Status -TODO Task::*Tag* -TODO Task::*Annotation* + TODO Task::composeCSV + TODO Task::composeYAML + TODO Task::id + TODO Task::*Status + TODO Task::*Tag* + TODO Task::*Annotation* -TODO Task::addDependency -TODO Task::addDependency -TODO Task::removeDependency -TODO Task::removeDependency -TODO Task::getDependencies -TODO Task::getDependencies + TODO Task::addDependency + TODO Task::addDependency + TODO Task::removeDependency + TODO Task::removeDependency + TODO Task::getDependencies + TODO Task::getDependencies -TODO Task::urgency + TODO Task::urgency -TODO Task::encode -TODO Task::decode + TODO Task::encode + TODO Task::decode -*/ + */ // Task::operator== - Task left ("{\"one\":\"1\", \"two\":\"2\", \"three\":\"3\"}"); - Task right (left); - test.ok (left == right, "left == right -> true"); - left.set ("one", "1.0"); - test.notok (left == right, "left == right -> false"); + Task left("{\"one\":\"1\", \"two\":\"2\", \"three\":\"3\"}"); + Task right(left); + test.ok(left == right, "left == right -> true"); + left.set("one", "1.0"); + test.notok(left == right, "left == right -> false"); //////////////////////////////////////////////////////////////////////////////// Task task; // Task::set task = Task(); - task.set ("name", "value"); - test.is (task.composeJSON (), "{\"name\":\"value\"}", "Task::set"); + task.set("name", "value"); + test.is(task.composeJSON(), "{\"name\":\"value\"}", "Task::set"); // Task::has - test.ok (task.has ("name"), "Task::has"); - test.notok (task.has ("woof"), "Task::has not"); + test.ok(task.has("name"), "Task::has"); + test.notok(task.has("woof"), "Task::has not"); // Task::get_int - task.set ("one", 1); - test.is (task.composeJSON (), R"({"name":"value","one":"1"})", "Task::set"); - test.is (task.get_int ("one"), 1, "Task::get_int"); + task.set("one", 1); + test.is(task.composeJSON(), R"({"name":"value","one":"1"})", "Task::set"); + test.is(task.get_int("one"), 1, "Task::get_int"); // Task::get_ulong - task.set ("two", "4294967295"); - test.is (task.composeJSON (), R"({"name":"value","one":"1","two":"4294967295"})", "Task::set"); - test.is ((size_t)task.get_ulong ("two"), (size_t)4294967295UL, "Task::get_ulong"); + task.set("two", "4294967295"); + test.is(task.composeJSON(), R"({"name":"value","one":"1","two":"4294967295"})", "Task::set"); + test.is((size_t)task.get_ulong("two"), (size_t)4294967295UL, "Task::get_ulong"); // Task::remove - task.remove ("one"); - task.remove ("two"); - test.is (task.composeJSON (), "{\"name\":\"value\"}", "Task::remove"); + task.remove("one"); + task.remove("two"); + test.is(task.composeJSON(), "{\"name\":\"value\"}", "Task::remove"); // Task::all - test.is (task.all ().size (), (size_t)1, "Task::all size"); + test.is(task.all().size(), (size_t)1, "Task::all size"); //////////////////////////////////////////////////////////////////////////////// @@ -120,34 +119,51 @@ TODO Task::decode Task::attributes["uuid"] = "string"; bool good = true; - try {Task t4 ("{}");} - catch (const std::string& e){test.diag (e); good = false;} - test.ok (good, "Task::Task ('{}')"); + try { + Task t4("{}"); + } catch (const std::string& e) { + test.diag(e); + good = false; + } + test.ok(good, "Task::Task ('{}')"); good = true; - try {Task t5 (R"({"uuid":"00000000-0000-0000-000000000001","description":"foo","entry":"1234567890"})");} - catch (const std::string& e){test.diag (e); good = false;} - test.ok (good, "Task::Task ('{}')"); + try { + Task t5( + R"({"uuid":"00000000-0000-0000-000000000001","description":"foo","entry":"1234567890"})"); + } catch (const std::string& e) { + test.diag(e); + good = false; + } + test.ok(good, "Task::Task ('{}')"); // Verify tag handling is correct Task t6; - t6.set ("entry", "20130602T224000Z"); - t6.set ("description", "DESC"); - t6.addTag ("tag1"); - test.is (t6.composeJSON (), R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1"]})", "JSON good"); + t6.set("entry", "20130602T224000Z"); + t6.set("description", "DESC"); + t6.addTag("tag1"); + test.is(t6.composeJSON(), R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1"]})", + "JSON good"); - t6.addTag ("tag2"); - test.is (t6.composeJSON (), R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1","tag2"]})", "JSON good"); + t6.addTag("tag2"); + test.is(t6.composeJSON(), + R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1","tag2"]})", + "JSON good"); good = true; Task t7; - try {t7 = Task (R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1","tag2"]})");} - catch (const std::string& e){test.diag (e); good = false;} - test.ok (good, "Task::Task ('{two tags}')"); - test.is (t7.composeJSON (), R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1","tag2"]})", "JSON good"); + try { + t7 = Task(R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1","tag2"]})"); + } catch (const std::string& e) { + test.diag(e); + good = false; + } + test.ok(good, "Task::Task ('{two tags}')"); + test.is(t7.composeJSON(), + R"({"description":"DESC","entry":"20130602T224000Z","tags":["tag1","tag2"]})", + "JSON good"); return 0; } //////////////////////////////////////////////////////////////////////////////// - diff --git a/test/tag.test.py b/test/tag.test.py index ba517d39e..32e4e0163 100755 --- a/test/tag.test.py +++ b/test/tag.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -41,15 +42,13 @@ class TestTags(TestCase): self.t = Task() def split_tags(self, tags): - return sorted(tags.strip().split(',')) + return sorted(tags.strip().split(",")) def test_tag_manipulation(self): """Test addition and removal of tags""" self.t("add +one This +two is a test +three") code, out, err = self.t("_get 1.tags") - self.assertEqual( - sorted(["one", "two", "three"]), - self.split_tags(out)) + self.assertEqual(sorted(["one", "two", "three"]), self.split_tags(out)) # Remove tags. self.t("1 modify -three -two -one") @@ -59,9 +58,7 @@ class TestTags(TestCase): # Add tags. self.t("1 modify +four +five +six") code, out, err = self.t("_get 1.tags") - self.assertEqual( - sorted(["four", "five", "six"]), - self.split_tags(out)) + self.assertEqual(sorted(["four", "five", "six"]), self.split_tags(out)) # Remove tags. self.t("1 modify -four -five -six") @@ -81,9 +78,7 @@ class TestTags(TestCase): """2655: Test bulk removal of tags""" self.t("add +one This +two is a test +three") code, out, err = self.t("_get 1.tags") - self.assertEqual( - sorted(["one", "two", "three"]), - self.split_tags(out)) + self.assertEqual(sorted(["one", "two", "three"]), self.split_tags(out)) # Remove all tags in bulk self.t("1 modify tags:") @@ -432,7 +427,7 @@ class TestVirtualTagUDA(TestCase): def setUp(self): """Executed before each test in the class""" self.t = Task() - self.t.config("uda.animal.type", "string") + self.t.config("uda.animal.type", "string") self.t.config("uda.animal.label", "Animal") self.t("add one animal:donkey") self.t("add two") @@ -448,7 +443,9 @@ class TestVirtualTagORPHAN(TestCase): def setUp(self): """Executed before each test in the class""" self.t = Task() - self.t("add one rc.uda.animal.type:string rc.uda.animal.label:Animal animal:donkey") + self.t( + "add one rc.uda.animal.type:string rc.uda.animal.label:Animal animal:donkey" + ) self.t("add two") def test_virtual_tag_ORPHAN(self): @@ -473,13 +470,13 @@ class Test285(TestCase): # due:1month - - - - - - - ? # due:1year - - - - - - - - - cls.t('add due_last_week due:-1week') - cls.t('add due_yesterday due:-1day') - cls.t('add due_earlier_today due:today') - cls.t('add due_later_today due:tomorrow') - cls.t('add due_three_days due:3days') - cls.t('add due_next_month due:1month') - cls.t('add due_next_year due:1year') + cls.t("add due_last_week due:-1week") + cls.t("add due_yesterday due:-1day") + cls.t("add due_earlier_today due:today") + cls.t("add due_later_today due:tomorrow") + cls.t("add due_three_days due:3days") + cls.t("add due_next_month due:1month") + cls.t("add due_next_year due:1year") def test_overdue(self): """285: +OVERDUE""" @@ -537,8 +534,8 @@ class TestListAllTags(TestCase): def test_list_all_tags(self): """Verify the 'tags' command obeys 'rc.list.all.tags' - Create a data set of two tasks, with unique tags, one - pending, one completed. + Create a data set of two tasks, with unique tags, one + pending, one completed. """ self.t("add +t1 one") self.t("add +t2 two") @@ -570,6 +567,7 @@ class TestBug1700(TestCase): self.assertNotIn("tag1", out) self.assertIn("tag2,tag3", out) + class TestBug818(TestCase): def setUp(self): """Executed before each test in the class""" @@ -599,6 +597,7 @@ class TestBug818(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/taskrc.test.py b/test/taskrc.test.py index 4f88a1632..044681881 100755 --- a/test/taskrc.test.py +++ b/test/taskrc.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -48,6 +49,7 @@ class TestTaskrc(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/tc.test.cpp b/test/tc.test.cpp index 724105404..e3351e101 100644 --- a/test/tc.test.cpp +++ b/test/tc.test.cpp @@ -27,19 +27,20 @@ #include // cmake.h include header must come first -#include #include #include -#include "test.h" + +#include + #include "tc/Replica.h" -#include "tc/WorkingSet.h" #include "tc/Task.h" +#include "tc/WorkingSet.h" #include "tc/util.h" +#include "test.h" //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (23); +int main(int, char**) { + UnitTest t(23); // This function contains unit tests for the various bits of the wrappers for // taskchampion-lib (that is, for `src/tc/*.cpp`). @@ -47,69 +48,69 @@ int main (int, char**) //// util { - auto s1 = std::string ("a\0string!"); - auto stc = tc::string2tc (s1); - auto s2 = tc::tc2string (stc); - t.is (s1, s2, "round-trip to tc string and back (containing an embedded NUL)"); + auto s1 = std::string("a\0string!"); + auto stc = tc::string2tc(s1); + auto s2 = tc::tc2string(stc); + t.is(s1, s2, "round-trip to tc string and back (containing an embedded NUL)"); } { - auto s1 = std::string ("62123ec9-c443-4f7e-919a-35362a8bef8d"); - auto tcuuid = tc::uuid2tc (s1); - auto s2 = tc::tc2uuid (tcuuid); + auto s1 = std::string("62123ec9-c443-4f7e-919a-35362a8bef8d"); + auto tcuuid = tc::uuid2tc(s1); + auto s2 = tc::tc2uuid(tcuuid); t.is(s1, s2, "round-trip to TCUuid and back"); } //// Replica - auto rep = tc::Replica (); - t.pass ("replica constructed"); + auto rep = tc::Replica(); + t.pass("replica constructed"); auto maybe_task = rep.get_task("24478a28-4609-4257-bc19-44ec51391431"); t.notok(maybe_task.has_value(), "task with fixed uuid does not exist"); - auto task = rep.new_task (tc::Status::Pending, "a test"); - t.pass ("new task constructed"); - t.is (task.get_description (), std::string ("a test"), "task description round-trip"); - t.is (task.get_status (), tc::Status::Pending, "task status round-trip"); + auto task = rep.new_task(tc::Status::Pending, "a test"); + t.pass("new task constructed"); + t.is(task.get_description(), std::string("a test"), "task description round-trip"); + t.is(task.get_status(), tc::Status::Pending, "task status round-trip"); auto uuid = task.get_uuid(); - auto maybe_task2 = rep.get_task (uuid); + auto maybe_task2 = rep.get_task(uuid); t.ok(maybe_task2.has_value(), "task lookup by uuid finds task"); - t.is ((*maybe_task2).get_description (), std::string ("a test"), "task description round-trip"); + t.is((*maybe_task2).get_description(), std::string("a test"), "task description round-trip"); - rep.rebuild_working_set (true); - t.pass ("rebuild_working_set"); + rep.rebuild_working_set(true); + t.pass("rebuild_working_set"); - auto tasks = rep.all_tasks (); - t.is ((int)tasks.size(), 1, "all_tasks returns one task"); + auto tasks = rep.all_tasks(); + t.is((int)tasks.size(), 1, "all_tasks returns one task"); //// Task task = std::move(tasks[0]); - t.is (task.get_uuid(), uuid, "returned task has correct uuid"); - t.is (task.get_status(), tc::Status::Pending, "returned task is pending"); - auto map = task.get_taskmap (); - t.is (map["description"], "a test", "task description in taskmap"); - t.is (task.get_description(), "a test", "returned task has correct description"); - t.is (task.is_waiting(), false, "task is not waiting"); - t.is (task.is_active(), false, "task is not active"); + t.is(task.get_uuid(), uuid, "returned task has correct uuid"); + t.is(task.get_status(), tc::Status::Pending, "returned task is pending"); + auto map = task.get_taskmap(); + t.is(map["description"], "a test", "task description in taskmap"); + t.is(task.get_description(), "a test", "returned task has correct description"); + t.is(task.is_waiting(), false, "task is not waiting"); + t.is(task.is_active(), false, "task is not active"); //// WorkingSet - auto ws = rep.working_set (); + auto ws = rep.working_set(); - t.is (ws.len (), (size_t)1, "WorkingSet::len"); - t.is (ws.largest_index (), (size_t)1, "WorkingSet::largest_index"); - t.is (ws.by_index (1).value(), uuid, "WorkingSet::by_index"); - t.is (ws.by_index (2).has_value(), false, "WorkingSet::by_index for unknown index"); - t.is (ws.by_uuid (uuid).value (), (size_t)1, "WorkingSet::by_uuid"); - t.is (ws.by_uuid ("3e18a306-e3a8-4a53-a85c-fa7c057759a2").has_value (), false, "WorkingSet::by_uuid for unknown uuid"); + t.is(ws.len(), (size_t)1, "WorkingSet::len"); + t.is(ws.largest_index(), (size_t)1, "WorkingSet::largest_index"); + t.is(ws.by_index(1).value(), uuid, "WorkingSet::by_index"); + t.is(ws.by_index(2).has_value(), false, "WorkingSet::by_index for unknown index"); + t.is(ws.by_uuid(uuid).value(), (size_t)1, "WorkingSet::by_uuid"); + t.is(ws.by_uuid("3e18a306-e3a8-4a53-a85c-fa7c057759a2").has_value(), false, + "WorkingSet::by_uuid for unknown uuid"); return 0; } //////////////////////////////////////////////////////////////////////////////// - diff --git a/test/tdb2.test.cpp b/test/tdb2.test.cpp index 03b1d9f84..e9a0b1508 100644 --- a/test/tdb2.test.cpp +++ b/test/tdb2.test.cpp @@ -27,114 +27,108 @@ #include // cmake.h include header must come first -#include -#include -#include #include +#include #include +#include + +#include Context context; -void cleardb () -{ - // Remove any residual test files. - rmdir ("./extensions"); - unlink ("./taskchampion.sqlite3"); +void cleardb() { + // Remove any residual test files. + rmdir("./extensions"); + unlink("./taskchampion.sqlite3"); } //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (12); +int main(int, char**) { + UnitTest t(12); Context context; Context::setContext(&context); // Ensure environment has no influence. - unsetenv ("TASKDATA"); - unsetenv ("TASKRC"); + unsetenv("TASKDATA"); + unsetenv("TASKRC"); - try - { - cleardb (); + try { + cleardb(); // Set the context to allow GC. - context.config.set ("gc", 1); - context.config.set ("debug", 1); + context.config.set("gc", 1); + context.config.set("debug", 1); - context.tdb2.open_replica (".", true); + context.tdb2.open_replica(".", true); // Try reading an empty database. - std::vector pending = context.tdb2.pending_tasks (); - std::vector completed = context.tdb2.completed_tasks (); - int num_reverts_possible = context.tdb2.num_reverts_possible (); - int num_local_changes = context.tdb2.num_local_changes (); + std::vector pending = context.tdb2.pending_tasks(); + std::vector completed = context.tdb2.completed_tasks(); + int num_reverts_possible = context.tdb2.num_reverts_possible(); + int num_local_changes = context.tdb2.num_local_changes(); - t.is ((int) pending.size (), 0, "TDB2 Read empty pending"); - t.is ((int) completed.size (), 0, "TDB2 Read empty completed"); - t.is ((int) num_reverts_possible, 0, "TDB2 Read empty undo"); - t.is ((int) num_local_changes, 0, "TDB2 Read empty backlog"); + t.is((int)pending.size(), 0, "TDB2 Read empty pending"); + t.is((int)completed.size(), 0, "TDB2 Read empty completed"); + t.is((int)num_reverts_possible, 0, "TDB2 Read empty undo"); + t.is((int)num_local_changes, 0, "TDB2 Read empty backlog"); // Add a task. - Task task (R"([description:"description" name:"value"])"); - context.tdb2.add (task); + Task task(R"([description:"description" name:"value"])"); + context.tdb2.add(task); - pending = context.tdb2.pending_tasks (); - completed = context.tdb2.completed_tasks (); - num_reverts_possible = context.tdb2.num_reverts_possible (); - num_local_changes = context.tdb2.num_local_changes (); + pending = context.tdb2.pending_tasks(); + completed = context.tdb2.completed_tasks(); + num_reverts_possible = context.tdb2.num_reverts_possible(); + num_local_changes = context.tdb2.num_local_changes(); - t.is ((int) pending.size (), 1, "TDB2 after add, 1 pending task"); - t.is ((int) completed.size (), 0, "TDB2 after add, 0 completed tasks"); - t.is ((int) num_reverts_possible, 1, "TDB2 after add, 1 revert possible"); - t.is ((int) num_local_changes, 6, "TDB2 after add, 6 local changes"); + t.is((int)pending.size(), 1, "TDB2 after add, 1 pending task"); + t.is((int)completed.size(), 0, "TDB2 after add, 0 completed tasks"); + t.is((int)num_reverts_possible, 1, "TDB2 after add, 1 revert possible"); + t.is((int)num_local_changes, 6, "TDB2 after add, 6 local changes"); - task.set ("description", "This is a test"); - context.tdb2.modify (task); + task.set("description", "This is a test"); + context.tdb2.modify(task); - pending = context.tdb2.pending_tasks (); - completed = context.tdb2.completed_tasks (); - num_reverts_possible = context.tdb2.num_reverts_possible (); - num_local_changes = context.tdb2.num_local_changes (); + pending = context.tdb2.pending_tasks(); + completed = context.tdb2.completed_tasks(); + num_reverts_possible = context.tdb2.num_reverts_possible(); + num_local_changes = context.tdb2.num_local_changes(); - t.is ((int) pending.size (), 1, "TDB2 after set, 1 pending task"); - t.is ((int) completed.size (), 0, "TDB2 after set, 0 completed tasks"); - t.is ((int) num_reverts_possible, 1, "TDB2 after set, 1 revert possible"); + t.is((int)pending.size(), 1, "TDB2 after set, 1 pending task"); + t.is((int)completed.size(), 0, "TDB2 after set, 0 completed tasks"); + t.is((int)num_reverts_possible, 1, "TDB2 after set, 1 revert possible"); // At this point, there may be 7 or 8 local changes, depending on whether // the `modified` property changed between the `add` and `modify` // invocation. That only happens if the clock ticks over to the next second // between those invocations. - t.ok (num_local_changes == 7 || num_local_changes == 8, - "TDB2 after set, 7 or 8 local changes"); + t.ok(num_local_changes == 7 || num_local_changes == 8, "TDB2 after set, 7 or 8 local changes"); // Reset for reuse. - cleardb (); - context.tdb2.open_replica (".", true); + cleardb(); + context.tdb2.open_replica(".", true); // TODO complete a task // TODO gc } - catch (const std::string& error) - { - t.diag (error); + catch (const std::string& error) { + t.diag(error); return -1; } - catch (...) - { - t.diag ("Unknown error."); + catch (...) { + t.diag("Unknown error."); return -2; } - rmdir ("./extensions"); - unlink ("./pending.data"); - unlink ("./completed.data"); - unlink ("./undo.data"); - unlink ("./backlog.data"); + rmdir("./extensions"); + unlink("./pending.data"); + unlink("./completed.data"); + unlink("./undo.data"); + unlink("./backlog.data"); return 0; } //////////////////////////////////////////////////////////////////////////////// - diff --git a/test/template.test.py b/test/template.test.py index 303795c90..ed6450d59 100755 --- a/test/template.test.py +++ b/test/template.test.py @@ -29,6 +29,7 @@ import sys import os import unittest from datetime import datetime + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -53,6 +54,7 @@ from basetest import Task, TestCase # self.assertNotRegex(t, r) # self.tap("") + class TestBugNumber(TestCase): @classmethod def setUpClass(cls): @@ -80,16 +82,16 @@ class TestBugNumber(TestCase): def test_faketime(self): """Running tests using libfaketime - WARNING: - faketime version 0.9.6 and later correctly propagates non-zero - exit codes. Please don't combine faketime tests and - self.t.runError(). + WARNING: + faketime version 0.9.6 and later correctly propagates non-zero + exit codes. Please don't combine faketime tests and + self.t.runError(). - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=750721 + https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=750721 """ self.t.faketime("-2y") - command = ("add Testing") + command = "add Testing" self.t(command) # Remove FAKETIME settings @@ -188,12 +190,12 @@ sys.exit(0) self.assertIn("/Hello/Greetings/", logs["calls"][0]["args"]) # Some message output from the hook - self.assertEqual(logs["output"]["msgs"][0], - "Hello from the template hook") + self.assertEqual(logs["output"]["msgs"][0], "Hello from the template hook") # This is what taskwarrior received - self.assertEqual(logs["output"]["json"][0]["description"], - "This is an example modify hook") + self.assertEqual( + logs["output"]["json"][0]["description"], "This is an example modify hook" + ) def test_onmodify_bad_builtin_with_log(self): """Testing a builtin hook and keeping track of its input/output @@ -216,16 +218,17 @@ sys.exit(0) hook.assertExitcode(1) # Some message output from the hook - self.assertEqual(logs["output"]["msgs"][0], - "Hello from the template hook") + self.assertEqual(logs["output"]["msgs"][0], "Hello from the template hook") # This is what taskwarrior would have used if hook finished cleanly - self.assertEqual(logs["output"]["json"][0]["description"], - "This is an example modify hook") + self.assertEqual( + logs["output"]["json"][0]["description"], "This is an example modify hook" + ) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/test.cpp b/test/test.cpp index 7f092af90..32f62c4b7 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -24,75 +24,46 @@ // //////////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include #include +#include #include +#include #include +#include + +#include +#include /////////////////////////////////////////////////////////////////////////////// -UnitTest::UnitTest () -: _planned (0) -, _counter (0) -, _passed (0) -, _failed (0) -, _skipped (0) -{ -} +UnitTest::UnitTest() : _planned(0), _counter(0), _passed(0), _failed(0), _skipped(0) {} /////////////////////////////////////////////////////////////////////////////// -UnitTest::UnitTest (int planned) -: _planned (planned) -, _counter (0) -, _passed (0) -, _failed (0) -, _skipped (0) -{ +UnitTest::UnitTest(int planned) + : _planned(planned), _counter(0), _passed(0), _failed(0), _skipped(0) { std::cout << "1.." << _planned << '\n'; } /////////////////////////////////////////////////////////////////////////////// -UnitTest::~UnitTest () -{ +UnitTest::~UnitTest() { float percentPassed = 0.0; if (_planned > 0) - percentPassed = (100.0 * _passed) / std::max (_planned, _passed + _failed + _skipped); + percentPassed = (100.0 * _passed) / std::max(_planned, _passed + _failed + _skipped); - if (_counter < _planned) - { - std::cout << "# Only " - << _counter - << " tests, out of a planned " - << _planned - << " were run.\n"; + if (_counter < _planned) { + std::cout << "# Only " << _counter << " tests, out of a planned " << _planned << " were run.\n"; _skipped += _planned - _counter; } else if (_counter > _planned) - std::cout << "# " - << _counter - << " tests were run, but only " - << _planned - << " were planned.\n"; + std::cout << "# " << _counter << " tests were run, but only " << _planned << " were planned.\n"; - std::cout << "# " - << _passed - << " passed, " - << _failed - << " failed, " - << _skipped - << " skipped. " - << std::setprecision (3) << percentPassed - << "% passed.\n"; - exit (_failed > 0); + std::cout << "# " << _passed << " passed, " << _failed << " failed, " << _skipped << " skipped. " + << std::setprecision(3) << percentPassed << "% passed.\n"; + exit(_failed > 0); } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::plan (int planned) -{ +void UnitTest::plan(int planned) { _planned = planned; _counter = 0; _passed = 0; @@ -103,429 +74,225 @@ void UnitTest::plan (int planned) } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::planMore (int extra) -{ +void UnitTest::planMore(int extra) { _planned += extra; std::cout << "1.." << _planned << '\n'; } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::ok (bool expression, const std::string& name, bool expfail /* = false */) -{ +void UnitTest::ok(bool expression, const std::string& name, bool expfail /* = false */) { ++_counter; bool success = expression; - if (success and ! expfail) - { + if (success and !expfail) { ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << name - << '\n'; - } - else - { - if (success == expfail) - ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << name - << (expfail ? (success ? " # FIXED" : " # TODO") : "") - << '\n'; + std::cout << green("ok") << " " << _counter << " - " << name << '\n'; + } else { + if (success == expfail) ++_failed; + std::cout << red("not ok") << " " << _counter << " - " << name + << (expfail ? (success ? " # FIXED" : " # TODO") : "") << '\n'; } } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::notok (bool expression, const std::string& name, bool expfail /* = false */) -{ +void UnitTest::notok(bool expression, const std::string& name, bool expfail /* = false */) { ++_counter; bool success = not expression; - if (success and ! expfail) - { + if (success and !expfail) { ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << name - << '\n'; - } - else - { - if (success == expfail) - ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << name - << (expfail ? (success ? " # FIXED" : " # TODO") : "") - << '\n'; + std::cout << green("ok") << " " << _counter << " - " << name << '\n'; + } else { + if (success == expfail) ++_failed; + std::cout << red("not ok") << " " << _counter << " - " << name + << (expfail ? (success ? " # FIXED" : " # TODO") : "") << '\n'; } } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::is (bool actual, bool expected, const std::string& name, bool expfail /* = false */) -{ +void UnitTest::is(bool actual, bool expected, const std::string& name, bool expfail /* = false */) { ++_counter; bool success = (actual == expected); - if (success and ! expfail) - { + if (success and !expfail) { ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << name - << '\n'; - } - else - { - if (success == expfail) - ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << name - << (expfail ? (success ? " # FIXED" : " # TODO") : "") - << "\n# expected: " - << expected - << "\n# got: " - << actual - << '\n'; + std::cout << green("ok") << " " << _counter << " - " << name << '\n'; + } else { + if (success == expfail) ++_failed; + std::cout << red("not ok") << " " << _counter << " - " << name + << (expfail ? (success ? " # FIXED" : " # TODO") : "") << "\n# expected: " << expected + << "\n# got: " << actual << '\n'; } } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::is (size_t actual, size_t expected, const std::string& name, bool expfail /* = false */) -{ +void UnitTest::is(size_t actual, size_t expected, const std::string& name, + bool expfail /* = false */) { ++_counter; bool success = (actual == expected); - if (success and ! expfail) - { + if (success and !expfail) { ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << name - << '\n'; - } - else - { - if (success == expfail) - ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << name - << (expfail ? (success ? " # FIXED" : " # TODO") : "") - << "\n# expected: " - << expected - << "\n# got: " - << actual - << '\n'; + std::cout << green("ok") << " " << _counter << " - " << name << '\n'; + } else { + if (success == expfail) ++_failed; + std::cout << red("not ok") << " " << _counter << " - " << name + << (expfail ? (success ? " # FIXED" : " # TODO") : "") << "\n# expected: " << expected + << "\n# got: " << actual << '\n'; } } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::is (int actual, int expected, const std::string& name, bool expfail /* = false */) -{ +void UnitTest::is(int actual, int expected, const std::string& name, bool expfail /* = false */) { ++_counter; bool success = (actual == expected); - if (success and ! expfail) - { + if (success and !expfail) { ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << name - << '\n'; - } - else - { - if (success == expfail) - ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << name - << (expfail ? (success ? " # FIXED" : " # TODO") : "") - << "\n# expected: " - << expected - << "\n# got: " - << actual - << '\n'; + std::cout << green("ok") << " " << _counter << " - " << name << '\n'; + } else { + if (success == expfail) ++_failed; + std::cout << red("not ok") << " " << _counter << " - " << name + << (expfail ? (success ? " # FIXED" : " # TODO") : "") << "\n# expected: " << expected + << "\n# got: " << actual << '\n'; } } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::is (double actual, double expected, const std::string& name, bool expfail /* = false */) -{ +void UnitTest::is(double actual, double expected, const std::string& name, + bool expfail /* = false */) { ++_counter; bool success = (actual == expected); - if (success and ! expfail) - { + if (success and !expfail) { ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << name - << '\n'; - } - else - { - if (success == expfail) - ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << name - << (expfail ? (success ? " # FIXED" : " # TODO") : "") - << "\n# expected: " - << expected - << "\n# got: " - << actual - << '\n'; + std::cout << green("ok") << " " << _counter << " - " << name << '\n'; + } else { + if (success == expfail) ++_failed; + std::cout << red("not ok") << " " << _counter << " - " << name + << (expfail ? (success ? " # FIXED" : " # TODO") : "") << "\n# expected: " << expected + << "\n# got: " << actual << '\n'; } } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::is (double actual, double expected, double tolerance, const std::string& name, bool expfail /* = false */) -{ +void UnitTest::is(double actual, double expected, double tolerance, const std::string& name, + bool expfail /* = false */) { ++_counter; - bool success = (fabs (actual - expected) <= tolerance); + bool success = (fabs(actual - expected) <= tolerance); - if (success and ! expfail) - { + if (success and !expfail) { ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << name - << '\n'; - } - else - { - if (success == expfail) - ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << name - << (expfail ? (success ? " # FIXED" : " # TODO") : "") - << "\n# expected: " - << expected - << "\n# got: " - << actual - << '\n'; + std::cout << green("ok") << " " << _counter << " - " << name << '\n'; + } else { + if (success == expfail) ++_failed; + std::cout << red("not ok") << " " << _counter << " - " << name + << (expfail ? (success ? " # FIXED" : " # TODO") : "") << "\n# expected: " << expected + << "\n# got: " << actual << '\n'; } } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::is (unsigned char actual, unsigned char expected, const std::string& name, bool expfail /* = false */) -{ +void UnitTest::is(unsigned char actual, unsigned char expected, const std::string& name, + bool expfail /* = false */) { ++_counter; bool success = (actual == expected); - if (success and ! expfail) - { + if (success and !expfail) { ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << name - << '\n'; - } - else - { - if (success == expfail) - ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << name - << (expfail ? (success ? " # FIXED" : " # TODO") : "") - << "\n# expected: " - << expected - << "\n# got: " - << actual - << '\n'; + std::cout << green("ok") << " " << _counter << " - " << name << '\n'; + } else { + if (success == expfail) ++_failed; + std::cout << red("not ok") << " " << _counter << " - " << name + << (expfail ? (success ? " # FIXED" : " # TODO") : "") << "\n# expected: " << expected + << "\n# got: " << actual << '\n'; } } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::is ( - const std::string& actual, - const std::string& expected, - const std::string& name, - bool expfail /* = false */) -{ +void UnitTest::is(const std::string& actual, const std::string& expected, const std::string& name, + bool expfail /* = false */) { ++_counter; bool success = (actual == expected); - if (success and ! expfail) - { + if (success and !expfail) { ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << name - << '\n'; - } - else - { - if (success == expfail) - ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << name - << (expfail ? (success ? " # FIXED" : " # TODO") : "") - << "\n# expected: '" - << expected - << "'" - << "\n# got: '" - << actual - << "'\n"; + std::cout << green("ok") << " " << _counter << " - " << name << '\n'; + } else { + if (success == expfail) ++_failed; + std::cout << red("not ok") << " " << _counter << " - " << name + << (expfail ? (success ? " # FIXED" : " # TODO") : "") << "\n# expected: '" + << expected << "'" + << "\n# got: '" << actual << "'\n"; } } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::is ( - const char* actual, - const char* expected, - const std::string& name, - bool expfail /* = false */) -{ +void UnitTest::is(const char* actual, const char* expected, const std::string& name, + bool expfail /* = false */) { ++_counter; - bool success = (! strcmp (actual, expected)); + bool success = (!strcmp(actual, expected)); - if (success and ! expfail) - { + if (success and !expfail) { ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << name - << '\n'; - } - else - { - if (success == expfail) - ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << name - << (expfail ? (success ? " # FIXED" : " # TODO") : "") - << "\n# expected: '" - << expected - << "'" - << "\n# got: '" - << actual - << "'\n"; + std::cout << green("ok") << " " << _counter << " - " << name << '\n'; + } else { + if (success == expfail) ++_failed; + std::cout << red("not ok") << " " << _counter << " - " << name + << (expfail ? (success ? " # FIXED" : " # TODO") : "") << "\n# expected: '" + << expected << "'" + << "\n# got: '" << actual << "'\n"; } } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::diag (const std::string& text) -{ - auto start = text.find_first_not_of (" \t\n\r\f"); - auto end = text.find_last_not_of (" \t\n\r\f"); - if (start != std::string::npos && - end != std::string::npos) - std::cout << "# " << text.substr (start, end - start + 1) << '\n'; +void UnitTest::diag(const std::string& text) { + auto start = text.find_first_not_of(" \t\n\r\f"); + auto end = text.find_last_not_of(" \t\n\r\f"); + if (start != std::string::npos && end != std::string::npos) + std::cout << "# " << text.substr(start, end - start + 1) << '\n'; } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::pass (const std::string& text) -{ +void UnitTest::pass(const std::string& text) { ++_counter; ++_passed; - std::cout << green ("ok") - << " " - << _counter - << " - " - << text - << '\n'; + std::cout << green("ok") << " " << _counter << " - " << text << '\n'; } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::fail (const std::string& text) -{ +void UnitTest::fail(const std::string& text) { ++_counter; ++_failed; - std::cout << red ("not ok") - << " " - << _counter - << " - " - << text - << '\n'; + std::cout << red("not ok") << " " << _counter << " - " << text << '\n'; } /////////////////////////////////////////////////////////////////////////////// -void UnitTest::skip (const std::string& text) -{ +void UnitTest::skip(const std::string& text) { ++_counter; ++_skipped; - std::cout << yellow ("ok") - << " " - << _counter - << " - " - << text - << " # skip" - << '\n'; + std::cout << yellow("ok") << " " << _counter << " - " << text << " # skip" << '\n'; } /////////////////////////////////////////////////////////////////////////////// -std::string UnitTest::red (const std::string& input) -{ - if (isatty (fileno (stdout))) - return std::string ("\033[31m" + input + "\033[0m"); +std::string UnitTest::red(const std::string& input) { + if (isatty(fileno(stdout))) return std::string("\033[31m" + input + "\033[0m"); return input; } /////////////////////////////////////////////////////////////////////////////// -std::string UnitTest::green (const std::string& input) -{ - if (isatty (fileno (stdout))) - return std::string ("\033[32m" + input + "\033[0m"); +std::string UnitTest::green(const std::string& input) { + if (isatty(fileno(stdout))) return std::string("\033[32m" + input + "\033[0m"); return input; } /////////////////////////////////////////////////////////////////////////////// -std::string UnitTest::yellow (const std::string& input) -{ - if (isatty (fileno (stdout))) - return std::string ("\033[33m" + input + "\033[0m"); +std::string UnitTest::yellow(const std::string& input) { + if (isatty(fileno(stdout))) return std::string("\033[33m" + input + "\033[0m"); return input; } diff --git a/test/test.h b/test/test.h index 21783f188..0c27e8e85 100644 --- a/test/test.h +++ b/test/test.h @@ -29,36 +29,35 @@ #include -class UnitTest -{ -public: - UnitTest (); - UnitTest (int); - ~UnitTest (); +class UnitTest { + public: + UnitTest(); + UnitTest(int); + ~UnitTest(); - void plan (int); - void planMore (int); - void ok (bool, const std::string&, bool expfail = false); - void notok (bool, const std::string&, bool expfail = false); - void is (bool, bool, const std::string&, bool expfail = false); - void is (size_t, size_t, const std::string&, bool expfail = false); - void is (int, int, const std::string&, bool expfail = false); - void is (double, double, const std::string&, bool expfail = false); - void is (double, double, double, const std::string&, bool expfail = false); - void is (unsigned char, unsigned char, const std::string&, bool expfail = false); - void is (const std::string&, const std::string&, const std::string&, bool expfail = false); - void is (const char*, const char*, const std::string&, bool expfail = false); - void diag (const std::string&); - void pass (const std::string&); - void fail (const std::string&); - void skip (const std::string&); + void plan(int); + void planMore(int); + void ok(bool, const std::string&, bool expfail = false); + void notok(bool, const std::string&, bool expfail = false); + void is(bool, bool, const std::string&, bool expfail = false); + void is(size_t, size_t, const std::string&, bool expfail = false); + void is(int, int, const std::string&, bool expfail = false); + void is(double, double, const std::string&, bool expfail = false); + void is(double, double, double, const std::string&, bool expfail = false); + void is(unsigned char, unsigned char, const std::string&, bool expfail = false); + void is(const std::string&, const std::string&, const std::string&, bool expfail = false); + void is(const char*, const char*, const std::string&, bool expfail = false); + void diag(const std::string&); + void pass(const std::string&); + void fail(const std::string&); + void skip(const std::string&); -private: - std::string red (const std::string&); - std::string green (const std::string&); - std::string yellow (const std::string&); + private: + std::string red(const std::string&); + std::string green(const std::string&); + std::string yellow(const std::string&); -private: + private: int _planned; int _counter; int _passed; diff --git a/test/test_hooks/on-exit-misbehave2 b/test/test_hooks/on-exit-misbehave2 index 2b52b012b..338b21284 100644 --- a/test/test_hooks/on-exit-misbehave2 +++ b/test/test_hooks/on-exit-misbehave2 @@ -15,4 +15,3 @@ echo 'FEEDBACK' # - 0: JSON ignored, non-JSON is feedback. # - non-0: JSON ignored, non-JSON is error. exit 0 - diff --git a/test/test_hooks/on-launch-bad b/test/test_hooks/on-launch-bad index 221a51262..01c7cbfdb 100644 --- a/test/test_hooks/on-launch-bad +++ b/test/test_hooks/on-launch-bad @@ -14,4 +14,3 @@ echo 'FEEDBACK' # - 0: JSON ignored, non-JSON is feedback. # - non-0: JSON ignored, non-JSON is error. exit 1 - diff --git a/test/test_hooks/on-launch-good b/test/test_hooks/on-launch-good index e6c6226c2..1c1aefea6 100644 --- a/test/test_hooks/on-launch-good +++ b/test/test_hooks/on-launch-good @@ -14,4 +14,3 @@ echo 'FEEDBACK' # - 0: JSON ignored, non-JSON is feedback. # - non-0: JSON ignored, non-JSON is error. exit 0 - diff --git a/test/test_hooks/on-launch-good-env b/test/test_hooks/on-launch-good-env index 34a55cac5..ffbe3ca62 100644 --- a/test/test_hooks/on-launch-good-env +++ b/test/test_hooks/on-launch-good-env @@ -23,4 +23,3 @@ echo $6 # - 0: JSON ignored, non-JSON is feedback. # - non-0: JSON ignored, non-JSON is error. exit 0 - diff --git a/test/test_hooks/on-launch-misbehave1 b/test/test_hooks/on-launch-misbehave1 index d309739e4..096218612 100644 --- a/test/test_hooks/on-launch-misbehave1 +++ b/test/test_hooks/on-launch-misbehave1 @@ -15,4 +15,3 @@ echo 'FEEDBACK' # - non-0: JSON ignored, non-JSON is error. kill -9 $$ exit 0 - diff --git a/test/test_hooks/on-launch-misbehave2 b/test/test_hooks/on-launch-misbehave2 index f981f8d68..7e46056aa 100644 --- a/test/test_hooks/on-launch-misbehave2 +++ b/test/test_hooks/on-launch-misbehave2 @@ -15,4 +15,3 @@ echo 'FEEDBACK' # - 0: JSON ignored, non-JSON is feedback. # - non-0: JSON ignored, non-JSON is error. exit 0 - diff --git a/test/test_hooks/on-modify-for-template-badexit.py b/test/test_hooks/on-modify-for-template-badexit.py index 32537561e..06f62918c 100644 --- a/test/test_hooks/on-modify-for-template-badexit.py +++ b/test/test_hooks/on-modify-for-template-badexit.py @@ -12,7 +12,7 @@ task["description"] = "This is an example modify hook" # A random message sys.stdout.write("Hello from the template hook\n") -sys.stdout.write(json.dumps(task, separators=(',', ':')) + '\n') +sys.stdout.write(json.dumps(task, separators=(",", ":")) + "\n") sys.exit(1) # vim: ai sts=4 et sw=4 diff --git a/test/test_hooks/on-modify-for-template.py b/test/test_hooks/on-modify-for-template.py index c3b01193e..01cf35a12 100644 --- a/test/test_hooks/on-modify-for-template.py +++ b/test/test_hooks/on-modify-for-template.py @@ -12,7 +12,7 @@ task["description"] = "This is an example modify hook" # A random message sys.stdout.write("Hello from the template hook\n") -sys.stdout.write(json.dumps(task, separators=(',', ':')) + '\n') +sys.stdout.write(json.dumps(task, separators=(",", ":")) + "\n") sys.exit(0) # vim: ai sts=4 et sw=4 diff --git a/test/timesheet.test.py b/test/timesheet.test.py index 83a48a253..2dc9af10d 100755 --- a/test/timesheet.test.py +++ b/test/timesheet.test.py @@ -30,6 +30,7 @@ import re import sys import unittest from time import time + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -53,8 +54,8 @@ class TestTimesheet(TestCase): # C0 completed, this week # C1 completed, last week # C2 completed, 2wks ago - now = int(time()) - seven = now - 7 * 86400 + now = int(time()) + seven = now - 7 * 86400 fourteen = now - 14 * 86400 cls.t("add P0 entry:{0}".format(fourteen)) @@ -76,13 +77,17 @@ class TestTimesheet(TestCase): expected = re.compile( "Started.+PS2.+Completed.+C2.+" "Started.+PS1.+Completed.+C1.+" - "Started.+PS0.+Completed.+C0", re.DOTALL) + "Started.+PS0.+Completed.+C0", + re.DOTALL, + ) self.assertRegex(out, expected) def test_one_week(self): """One week of started and completed""" # This is the default filter, reduced from 4 weeks to 1. - code, out, err = self.t("timesheet (+PENDING and start.after:now-1wk) or (+COMPLETED and end.after:now-1wk)") + code, out, err = self.t( + "timesheet (+PENDING and start.after:now-1wk) or (+COMPLETED and end.after:now-1wk)" + ) expected = re.compile("Started.+PS0.+Completed.+C0", re.DOTALL) self.assertRegex(out, expected) @@ -91,8 +96,10 @@ class TestTimesheet(TestCase): self.assertNotIn("C1", out) self.assertNotIn("C2", out) + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/tw-1379.test.py b/test/tw-1379.test.py index bc6f2aaaa..f30cdee8b 100755 --- a/test/tw-1379.test.py +++ b/test/tw-1379.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase @@ -39,7 +40,7 @@ class TestBug1379(TestCase): def setUp(self): self.t = Task() # Themes are a special case that cannot be set via "task config" - with open(self.t.taskrc, 'a') as fh: + with open(self.t.taskrc, "a") as fh: fh.write("include " + REPO_DIR + "/../doc/rc/no-color.theme\n") self.t.config("color.alternate", "") @@ -157,8 +158,10 @@ class TestBug1379(TestCase): code, out, err = self.t("all +DELETED") self.assertRegex(out, self.RED + r".*Delete.*" + self.CLEAR) + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/tw-1837.test.py b/test/tw-1837.test.py index 7078545a6..78ecbc012 100755 --- a/test/tw-1837.test.py +++ b/test/tw-1837.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase @@ -50,6 +51,7 @@ from basetest import Task, TestCase # self.assertNotRegex(t, r) # self.tap("") + class TestBug1837(TestCase): def setUp(self): self.t = Task() @@ -61,8 +63,10 @@ class TestBug1837(TestCase): self.tap(out) self.tap(err) + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/tw-20.test.py b/test/tw-20.test.py index f06fafe8b..a2daa97c1 100755 --- a/test/tw-20.test.py +++ b/test/tw-20.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -48,7 +49,7 @@ class TestBug20(TestCase): self.t.env["VISUAL"] = self.editor def test_annotate_edit_does_not_delete(self): - """ edit annotation should not delete then add untouched annotations """ + """edit annotation should not delete then add untouched annotations""" self.t("add tw-20") self.t("1 annotate 1st annotation") @@ -62,8 +63,8 @@ class TestBug20(TestCase): code, _timestamp1b, err = self.t("_get 1.annotations.1.entry") code, _timestamp2b, err = self.t("_get 1.annotations.2.entry") - self.assertEqual( _timestamp1a, _timestamp1b ) - self.assertEqual( _timestamp2a, _timestamp2b ) + self.assertEqual(_timestamp1a, _timestamp1b) + self.assertEqual(_timestamp2a, _timestamp2b) code, out, err = self.t("info") @@ -73,6 +74,7 @@ class TestBug20(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/tw-2575.test.py b/test/tw-2575.test.py index cd9c75dd1..0638fe49a 100755 --- a/test/tw-2575.test.py +++ b/test/tw-2575.test.py @@ -40,10 +40,41 @@ class TestExport(TestCase): self.assertEqual(len(out), 4) - self.assertTaskEqual(out[0], {"id": 1, "description": "one", "status": "pending", "urgency": 0}) - self.assertTaskEqual(out[1], {"id": 2, "description": "two", "project": "strange", "status": "pending", "urgency": 1}) - self.assertTaskEqual(out[2], {"id": 3, "description": "task1", "status": "pending", "project": "A", "tags": ["home"], "urgency": 16.8}) - self.assertTaskEqual(out[3], {"id": 4, "description": "task2", "status": "pending", "project": "A", "tags": ["work"], "urgency": 1.8}) + self.assertTaskEqual( + out[0], {"id": 1, "description": "one", "status": "pending", "urgency": 0} + ) + self.assertTaskEqual( + out[1], + { + "id": 2, + "description": "two", + "project": "strange", + "status": "pending", + "urgency": 1, + }, + ) + self.assertTaskEqual( + out[2], + { + "id": 3, + "description": "task1", + "status": "pending", + "project": "A", + "tags": ["home"], + "urgency": 16.8, + }, + ) + self.assertTaskEqual( + out[3], + { + "id": 4, + "description": "task2", + "status": "pending", + "project": "A", + "tags": ["work"], + "urgency": 1.8, + }, + ) def test_exports_filter(self): """Verify exports with filter work""" @@ -52,7 +83,9 @@ class TestExport(TestCase): self.assertEqual(len(out), 1) - self.assertTaskEqual(out[0], {"id": 1, "description": "one", "status": "pending", "urgency": 0}) + self.assertTaskEqual( + out[0], {"id": 1, "description": "one", "status": "pending", "urgency": 0} + ) def test_exports_with_limits_and_filter(self): """Verify exports with limits and filter work""" @@ -61,15 +94,45 @@ class TestExport(TestCase): self.assertEqual(len(out), 2) - self.assertTaskEqual(out[0], {"id": 3, "description": "task1", "status": "pending", "project": "A", "tags": ["home"], "urgency": 16.8}) - self.assertTaskEqual(out[1], {"id": 4, "description": "task2", "status": "pending", "project": "A", "tags": ["work"], "urgency": 1.8}) + self.assertTaskEqual( + out[0], + { + "id": 3, + "description": "task1", + "status": "pending", + "project": "A", + "tags": ["home"], + "urgency": 16.8, + }, + ) + self.assertTaskEqual( + out[1], + { + "id": 4, + "description": "task2", + "status": "pending", + "project": "A", + "tags": ["work"], + "urgency": 1.8, + }, + ) code, out, err = self.t("task limit:1 export") out = json.loads(out) self.assertEqual(len(out), 1) - self.assertTaskEqual(out[0], {"id": 3, "description": "task1", "status": "pending", "project": "A", "tags": ["home"], "urgency": 16.8}) + self.assertTaskEqual( + out[0], + { + "id": 3, + "description": "task1", + "status": "pending", + "project": "A", + "tags": ["home"], + "urgency": 16.8, + }, + ) def test_exports_report(self): """Verify exports with report work""" @@ -78,8 +141,28 @@ class TestExport(TestCase): self.assertEqual(len(out), 2) - self.assertTaskEqual(out[0], {"id": 4, "description": "task2", "status": "pending", "project": "A", "tags": ["work"], "urgency": 1.8}) - self.assertTaskEqual(out[1], {"id": 3, "description": "task1", "status": "pending", "project": "A", "tags": ["home"], "urgency": 16.8}) + self.assertTaskEqual( + out[0], + { + "id": 4, + "description": "task2", + "status": "pending", + "project": "A", + "tags": ["work"], + "urgency": 1.8, + }, + ) + self.assertTaskEqual( + out[1], + { + "id": 3, + "description": "task1", + "status": "pending", + "project": "A", + "tags": ["home"], + "urgency": 16.8, + }, + ) if __name__ == "__main__": diff --git a/test/tw-262.test.py b/test/tw-262.test.py index c3a0ff006..74935075e 100755 --- a/test/tw-262.test.py +++ b/test/tw-262.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -77,8 +78,7 @@ class TestBug262(TestCase): self._check_expectation(" (project.isnt:tw)") def test_proj_isnt_parenthesis_space_leading_double(self): - """project.isnt works within parenthesis after a double leading space - """ + """project.isnt works within parenthesis after a double leading space""" self._check_expectation(" ( project.isnt:tw)") def test_proj_isnt_parenthesis_space_trailing(self): @@ -86,8 +86,7 @@ class TestBug262(TestCase): self._check_expectation("(project.isnt:tw) ") def test_proj_isnt_parenthesis_space_trailing_double(self): - """project.isnt works within parenthesis after a double trailing space - """ + """project.isnt works within parenthesis after a double trailing space""" self._check_expectation("(project.isnt:tw ) ") def test_proj_isnt_spaces_parenthesis(self): @@ -98,8 +97,10 @@ class TestBug262(TestCase): """project.isnt works within parenthesis and double spaces""" self._check_expectation(" ( project.isnt:tw ) ") + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/tw-2689.test.cpp b/test/tw-2689.test.cpp index e18fdb021..14f95a784 100644 --- a/test/tw-2689.test.cpp +++ b/test/tw-2689.test.cpp @@ -24,68 +24,75 @@ // //////////////////////////////////////////////////////////////////////////////// -#include #include + +#include // cmake.h include header must come first -#include #include +#include #include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest test (12); +int main(int, char**) { + UnitTest test(12); // Ensure environment has no influence. - unsetenv ("TASKDATA"); - unsetenv ("TASKRC"); + unsetenv("TASKDATA"); + unsetenv("TASKRC"); // Inform Task about the attributes in the JSON below Task::attributes["depends"] = "string"; Task::attributes["uuid"] = "string"; // depends in [..] string from a taskserver (issue#2689) - auto sample = "{" - "\"depends\":\"[\\\"92a40a34-37f3-4785-8ca1-ff89cfbfd105\\\",\\\"e08e35fa-e42b-4de0-acc4-518fca8f6365\\\"]\"," - "\"uuid\":\"00000000-0000-0000-0000-000000000000\"" - "}"; - auto json = Task (sample); - auto value = json.get ("uuid"); - test.is (value, "00000000-0000-0000-0000-000000000000", "json [..] uuid"); - value = json.get ("depends"); - test.is (value, "92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365", "json [..] depends"); - test.ok (json.has ("dep_92a40a34-37f3-4785-8ca1-ff89cfbfd105"), "json [..] dep attr"); - test.ok (json.has ("dep_e08e35fa-e42b-4de0-acc4-518fca8f6365"), "json [..] dep attr"); + auto sample = + "{" + "\"depends\":\"[\\\"92a40a34-37f3-4785-8ca1-ff89cfbfd105\\\",\\\"e08e35fa-e42b-4de0-acc4-" + "518fca8f6365\\\"]\"," + "\"uuid\":\"00000000-0000-0000-0000-000000000000\"" + "}"; + auto json = Task(sample); + auto value = json.get("uuid"); + test.is(value, "00000000-0000-0000-0000-000000000000", "json [..] uuid"); + value = json.get("depends"); + test.is(value, "92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365", + "json [..] depends"); + test.ok(json.has("dep_92a40a34-37f3-4785-8ca1-ff89cfbfd105"), "json [..] dep attr"); + test.ok(json.has("dep_e08e35fa-e42b-4de0-acc4-518fca8f6365"), "json [..] dep attr"); // depends in comma-delimited string from a taskserver (deprecated format) - sample = "{" - "\"depends\":\"92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365\"," - "\"uuid\":\"00000000-0000-0000-0000-000000000000\"" - "}"; - json = Task (sample); - value = json.get ("uuid"); - test.is (value, "00000000-0000-0000-0000-000000000000", "json comma-separated uuid"); - value = json.get ("depends"); - test.is (value, "92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365", "json comma-separated depends"); - test.ok (json.has ("dep_92a40a34-37f3-4785-8ca1-ff89cfbfd105"), "json comma-separated dep attr"); - test.ok (json.has ("dep_e08e35fa-e42b-4de0-acc4-518fca8f6365"), "json comma-separated dep attr"); + sample = + "{" + "\"depends\":\"92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365\"," + "\"uuid\":\"00000000-0000-0000-0000-000000000000\"" + "}"; + json = Task(sample); + value = json.get("uuid"); + test.is(value, "00000000-0000-0000-0000-000000000000", "json comma-separated uuid"); + value = json.get("depends"); + test.is(value, "92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365", + "json comma-separated depends"); + test.ok(json.has("dep_92a40a34-37f3-4785-8ca1-ff89cfbfd105"), "json comma-separated dep attr"); + test.ok(json.has("dep_e08e35fa-e42b-4de0-acc4-518fca8f6365"), "json comma-separated dep attr"); // depends in a JSON array from a taskserver - sample = "{" - "\"depends\":[\"92a40a34-37f3-4785-8ca1-ff89cfbfd105\",\"e08e35fa-e42b-4de0-acc4-518fca8f6365\"]," - "\"uuid\":\"00000000-0000-0000-0000-000000000000\"" - "}"; - json = Task (sample); - value = json.get ("uuid"); - test.is (value, "00000000-0000-0000-0000-000000000000", "json array uuid"); - value = json.get ("depends"); - test.is (value, "92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365", "json array depends"); - test.ok (json.has ("dep_92a40a34-37f3-4785-8ca1-ff89cfbfd105"), "json array dep attr"); - test.ok (json.has ("dep_e08e35fa-e42b-4de0-acc4-518fca8f6365"), "json array dep attr"); + sample = + "{" + "\"depends\":[\"92a40a34-37f3-4785-8ca1-ff89cfbfd105\",\"e08e35fa-e42b-4de0-acc4-" + "518fca8f6365\"]," + "\"uuid\":\"00000000-0000-0000-0000-000000000000\"" + "}"; + json = Task(sample); + value = json.get("uuid"); + test.is(value, "00000000-0000-0000-0000-000000000000", "json array uuid"); + value = json.get("depends"); + test.is(value, "92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365", + "json array depends"); + test.ok(json.has("dep_92a40a34-37f3-4785-8ca1-ff89cfbfd105"), "json array dep attr"); + test.ok(json.has("dep_e08e35fa-e42b-4de0-acc4-518fca8f6365"), "json array dep attr"); return 0; } //////////////////////////////////////////////////////////////////////////////// - diff --git a/test/tw-295.test.py b/test/tw-295.test.py index b46a892c2..0cb9932e1 100755 --- a/test/tw-295.test.py +++ b/test/tw-295.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + sys.path.append(os.path.dirname(os.path.abspath(__file__))) from basetest import Task, TestCase @@ -39,7 +40,7 @@ class TestBug295(TestCase): def test_subst_with_slashes(self): """Test substitution containing slashes""" - self.t('add -- one/two/three') + self.t("add -- one/two/three") # Python string contains \\\\, converts to internal \\. # Something in the command processing converts \\ --> \. @@ -49,12 +50,14 @@ class TestBug295(TestCase): # code, out, err = self.t('rc.debug.parser=2 1 modify /\\\\/two\\\\//TWO/') # self.tap(err) - self.t('1 modify /\\\\/two\\\\//TWO/') - code, out, err = self.t('list') - self.assertIn('oneTWOthree', out) + self.t("1 modify /\\\\/two\\\\//TWO/") + code, out, err = self.t("list") + self.assertIn("oneTWOthree", out) + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/tw-3527.test.py b/test/tw-3527.test.py index a6674f60c..94b81e7ff 100755 --- a/test/tw-3527.test.py +++ b/test/tw-3527.test.py @@ -35,5 +35,5 @@ class TestExport(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner - unittest.main(testRunner=TAPTestRunner()) + unittest.main(testRunner=TAPTestRunner()) diff --git a/test/uda.test.py b/test/uda.test.py index 1c9901e4f..1919e8600 100755 --- a/test/uda.test.py +++ b/test/uda.test.py @@ -29,6 +29,7 @@ import sys import os import re import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -179,8 +180,7 @@ class TestUdaDuration(TestBaseUda): """Add tasks with an invalid UDA duration""" code, out, err = self.t.runError("add bad extra:bad_duration") self.assertNotIn("Created task", out) - self.assertIn("The duration value 'bad_duration' is not supported", - err) + self.assertIn("The duration value 'bad_duration' is not supported", err) class TestUdaNumeric(TestBaseUda): @@ -254,8 +254,7 @@ class TestUdaValue(TestBaseUda): self.assertIn("Created task", out) code, out, err = self.t.runError("add two extra:toxic") - self.assertIn("The 'extra' attribute does not allow a value of " - "'toxic'", err) + self.assertIn("The 'extra' attribute does not allow a value of " "'toxic'", err) code, out, err = self.t("uda") self.assertRegex(out, r"1\s+strong\s+one") @@ -299,8 +298,8 @@ class TestBug21(TestCase): def test_almost_UDA(self): """21: do not match a UDA if not followed by colon""" - self.t.config('uda.foo.type', 'string') - self.t.config('uda.foo.label', 'FOO') + self.t.config("uda.foo.type", "string") + self.t.config("uda.foo.label", "FOO") self.t("add this is a foo bar") code, out, err = self.t("1 info") @@ -314,12 +313,12 @@ class Test1447(TestCase): def test_filter_uda(self): """1447: Verify ability to filter on empty UDA that resembles named date""" - self.t.config('uda.sep.type', 'string') - self.t('add one') - self.t('add two sep:foo') - code, out, err = self.t('sep: list') + self.t.config("uda.sep.type", "string") + self.t("add one") + self.t("add two sep:foo") + code, out, err = self.t("sep: list") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('one', out) + self.assertIn("one", out) class Test1542(TestCase): @@ -333,18 +332,18 @@ class Test1542(TestCase): 1542: Make sure the numeric UDA value 1187962 does not get converted to scientific notation on export. """ - self.t('add large bugid:1187962') - code, out, err = self.t('1 export') - self.assertIn("\"bugid\":1187962,", out) + self.t("add large bugid:1187962") + code, out, err = self.t("1 export") + self.assertIn('"bugid":1187962,', out) def test_small_numeric_uda_retains_value(self): """ 1542: Make sure the numeric UDA value 43.21 does not get converted to integer on export. """ - self.t('add small bugid:43.21') - code, out, err = self.t('1 export') - self.assertIn("\"bugid\":43.21", out) + self.t("add small bugid:43.21") + code, out, err = self.t("1 export") + self.assertIn('"bugid":43.21', out) class TestBug1622(TestCase): @@ -363,6 +362,7 @@ class TestBug1622(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/uda_orphan.test.py b/test/uda_orphan.test.py index edceaafab..0ba3e9c03 100755 --- a/test/uda_orphan.test.py +++ b/test/uda_orphan.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -47,7 +48,9 @@ class TestUDAOrphans(TestCase): """Verify that orphans are preserved during various operations""" self.t("rc.uda.extra.type:string rc.uda.extra.label:Extra add one extra:foo") - code, out, err = self.t("rc.uda.extra.type:string rc.uda.extra.label:Extra _get 1.extra") + code, out, err = self.t( + "rc.uda.extra.type:string rc.uda.extra.label:Extra _get 1.extra" + ) self.assertEqual("foo\n", out) # DOM access for orphans is not supported. @@ -97,11 +100,12 @@ class TestUDAOrphans(TestCase): # Only the first task should be identified as orphan code, out, err = self.t("udas") - self.assertRegex(out, r'extra\s+1\s+1 Orphan UDA') + self.assertRegex(out, r"extra\s+1\s+1 Orphan UDA") if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/uda_report.test.py b/test/uda_report.test.py index 427b20660..b4853c478 100755 --- a/test/uda_report.test.py +++ b/test/uda_report.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -69,6 +70,7 @@ class TestUdaReports(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/uda_sort.test.py b/test/uda_sort.test.py index a8274672e..29116d5e4 100755 --- a/test/uda_sort.test.py +++ b/test/uda_sort.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -38,89 +39,89 @@ class TestUDACustomSort(TestCase): @classmethod def setUpClass(cls): cls.t = Task() - cls.t.config('uda.foo.type', 'string') - cls.t.config('uda.foo.label', 'Foo') - cls.t.config('uda.foo.values', 'H,M,L,') - cls.t.config('report.list.columns', 'id,description,foo') - cls.t.config('report.list.labels', 'ID,Desc,Foo') - cls.t('add four foo:H') - cls.t('add three foo:M') - cls.t('add two foo:L') - cls.t('add one') + cls.t.config("uda.foo.type", "string") + cls.t.config("uda.foo.label", "Foo") + cls.t.config("uda.foo.values", "H,M,L,") + cls.t.config("report.list.columns", "id,description,foo") + cls.t.config("report.list.labels", "ID,Desc,Foo") + cls.t("add four foo:H") + cls.t("add three foo:M") + cls.t("add two foo:L") + cls.t("add one") def test_ascending(self): """Ascending custom sort order""" - self.t.config('uda.foo.values', 'H,M,L,') - code, out, err = self.t('rc.report.list.sort:foo+ list') + self.t.config("uda.foo.values", "H,M,L,") + code, out, err = self.t("rc.report.list.sort:foo+ list") - one = out.find('one') - two = out.find('two') - three = out.find('three') - four = out.find('four') + one = out.find("one") + two = out.find("two") + three = out.find("three") + four = out.find("four") - self.assertTrue(one < two) - self.assertTrue(two < three) + self.assertTrue(one < two) + self.assertTrue(two < three) self.assertTrue(three < four) def test_descending(self): """Descending custom sort order""" - self.t.config('uda.foo.values', 'H,M,L,') - code, out, err = self.t('rc.report.list.sort:foo- list') + self.t.config("uda.foo.values", "H,M,L,") + code, out, err = self.t("rc.report.list.sort:foo- list") - one = out.find('one') - two = out.find('two') - three = out.find('three') - four = out.find('four') + one = out.find("one") + two = out.find("two") + three = out.find("three") + four = out.find("four") - self.assertTrue(four < three) + self.assertTrue(four < three) self.assertTrue(three < two) - self.assertTrue(two < one) + self.assertTrue(two < one) def test_ridiculous(self): """Ridiculous custom sort order""" - self.t.config('uda.foo.values', 'H,M,,L') - code, out, err = self.t('rc.report.list.sort:foo- list') + self.t.config("uda.foo.values", "H,M,,L") + code, out, err = self.t("rc.report.list.sort:foo- list") - one = out.find('one') - two = out.find('two') - three = out.find('three') - four = out.find('four') + one = out.find("one") + two = out.find("two") + three = out.find("three") + four = out.find("four") - self.assertTrue(four < three) + self.assertTrue(four < three) self.assertTrue(three < one) - self.assertTrue(one < two) + self.assertTrue(one < two) class TestUDADefaultSort(TestCase): @classmethod def setUpClass(cls): cls.t = Task() - cls.t.config('uda.foo.type', 'string') - cls.t.config('uda.foo.label', 'Foo') - cls.t.config('report.list.columns', 'id,description,foo') - cls.t.config('report.list.labels', 'ID,Desc,Foo') - cls.t('add one foo:A') - cls.t('add three') - cls.t('add two foo:B') + cls.t.config("uda.foo.type", "string") + cls.t.config("uda.foo.label", "Foo") + cls.t.config("report.list.columns", "id,description,foo") + cls.t.config("report.list.labels", "ID,Desc,Foo") + cls.t("add one foo:A") + cls.t("add three") + cls.t("add two foo:B") def test_ascending(self): """Ascending default sort order""" - code, out, err = self.t('rc.report.list.sort:foo+ list') + code, out, err = self.t("rc.report.list.sort:foo+ list") - one = out.find('one') - two = out.find('two') - three = out.find('three') + one = out.find("one") + two = out.find("two") + three = out.find("three") self.assertTrue(one < two) self.assertTrue(two < three) def test_descending(self): """Descending default sort order""" - code, out, err = self.t('rc.report.list.sort:foo- list') + code, out, err = self.t("rc.report.list.sort:foo- list") - one = out.find('one') - two = out.find('two') - three = out.find('three') + one = out.find("one") + two = out.find("two") + three = out.find("three") self.assertTrue(one < three) self.assertTrue(two < one) @@ -133,12 +134,12 @@ class TestBug1319(TestCase): def test_uda_sorting(self): """1319: Verify that UDAs are sorted according to defined order""" - self.t.config("uda.when.type", "string") - self.t.config("uda.when.values", "night,evening,noon,morning") + self.t.config("uda.when.type", "string") + self.t.config("uda.when.values", "night,evening,noon,morning") self.t.config("report.foo.columns", "id,when,description") - self.t.config("report.foo.labels", "ID,WHEN,DESCRIPTION") - self.t.config("report.foo.sort", "when+") + self.t.config("report.foo.labels", "ID,WHEN,DESCRIPTION") + self.t.config("report.foo.sort", "when+") self.t("add one when:night") self.t("add two when:evening") @@ -146,11 +147,15 @@ class TestBug1319(TestCase): self.t("add four when:morning") code, out, err = self.t("rc.verbose:nothing foo") - self.assertRegex(out, r"4\s+morning\s+four\s+3\s+noon\s+three\s+2\s+evening\s+two\s+1\s+night\s+one") + self.assertRegex( + out, + r"4\s+morning\s+four\s+3\s+noon\s+three\s+2\s+evening\s+two\s+1\s+night\s+one", + ) if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/undo.test.py b/test/undo.test.py index b50acb0cc..b0beca1b1 100755 --- a/test/undo.test.py +++ b/test/undo.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -41,24 +42,24 @@ class TestUndo(TestCase): def test_add_undo(self): """'add' then 'undo'""" - self.t('add one') - code, out, err = self.t('_get 1.status') - self.assertEqual(out.strip(), 'pending') - self.t('undo', input="y\n") - code, out, err = self.t('_get 1.status') - self.assertEqual(out.strip(), '') + self.t("add one") + code, out, err = self.t("_get 1.status") + self.assertEqual(out.strip(), "pending") + self.t("undo", input="y\n") + code, out, err = self.t("_get 1.status") + self.assertEqual(out.strip(), "") def test_add_done_undo(self): """'add' then 'done' then 'undo'""" - self.t('add two') - code, out, err = self.t('_get 1.status') - self.assertEqual(out.strip(), 'pending') - self.t('1 done') - code, out, err = self.t('_get 1.status') - self.assertEqual(out.strip(), 'completed') - self.t('undo', input="y\n") - code, out, err = self.t('_get 1.status') - self.assertEqual(out.strip(), 'pending') + self.t("add two") + code, out, err = self.t("_get 1.status") + self.assertEqual(out.strip(), "pending") + self.t("1 done") + code, out, err = self.t("_get 1.status") + self.assertEqual(out.strip(), "completed") + self.t("undo", input="y\n") + code, out, err = self.t("_get 1.status") + self.assertEqual(out.strip(), "pending") def test_undo_en_passant(self): """Verify that en-passant changes during undo are an error""" @@ -73,7 +74,7 @@ class TestUndoStyle(TestCase): self.t("add one project:foo priority:H") self.t("1 modify +tag project:bar priority:") - @unittest.expectedFailure # undo diffs are not supported + @unittest.expectedFailure # undo diffs are not supported def test_undo_side_style(self): """Test that 'rc.undo.style:side' generates the right output""" self.t.config("undo.style", "side") @@ -81,7 +82,7 @@ class TestUndoStyle(TestCase): self.assertNotRegex(out, "-tags:\\s*\n\\+tags:\\s+tag") self.assertRegex(out, r"tags\s+tag\s*") - @unittest.expectedFailure # undo diffs are not supported + @unittest.expectedFailure # undo diffs are not supported def test_undo_diff_style(self): """Test that 'rc.undo.style:diff' generates the right output""" self.t.config("undo.style", "diff") @@ -114,11 +115,12 @@ class TestBug634(TestCase): # If a prompt happens, the test will timeout on input (exitcode != 0) code, out, err = self.t("rc.confirmation=off undo") code, out, err = self.t("_get 1.description") - self.assertEqual(out.strip(), '') # task is gone + self.assertEqual(out.strip(), "") # task is gone if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/unicode.test.py b/test/unicode.test.py index d18cc0239..8c7491870 100755 --- a/test/unicode.test.py +++ b/test/unicode.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -46,13 +47,19 @@ class TestUnicode(TestCase): self.t("add ¥£€¢₡₢₣₤₥₦₧₨₩₪₫₭₮₯ ") self.t("add Pchnąć w tę łódź jeża lub ośm skrzyń fig") self.t("add ๏ เป็นมนุษย์สุดประเสริฐเลิศคุณค่า") - self.t("add イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラムイ ロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム") + self.t( + "add イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラムイ ロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム" + ) self.t("add いろはにほへとちりぬるを") - self.t("add D\\'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh") + self.t( + "add D\\'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh" + ) self.t("add Árvíztűrő tükörfúrógép") self.t("add Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa") self.t("add Sævör grét áðan því úlpan var ónýt") - self.t("add Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther spillede på xylofon.") + self.t( + "add Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther spillede på xylofon." + ) self.t("add Falsches Üben von Xylophonmusik quält jeden größeren Zwerg") self.t("add Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich") self.t("add Heizölrückstoßabdämpfung") @@ -71,15 +78,15 @@ class TestUnicode(TestCase): def test_utf8_tag(self): """Verify that UTF8 can be used in a tag""" - self.t("add utf8 in tag +Zwölf"); + self.t("add utf8 in tag +Zwölf") code, out, err = self.t("_get 1.tags") - self.assertEqual("Zwölf\n", out); + self.assertEqual("Zwölf\n", out) def test_unicode_escape1(self): """Verify U+NNNN unicode sequences""" self.t("add Price U+20AC4") code, out, err = self.t("_get 1.description") - self.assertEqual("Price €4\n", out); + self.assertEqual("Price €4\n", out) def test_unicode_escape2(self): """Verify \\uNNNN unicode sequences""" @@ -92,10 +99,12 @@ class TestUnicode(TestCase): # $ echo add 'Price \u20A43' # \ is preserved self.t(r"add 'Price \u20A43'") code, out, err = self.t("_get 1.description") - self.assertEqual("Price ₤3\n", out); + self.assertEqual("Price ₤3\n", out) + if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/unique.test.py b/test/unique.test.py index efa9d12eb..2204b0e9a 100755 --- a/test/unique.test.py +++ b/test/unique.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -67,18 +68,18 @@ class TestUnique(TestCase): def test_unique_status(self): """Verify that unique status values are correctly counted""" code, out, err = self.t("_unique status") - self.assertIn("pending", out) + self.assertIn("pending", out) self.assertIn("completed", out) - self.assertIn("deleted", out) + self.assertIn("deleted", out) def test_unique_description(self): """Verify that unique description values are correctly counted""" code, out, err = self.t("_unique description") - self.assertIn("one", out) - self.assertIn("two", out) + self.assertIn("one", out) + self.assertIn("two", out) self.assertIn("three", out) - self.assertIn("four", out) - self.assertIn("five", out) + self.assertIn("four", out) + self.assertIn("five", out) def test_unique_id(self): """Verify that unique id values are correctly counted""" @@ -92,6 +93,7 @@ class TestUnique(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/upgrade.test.py b/test/upgrade.test.py index 021591a36..d022cc7ee 100755 --- a/test/upgrade.test.py +++ b/test/upgrade.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -60,6 +61,7 @@ class TestUpgrade(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/urgency.test.py b/test/urgency.test.py index 6ab72f087..489dd786b 100755 --- a/test/urgency.test.py +++ b/test/urgency.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -39,94 +40,94 @@ class TestUrgency(TestCase): def setUpClass(cls): """Executed once before any test in the class""" cls.t = Task() - cls.t.config("urgency.uda.priority.H.coefficient", "10") - cls.t.config("urgency.uda.priority.M.coefficient", "6.5") - cls.t.config("urgency.uda.priority.L.coefficient", "3") - cls.t.config("urgency.active.coefficient", "10") - cls.t.config("urgency.project.coefficient", "10") - cls.t.config("urgency.due.coefficient", "10") - cls.t.config("urgency.blocking.coefficient", "10") - cls.t.config("urgency.blocked.coefficient", "10") - cls.t.config("urgency.annotations.coefficient", "10") - cls.t.config("urgency.tags.coefficient", "10") - cls.t.config("urgency.waiting.coefficient", "-10") - cls.t.config("urgency.user.tag.next.coefficient", "10") + cls.t.config("urgency.uda.priority.H.coefficient", "10") + cls.t.config("urgency.uda.priority.M.coefficient", "6.5") + cls.t.config("urgency.uda.priority.L.coefficient", "3") + cls.t.config("urgency.active.coefficient", "10") + cls.t.config("urgency.project.coefficient", "10") + cls.t.config("urgency.due.coefficient", "10") + cls.t.config("urgency.blocking.coefficient", "10") + cls.t.config("urgency.blocked.coefficient", "10") + cls.t.config("urgency.annotations.coefficient", "10") + cls.t.config("urgency.tags.coefficient", "10") + cls.t.config("urgency.waiting.coefficient", "-10") + cls.t.config("urgency.user.tag.next.coefficient", "10") cls.t.config("urgency.user.project.PROJECT.coefficient", "10") - cls.t.config("urgency.user.tag.TAG.coefficient", "10") - cls.t.config("confirmation", "0") + cls.t.config("urgency.user.tag.TAG.coefficient", "10") + cls.t.config("confirmation", "0") - cls.t("add control") # 1 + cls.t("add control") # 1 - cls.t("add 1a pri:H") # 2 - cls.t("add 1b pri:M") # 3 - cls.t("add 1c pri:L") # 4 + cls.t("add 1a pri:H") # 2 + cls.t("add 1b pri:M") # 3 + cls.t("add 1c pri:L") # 4 - cls.t("add 2a project:P") # 5 + cls.t("add 2a project:P") # 5 - cls.t("add 3a") # 6 + cls.t("add 3a") # 6 cls.t("6 start") - cls.t("add 4a +next") # 7 + cls.t("add 4a +next") # 7 - cls.t("add 5a +one") # 8 - cls.t("add 5b +one +two") # 9 - cls.t("add 5c +one +two +three") # 10 - cls.t("add 5d +one +two +three +four") # 11 + cls.t("add 5a +one") # 8 + cls.t("add 5b +one +two") # 9 + cls.t("add 5c +one +two +three") # 10 + cls.t("add 5d +one +two +three +four") # 11 - cls.t("add 6a") # 12 + cls.t("add 6a") # 12 cls.t("12 annotate A") - cls.t("add 6b") # 13 + cls.t("add 6b") # 13 cls.t("13 annotate A") cls.t("13 annotate B") - cls.t("add 6c") # 14 + cls.t("add 6c") # 14 cls.t("14 annotate A") cls.t("14 annotate B") cls.t("14 annotate C") - cls.t("add 6d") # 15 + cls.t("add 6d") # 15 cls.t("15 annotate A") cls.t("15 annotate B") cls.t("15 annotate C") cls.t("15 annotate D") - cls.t("add 7a wait:10y") # 16 + cls.t("add 7a wait:10y") # 16 - cls.t("add 8a") # 17 - cls.t("add 8b depends:17") # 18 + cls.t("add 8a") # 17 + cls.t("add 8b depends:17") # 18 - cls.t("add 9a due:-10d") # 19 - cls.t("add 9b due:-7d") # 20 - cls.t("add 9c due:-6d") # 21 - cls.t("add 9d due:-5d") # 22 - cls.t("add 9e due:-4d") # 23 - cls.t("add 9f due:-3d") # 24 - cls.t("add 9g due:-2d") # 25 - cls.t("add 9h due:-1d") # 26 - cls.t("add 9i due:now") # 27 - cls.t("add 9j due:25h") # 28 - cls.t("add 9k due:49h") # 29 - cls.t("add 9l due:73h") # 30 - cls.t("add 9m due:97h") # 31 - cls.t("add 9n due:121h") # 32 - cls.t("add 9o due:145h") # 33 - cls.t("add 9p due:169h") # 34 - cls.t("add 9q due:193h") # 35 - cls.t("add 9r due:217h") # 36 - cls.t("add 9s due:241h") # 37 - cls.t("add 9t due:265h") # 38 - cls.t("add 9u due:289h") # 39 - cls.t("add 9v due:313h") # 40 - cls.t("add 9w due:337h") # 41 - cls.t("add 9x due:361h") # 42 + cls.t("add 9a due:-10d") # 19 + cls.t("add 9b due:-7d") # 20 + cls.t("add 9c due:-6d") # 21 + cls.t("add 9d due:-5d") # 22 + cls.t("add 9e due:-4d") # 23 + cls.t("add 9f due:-3d") # 24 + cls.t("add 9g due:-2d") # 25 + cls.t("add 9h due:-1d") # 26 + cls.t("add 9i due:now") # 27 + cls.t("add 9j due:25h") # 28 + cls.t("add 9k due:49h") # 29 + cls.t("add 9l due:73h") # 30 + cls.t("add 9m due:97h") # 31 + cls.t("add 9n due:121h") # 32 + cls.t("add 9o due:145h") # 33 + cls.t("add 9p due:169h") # 34 + cls.t("add 9q due:193h") # 35 + cls.t("add 9r due:217h") # 36 + cls.t("add 9s due:241h") # 37 + cls.t("add 9t due:265h") # 38 + cls.t("add 9u due:289h") # 39 + cls.t("add 9v due:313h") # 40 + cls.t("add 9w due:337h") # 41 + cls.t("add 9x due:361h") # 42 - cls.t("add 10a project:PROJECT") # 43 + cls.t("add 10a project:PROJECT") # 43 - cls.t("add 11a +TAG") # 44 + cls.t("add 11a +TAG") # 44 - cls.t("add 12a scheduled:30d") # 45 - cls.t("add 12b scheduled:yesterday") # 46 + cls.t("add 12a scheduled:30d") # 45 + cls.t("add 12b scheduled:yesterday") # 46 - cls.t("add 13 pri:H") # 47 + cls.t("add 13 pri:H") # 47 def assertApproximately(self, target, value): """Verify that the number in 'value' is within the range""" @@ -263,7 +264,9 @@ class TestUrgency(TestCase): def test_urgency_coefficient_override(self): """Verify urgency coefficient override""" - code, out, err = self.t("rc.urgency.uda.priority.H.coefficient:0.01234 _get 47.urgency") + code, out, err = self.t( + "rc.urgency.uda.priority.H.coefficient:0.01234 _get 47.urgency" + ) self.assertApproximately(0.01234, out) def test_urgency_no_task(self): @@ -297,13 +300,13 @@ class TestBug837(TestCase): def test_unblocked_urgency(self): """837: Verify urgency goes to zero after unblocking - Bug 837: When a task is completed, tasks that depended upon it do not - have the correct urgency and depend on 0 when edited + Bug 837: When a task is completed, tasks that depended upon it do not + have the correct urgency and depend on 0 when edited """ self.t("add one") self.t("add two dep:1") - self.t("list") # GC/handleRecurrence + self.t("list") # GC/handleRecurrence code, out, err = self.t("_get 1.urgency") self.assertEqual("8\n", out) @@ -312,7 +315,7 @@ class TestBug837(TestCase): self.assertEqual("-5\n", out) self.t("1 done") - self.t("list") # GC/handleRecurrence + self.t("list") # GC/handleRecurrence code, out, err = self.t("_get 1.urgency") self.assertEqual("0\n", out) @@ -320,6 +323,7 @@ class TestBug837(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/urgency_inherit.test.py b/test/urgency_inherit.test.py index ac38aa6a9..3df1c42fb 100755 --- a/test/urgency_inherit.test.py +++ b/test/urgency_inherit.test.py @@ -29,6 +29,7 @@ import sys import os import unittest import json + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -78,6 +79,7 @@ class TestUrgencyInherit(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/util.test.cpp b/test/util.test.cpp index 36276a38b..a627c16a9 100644 --- a/test/util.test.cpp +++ b/test/util.test.cpp @@ -27,22 +27,22 @@ #include // cmake.h include header must come first -#include -#include #include -#include +#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (19); +int main(int, char**) { + UnitTest t(19); Context context; Context::setContext(&context); // Ensure environment has no influence. - unsetenv ("TASKDATA"); - unsetenv ("TASKRC"); + unsetenv("TASKDATA"); + unsetenv("TASKRC"); // TODO int confirm4 (const std::string&); @@ -50,46 +50,48 @@ int main (int, char**) // TODO These are in feedback.cpp, not util.cpp. Task left; - left.set ("zero", "0"); - left.set ("one", 1); - left.set ("two", 2); + left.set("zero", "0"); + left.set("one", 1); + left.set("two", 2); Task right; - right.set ("zero", "00"); - right.set ("two", 2); - right.set ("three", 3); + right.set("zero", "00"); + right.set("two", 2); + right.set("three", 3); - Task rightAgain (right); + Task rightAgain(right); - std::string output = left.diff (right); - t.ok (!(left == right), "Detected changes"); - t.ok (output.find ("Zero will be changed from '0' to '00'") != std::string::npos, "Detected change zero:0 -> zero:00"); - t.ok (output.find ("One will be deleted") != std::string::npos, "Detected deletion one:1 ->"); - t.ok (output.find ("Two") == std::string::npos, "Detected no change two:2 -> two:2"); - t.ok (output.find ("Three will be set to '3'") != std::string::npos, "Detected addition -> three:3"); + std::string output = left.diff(right); + t.ok(!(left == right), "Detected changes"); + t.ok(output.find("Zero will be changed from '0' to '00'") != std::string::npos, + "Detected change zero:0 -> zero:00"); + t.ok(output.find("One will be deleted") != std::string::npos, "Detected deletion one:1 ->"); + t.ok(output.find("Two") == std::string::npos, "Detected no change two:2 -> two:2"); + t.ok(output.find("Three will be set to '3'") != std::string::npos, + "Detected addition -> three:3"); - output = right.diff (rightAgain); - t.ok (output.find ("No changes will be made") != std::string::npos, "No changes detected"); + output = right.diff(rightAgain); + t.ok(output.find("No changes will be made") != std::string::npos, "No changes detected"); - // std::vector indentProject (const std::string&, const std::string whitespace=" ", char delimiter='.'); - t.is (indentProject (""), "", "indentProject '' -> ''"); - t.is (indentProject ("one"), "one", "indentProject 'one' -> 'one'"); - t.is (indentProject ("one.two"), " two", "indentProject 'one.two' -> ' two'"); - t.is (indentProject ("one.two.three"), " three", "indentProject 'one.two.three' -> ' three'"); + // std::vector indentProject (const std::string&, const std::string whitespace=" ", + // char delimiter='.'); + t.is(indentProject(""), "", "indentProject '' -> ''"); + t.is(indentProject("one"), "one", "indentProject 'one' -> 'one'"); + t.is(indentProject("one.two"), " two", "indentProject 'one.two' -> ' two'"); + t.is(indentProject("one.two.three"), " three", "indentProject 'one.two.three' -> ' three'"); // bool nontrivial (const std::string&); - t.notok (nontrivial (""), "nontrivial '' -> false"); - t.notok (nontrivial (" "), "nontrivial ' ' -> false"); - t.notok (nontrivial ("\t\t"), "nontrivial '\\t\\t' -> false"); - t.notok (nontrivial (" \t \t"), "nontrivial ' \\t \\t' -> false"); - t.ok (nontrivial ("a"), "nontrivial 'a' -> true"); - t.ok (nontrivial (" a"), "nontrivial ' a' -> true"); - t.ok (nontrivial ("a "), "nontrivial 'a ' -> true"); - t.ok (nontrivial (" \t\ta"), "nontrivial ' \\t\\ta' -> true"); - t.ok (nontrivial ("a\t\t "), "nontrivial 'a\\t\\t ' -> true"); + t.notok(nontrivial(""), "nontrivial '' -> false"); + t.notok(nontrivial(" "), "nontrivial ' ' -> false"); + t.notok(nontrivial("\t\t"), "nontrivial '\\t\\t' -> false"); + t.notok(nontrivial(" \t \t"), "nontrivial ' \\t \\t' -> false"); + t.ok(nontrivial("a"), "nontrivial 'a' -> true"); + t.ok(nontrivial(" a"), "nontrivial ' a' -> true"); + t.ok(nontrivial("a "), "nontrivial 'a ' -> true"); + t.ok(nontrivial(" \t\ta"), "nontrivial ' \\t\\ta' -> true"); + t.ok(nontrivial("a\t\t "), "nontrivial 'a\\t\\t ' -> true"); return 0; } //////////////////////////////////////////////////////////////////////////////// - diff --git a/test/uuid.test.py b/test/uuid.test.py index ca42036b7..5227cb616 100755 --- a/test/uuid.test.py +++ b/test/uuid.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -40,7 +41,9 @@ class TestUUID(TestCase): self.t.config("dateformat", "m/d/Y") - self.t("import -", input="""[ + self.t( + "import -", + input="""[ {"description":"one", "entry":"1315260230", "status":"pending", "uuid":"9deed7ca-843d-4259-b2c4-40ce73e8e4f3"}, {"description":"two", "entry":"1315260230", "status":"pending", "uuid":"0f4c83d2-552f-4108-ae3f-ccc7959f84a3"}, {"description":"three", "entry":"1315260230", "status":"pending", "uuid":"aa4abef1-1dc5-4a43-b6a0-7872df3094bb"}, @@ -51,7 +54,8 @@ class TestUUID(TestCase): {"description":"seven", "end":"1315338826", "entry":"1315338726", "status":"completed", "uuid":"abcdefab-abcd-abcd-abcd-abcdefabcdef"}, {"description":"eenndd", "end":"1315335841", "entry":"1315335841", "start":"1315338516", "status":"completed", "uuid":"727baa6c-65b8-485e-a810-e133e3cd83dc"}, {"description":"UUNNDDOO", "end":"1315338626", "entry":"1315338626", "status":"completed", "uuid":"c1361003-948e-43e8-85c8-15d28dc3c71c"} - ]""") + ]""", + ) def _config_unittest_report(self): self.t.config("report.unittest.columns", "id,entry,start,description") @@ -210,18 +214,19 @@ class TestFeature891(TestCase): def test_uuid_filter(self): """891: Test that a task is addressable using UUIDs of length 7 - 36""" - for i in range(35,7,-1): + for i in range(35, 7, -1): code, out, err = self.t(self.uuid[0:i] + " list") self.assertIn("one", out) self.assertNotIn("two", out) # TODO This should fail because a 7-character UUID is not a UUID, but # instead it blindly does nothing, and succeeds. Voodoo. - #code, out, err = self.t(self.uuid[0:6] + " list") + # code, out, err = self.t(self.uuid[0:6] + " list") if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/variant_add.test.cpp b/test/variant_add.test.cpp index 8c6f22e9f..1cbd1eb92 100644 --- a/test/variant_add.test.cpp +++ b/test/variant_add.test.cpp @@ -27,213 +27,221 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include #define EPSILON 0.001 //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (80); +int main(int, char**) { + UnitTest t(80); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // boolean + boolean -> ERROR - try {Variant v00 = v0 + v0; t.fail ("true + true --> error");} - catch (...) {t.pass ("true + true --> error");} + try { + Variant v00 = v0 + v0; + t.fail("true + true --> error"); + } catch (...) { + t.pass("true + true --> error"); + } // boolean + integer -> integer Variant v01 = v0 + v1; - t.is (v01.type (), Variant::type_integer, "true + 42 --> integer"); - t.is (v01.get_integer (), 43, "true + 42 --> 43"); + t.is(v01.type(), Variant::type_integer, "true + 42 --> integer"); + t.is(v01.get_integer(), 43, "true + 42 --> 43"); // boolean + real -> real Variant v02 = v0 + v2; - t.is (v02.type (), Variant::type_real, "true + 3.14 --> real"); - t.is (v02.get_real (), 4.14, EPSILON, "true + 3.14 --> 4.14"); + t.is(v02.type(), Variant::type_real, "true + 3.14 --> real"); + t.is(v02.get_real(), 4.14, EPSILON, "true + 3.14 --> 4.14"); // boolean + string -> string Variant v03 = v0 + v3; - t.is (v03.type (), Variant::type_string, "true + foo --> string"); - t.is (v03.get_string (), "truefoo", "true + foo --> truefoo"); + t.is(v03.type(), Variant::type_string, "true + foo --> string"); + t.is(v03.get_string(), "truefoo", "true + foo --> truefoo"); // boolean + date -> date Variant v04 = v0 + v4; - t.is (v04.type (), Variant::type_date, "true + 1234567890 --> date"); - t.is (v04.get_date (), "1234567891", "true + 1234567890 --> 1234567891"); + t.is(v04.type(), Variant::type_date, "true + 1234567890 --> date"); + t.is(v04.get_date(), "1234567891", "true + 1234567890 --> 1234567891"); // boolean + duration -> duration Variant v05 = v0 + v5; - t.is (v05.type (), Variant::type_duration, "true + 1200 --> duration"); - t.is (v05.get_duration (), "1201", "true + 1200 --> 1201"); + t.is(v05.type(), Variant::type_duration, "true + 1200 --> duration"); + t.is(v05.get_duration(), "1201", "true + 1200 --> 1201"); // integer + boolean -> integer Variant v10 = v1 + v0; - t.is (v10.type (), Variant::type_integer, "42 + true --> integer"); - t.is (v10.get_integer (), 43, "42 + true --> 43"); + t.is(v10.type(), Variant::type_integer, "42 + true --> integer"); + t.is(v10.get_integer(), 43, "42 + true --> 43"); // integer + integer -> integer Variant v11 = v1 + v1; - t.is (v11.type (), Variant::type_integer, "42 + 42 --> integer"); - t.is (v11.get_integer (), 84, "42 + 42 --> 84"); + t.is(v11.type(), Variant::type_integer, "42 + 42 --> integer"); + t.is(v11.get_integer(), 84, "42 + 42 --> 84"); // integer + real -> real Variant v12 = v1 + v2; - t.is (v12.type (), Variant::type_real, "42 + 3.14 --> real"); - t.is (v12.get_real (), 45.14, EPSILON, "42 + 3.14 --> 45.14"); + t.is(v12.type(), Variant::type_real, "42 + 3.14 --> real"); + t.is(v12.get_real(), 45.14, EPSILON, "42 + 3.14 --> 45.14"); // integer + string -> string Variant v13 = v1 + v3; - t.is (v13.type (), Variant::type_string, "42 + foo --> string"); - t.is (v13.get_string (), "42foo", "42 + foo --> 42foo"); + t.is(v13.type(), Variant::type_string, "42 + foo --> string"); + t.is(v13.get_string(), "42foo", "42 + foo --> 42foo"); // integer + date -> date Variant v14 = v1 + v4; - t.is (v14.type (), Variant::type_date, "42 + 1234567890 --> date"); - t.is (v14.get_date (), 1234567932, "42 + 1234567890 --> 1234567932"); + t.is(v14.type(), Variant::type_date, "42 + 1234567890 --> date"); + t.is(v14.get_date(), 1234567932, "42 + 1234567890 --> 1234567932"); // integer + duration -> duration Variant v15 = v1 + v5; - t.is (v15.type (), Variant::type_duration, "42 + 1200 --> duration"); - t.is (v15.get_duration (), 1242, "42 + 1200 --> 1242"); + t.is(v15.type(), Variant::type_duration, "42 + 1200 --> duration"); + t.is(v15.get_duration(), 1242, "42 + 1200 --> 1242"); // real + boolean -> real Variant v20 = v2 + v0; - t.is (v20.type (), Variant::type_real, "3.14 + true --> real"); - t.is (v20.get_real (), 4.14, EPSILON, "3.14 + true --> 4.14"); + t.is(v20.type(), Variant::type_real, "3.14 + true --> real"); + t.is(v20.get_real(), 4.14, EPSILON, "3.14 + true --> 4.14"); // real + integer -> real Variant v21 = v2 + v1; - t.is (v21.type (), Variant::type_real, "3.14 + 42 --> real"); - t.is (v21.get_real (), 45.14, EPSILON, "3.14 + 42 --> 45.14"); + t.is(v21.type(), Variant::type_real, "3.14 + 42 --> real"); + t.is(v21.get_real(), 45.14, EPSILON, "3.14 + 42 --> 45.14"); // real + real -> real Variant v22 = v2 + v2; - t.is (v22.type (), Variant::type_real, "3.14 + 3.14 --> real"); - t.is (v22.get_real (), 6.28, EPSILON, "3.14 + 3.14 --> 6.28"); + t.is(v22.type(), Variant::type_real, "3.14 + 3.14 --> real"); + t.is(v22.get_real(), 6.28, EPSILON, "3.14 + 3.14 --> 6.28"); // real + string -> string Variant v23 = v2 + v3; - t.is (v23.type (), Variant::type_string, "3.14 + foo --> string"); - t.is (v23.get_string (), "3.14foo", "3.14 + foo --> 3.14foo"); + t.is(v23.type(), Variant::type_string, "3.14 + foo --> string"); + t.is(v23.get_string(), "3.14foo", "3.14 + foo --> 3.14foo"); // real + date -> date Variant v24 = v2 + v4; - t.is (v24.type (), Variant::type_date, "3.14 + 1234567890 --> date"); - t.is (v24.get_date (), 1234567893, "3.14 + 1234567890 --> 1234567893"); + t.is(v24.type(), Variant::type_date, "3.14 + 1234567890 --> date"); + t.is(v24.get_date(), 1234567893, "3.14 + 1234567890 --> 1234567893"); // real + duration -> duration Variant v25 = v2 + v5; - t.is (v25.type (), Variant::type_duration, "3.14 + 1200 --> duration"); - t.is (v25.get_duration (), 1203, "3.14 + 1200 --> 1203"); + t.is(v25.type(), Variant::type_duration, "3.14 + 1200 --> duration"); + t.is(v25.get_duration(), 1203, "3.14 + 1200 --> 1203"); // string + boolean -> string Variant v30 = v3 + v0; - t.is (v30.type (), Variant::type_string, "foo + true --> string"); - t.is (v30.get_string (), "footrue", "foo + true --> footrue"); + t.is(v30.type(), Variant::type_string, "foo + true --> string"); + t.is(v30.get_string(), "footrue", "foo + true --> footrue"); // string + integer -> string Variant v31 = v3 + v1; - t.is (v31.type (), Variant::type_string, "foo + 42 --> string"); - t.is (v31.get_string (), "foo42", "foo + 42 --> foo42"); + t.is(v31.type(), Variant::type_string, "foo + 42 --> string"); + t.is(v31.get_string(), "foo42", "foo + 42 --> foo42"); // string + real -> string Variant v32 = v3 + v2; - t.is (v32.type (), Variant::type_string, "foo + 3.14 --> string"); - t.is (v32.get_string (), "foo3.14", "foo + 3.14 --> foo3.14"); + t.is(v32.type(), Variant::type_string, "foo + 3.14 --> string"); + t.is(v32.get_string(), "foo3.14", "foo + 3.14 --> foo3.14"); // string + string -> string Variant v33 = v3 + v3; - t.is (v33.type (), Variant::type_string, "foo + foo --> string"); - t.is (v33.get_string (), "foofoo", "foo + foo --> foofoo"); + t.is(v33.type(), Variant::type_string, "foo + foo --> string"); + t.is(v33.get_string(), "foofoo", "foo + foo --> foofoo"); // string + date -> string Variant v34 = v3 + v4; - t.is (v34.type (), Variant::type_string, "foo + 1234567890 --> string"); - std::string s = v34.get_string (); - t.is ((int)s[7], (int)'-', "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); - t.is ((int)s[10], (int)'-', "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); - t.is ((int)s[13], (int)'T', "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); - t.is ((int)s[16], (int)':', "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); - t.is ((int)s[19], (int)':', "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); - t.is ((int)s.length (), 22, "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); + t.is(v34.type(), Variant::type_string, "foo + 1234567890 --> string"); + std::string s = v34.get_string(); + t.is((int)s[7], (int)'-', "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); + t.is((int)s[10], (int)'-', "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); + t.is((int)s[13], (int)'T', "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); + t.is((int)s[16], (int)':', "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); + t.is((int)s[19], (int)':', "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); + t.is((int)s.length(), 22, "foo + 1234567890 --> fooYYYY-MM-DDThh:mm:ss"); // string + duration -> string Variant v35 = v3 + v5; - t.is (v35.type (), Variant::type_string, "foo + 1200 --> string"); - t.is (v35.get_string (), "fooPT20M", "foo + 1200 --> fooPT20M"); + t.is(v35.type(), Variant::type_string, "foo + 1200 --> string"); + t.is(v35.get_string(), "fooPT20M", "foo + 1200 --> fooPT20M"); // date + boolean -> date Variant v40 = v4 + v0; - t.is (v40.type (), Variant::type_date, "1234567890 + true --> date"); - t.is (v40.get_date (), 1234567891, "1234567890 + true --> 1234567891"); + t.is(v40.type(), Variant::type_date, "1234567890 + true --> date"); + t.is(v40.get_date(), 1234567891, "1234567890 + true --> 1234567891"); // date + integer -> date Variant v41 = v4 + v1; - t.is (v41.type (), Variant::type_date, "1234567890 + 42 --> date"); - t.is (v41.get_date (), 1234567932, "1234567890 + 42 --> 1234567932"); + t.is(v41.type(), Variant::type_date, "1234567890 + 42 --> date"); + t.is(v41.get_date(), 1234567932, "1234567890 + 42 --> 1234567932"); // date + real -> date Variant v42 = v4 + v2; - t.is (v42.type (), Variant::type_date, "1234567890 + 3.14 --> date"); - t.is (v42.get_date (), 1234567893, "1234567890 + 3.14 --> 1234567893"); + t.is(v42.type(), Variant::type_date, "1234567890 + 3.14 --> date"); + t.is(v42.get_date(), 1234567893, "1234567890 + 3.14 --> 1234567893"); // date + string -> string Variant v43 = v4 + v3; - t.is (v43.type (), Variant::type_string, "1234567890 + foo --> string"); - s = v43.get_string (); - t.is ((int)s[4], (int)'-', "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); - t.is ((int)s[7], (int)'-', "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); - t.is ((int)s[10], (int)'T', "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); - t.is ((int)s[13], (int)':', "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); - t.is ((int)s[16], (int)':', "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); - t.is ((int)s.length (), 22, "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); + t.is(v43.type(), Variant::type_string, "1234567890 + foo --> string"); + s = v43.get_string(); + t.is((int)s[4], (int)'-', "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); + t.is((int)s[7], (int)'-', "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); + t.is((int)s[10], (int)'T', "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); + t.is((int)s[13], (int)':', "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); + t.is((int)s[16], (int)':', "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); + t.is((int)s.length(), 22, "1234567890 + foo --> YYYY-MM-DDThh:mm:ssfoo"); // date + date -> ERROR - try {Variant v44 = v4 + v4; t.fail ("1234567890 + 1234567890 --> error");} - catch (...) {t.pass ("1234567890 + 1234567890 --> error");} + try { + Variant v44 = v4 + v4; + t.fail("1234567890 + 1234567890 --> error"); + } catch (...) { + t.pass("1234567890 + 1234567890 --> error"); + } // date + duration -> date Variant v45 = v4 + v5; - t.is (v45.type (), Variant::type_date, "1234567890 + 1200 --> date"); - t.is (v45.get_date (), 1234569090, "1234567890 + 1200 --> 1234569090"); + t.is(v45.type(), Variant::type_date, "1234567890 + 1200 --> date"); + t.is(v45.get_date(), 1234569090, "1234567890 + 1200 --> 1234569090"); // duration + boolean -> duration Variant v50 = v5 + v0; - t.is (v50.type (), Variant::type_duration, "1200 + true --> duration"); - t.is (v50.get_duration (), 1201, "1200 + true --> 1201"); + t.is(v50.type(), Variant::type_duration, "1200 + true --> duration"); + t.is(v50.get_duration(), 1201, "1200 + true --> 1201"); // duration + integer -> duration Variant v51 = v5 + v1; - t.is (v51.type (), Variant::type_duration, "1200 + 42 --> duration"); - t.is (v51.get_duration (), 1242, "1200 + 42 --> 1242"); + t.is(v51.type(), Variant::type_duration, "1200 + 42 --> duration"); + t.is(v51.get_duration(), 1242, "1200 + 42 --> 1242"); // duration + real -> duration Variant v52 = v5 + v2; - t.is (v52.type (), Variant::type_duration, "1200 + 3.14 --> duration"); - t.is (v52.get_duration (), 1203, "1200 + 3.14 --> 1203"); + t.is(v52.type(), Variant::type_duration, "1200 + 3.14 --> duration"); + t.is(v52.get_duration(), 1203, "1200 + 3.14 --> 1203"); // duration + string -> string Variant v53 = v5 + v3; - t.is (v53.type (), Variant::type_string, "1200 + foo --> string"); - t.is (v53.get_string (), "PT20Mfoo", "1200 + foo --> PT20Mfoo"); + t.is(v53.type(), Variant::type_string, "1200 + foo --> string"); + t.is(v53.get_string(), "PT20Mfoo", "1200 + foo --> PT20Mfoo"); // duration + date -> date Variant v54 = v5 + v4; - t.is (v54.type (), Variant::type_date, "1200 + 1234567890 --> date"); - t.is (v54.get_date (), 1234569090, "1200 + 1234567890 --> 1234569090"); + t.is(v54.type(), Variant::type_date, "1200 + 1234567890 --> date"); + t.is(v54.get_date(), 1234569090, "1200 + 1234567890 --> 1234569090"); // duration + duration -> duration Variant v55 = v5 + v5; - t.is (v55.type (), Variant::type_duration, "1200 + 1200 --> duration"); - t.is (v55.get_duration (), 2400, "1200 + 1200 --> 2400"); + t.is(v55.type(), Variant::type_duration, "1200 + 1200 --> duration"); + t.is(v55.get_duration(), 2400, "1200 + 1200 --> 2400"); return 0; } diff --git a/test/variant_and.test.cpp b/test/variant_and.test.cpp index 237bbd02d..78267656b 100644 --- a/test/variant_and.test.cpp +++ b/test/variant_and.test.cpp @@ -27,173 +27,173 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (76); +int main(int, char**) { + UnitTest t(76); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // Truth table. - Variant vFalse (false); - Variant vTrue (true); - t.is (vFalse && vFalse, false, "false && false --> false"); - t.is (vFalse && vTrue, false, "false && true --> false"); - t.is (vTrue && vFalse, false, "true && false --> false"); - t.is (vTrue && vTrue, true, "true && true --> true"); + Variant vFalse(false); + Variant vTrue(true); + t.is(vFalse && vFalse, false, "false && false --> false"); + t.is(vFalse && vTrue, false, "false && true --> false"); + t.is(vTrue && vFalse, false, "true && false --> false"); + t.is(vTrue && vTrue, true, "true && true --> true"); Variant v00 = v0 && v0; - t.is (v00.type (), Variant::type_boolean, "true && true --> boolean"); - t.is (v00.get_bool (), true, "true && true --> true"); + t.is(v00.type(), Variant::type_boolean, "true && true --> boolean"); + t.is(v00.get_bool(), true, "true && true --> true"); Variant v01 = v0 && v1; - t.is (v01.type (), Variant::type_boolean, "true && 42 --> boolean"); - t.is (v01.get_bool (), true, "true && 42 --> true"); + t.is(v01.type(), Variant::type_boolean, "true && 42 --> boolean"); + t.is(v01.get_bool(), true, "true && 42 --> true"); Variant v02 = v0 && v2; - t.is (v02.type (), Variant::type_boolean, "true && 3.14 --> boolean"); - t.is (v02.get_bool (), true, "true && 3.14 --> true"); + t.is(v02.type(), Variant::type_boolean, "true && 3.14 --> boolean"); + t.is(v02.get_bool(), true, "true && 3.14 --> true"); Variant v03 = v0 && v3; - t.is (v03.type (), Variant::type_boolean, "true && 'foo' --> boolean"); - t.is (v03.get_bool (), true, "true && 'foo' --> true"); + t.is(v03.type(), Variant::type_boolean, "true && 'foo' --> boolean"); + t.is(v03.get_bool(), true, "true && 'foo' --> true"); Variant v04 = v0 && v4; - t.is (v04.type (), Variant::type_boolean, "true && 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "true && 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "true && 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "true && 1234567890 --> true"); Variant v05 = v0 && v5; - t.is (v05.type (), Variant::type_boolean, "true && 1200 --> boolean"); - t.is (v05.get_bool (), true, "true && 1200 --> true"); + t.is(v05.type(), Variant::type_boolean, "true && 1200 --> boolean"); + t.is(v05.get_bool(), true, "true && 1200 --> true"); Variant v10 = v1 && v0; - t.is (v10.type (), Variant::type_boolean, "42 && true --> boolean"); - t.is (v10.get_bool (), true, "42 && true --> true"); + t.is(v10.type(), Variant::type_boolean, "42 && true --> boolean"); + t.is(v10.get_bool(), true, "42 && true --> true"); Variant v11 = v1 && v1; - t.is (v11.type (), Variant::type_boolean, "42 && 42 --> boolean"); - t.is (v11.get_bool (), true, "42 && 42 --> true"); + t.is(v11.type(), Variant::type_boolean, "42 && 42 --> boolean"); + t.is(v11.get_bool(), true, "42 && 42 --> true"); Variant v12 = v1 && v2; - t.is (v12.type (), Variant::type_boolean, "42 && 3.14 --> boolean"); - t.is (v12.get_bool (), true, "42 && 3.14 --> true"); + t.is(v12.type(), Variant::type_boolean, "42 && 3.14 --> boolean"); + t.is(v12.get_bool(), true, "42 && 3.14 --> true"); Variant v13 = v1 && v3; - t.is (v13.type (), Variant::type_boolean, "42 && 'foo' --> boolean"); - t.is (v13.get_bool (), true, "42 && 'foo' --> true"); + t.is(v13.type(), Variant::type_boolean, "42 && 'foo' --> boolean"); + t.is(v13.get_bool(), true, "42 && 'foo' --> true"); Variant v14 = v1 && v4; - t.is (v04.type (), Variant::type_boolean, "42 && 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "42 && 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "42 && 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "42 && 1234567890 --> true"); Variant v15 = v1 && v5; - t.is (v15.type (), Variant::type_boolean, "42 && 1200 --> boolean"); - t.is (v15.get_bool (), true, "42 && 1200 --> true"); + t.is(v15.type(), Variant::type_boolean, "42 && 1200 --> boolean"); + t.is(v15.get_bool(), true, "42 && 1200 --> true"); Variant v20 = v2 && v0; - t.is (v20.type (), Variant::type_boolean, "3.14 && true --> boolean"); - t.is (v20.get_bool (), true, "3.14 && true --> true"); + t.is(v20.type(), Variant::type_boolean, "3.14 && true --> boolean"); + t.is(v20.get_bool(), true, "3.14 && true --> true"); Variant v21 = v2 && v1; - t.is (v21.type (), Variant::type_boolean, "3.14 && 42 --> boolean"); - t.is (v21.get_bool (), true, "3.14 && 42 --> true"); + t.is(v21.type(), Variant::type_boolean, "3.14 && 42 --> boolean"); + t.is(v21.get_bool(), true, "3.14 && 42 --> true"); Variant v22 = v2 && v2; - t.is (v22.type (), Variant::type_boolean, "3.14 && 3.14 --> boolean"); - t.is (v22.get_bool (), true, "3.14 && 3.14 --> true"); + t.is(v22.type(), Variant::type_boolean, "3.14 && 3.14 --> boolean"); + t.is(v22.get_bool(), true, "3.14 && 3.14 --> true"); Variant v23 = v2 && v3; - t.is (v23.type (), Variant::type_boolean, "3.14 && 'foo' --> boolean"); - t.is (v23.get_bool (), true, "3.14 && 'foo' --> true"); + t.is(v23.type(), Variant::type_boolean, "3.14 && 'foo' --> boolean"); + t.is(v23.get_bool(), true, "3.14 && 'foo' --> true"); Variant v24 = v2 && v4; - t.is (v24.type (), Variant::type_boolean, "3.14 && 1234567890 --> boolean"); - t.is (v24.get_bool (), true, "3.14 && 1234567890 --> true"); + t.is(v24.type(), Variant::type_boolean, "3.14 && 1234567890 --> boolean"); + t.is(v24.get_bool(), true, "3.14 && 1234567890 --> true"); Variant v25 = v2 && v5; - t.is (v25.type (), Variant::type_boolean, "3.14 && 1200 --> boolean"); - t.is (v25.get_bool (), true, "3.14 && 1200 --> true"); + t.is(v25.type(), Variant::type_boolean, "3.14 && 1200 --> boolean"); + t.is(v25.get_bool(), true, "3.14 && 1200 --> true"); Variant v30 = v3 && v0; - t.is (v30.type (), Variant::type_boolean, "'foo' && true --> boolean"); - t.is (v30.get_bool (), true, "'foo' && true --> true"); + t.is(v30.type(), Variant::type_boolean, "'foo' && true --> boolean"); + t.is(v30.get_bool(), true, "'foo' && true --> true"); Variant v31 = v3 && v1; - t.is (v31.type (), Variant::type_boolean, "'foo' && 42 --> boolean"); - t.is (v31.get_bool (), true, "'foo' && 42 --> true"); + t.is(v31.type(), Variant::type_boolean, "'foo' && 42 --> boolean"); + t.is(v31.get_bool(), true, "'foo' && 42 --> true"); Variant v32 = v3 && v2; - t.is (v32.type (), Variant::type_boolean, "'foo' && 3.14 --> boolean"); - t.is (v32.get_bool (), true, "'foo' && 3.14 --> true"); + t.is(v32.type(), Variant::type_boolean, "'foo' && 3.14 --> boolean"); + t.is(v32.get_bool(), true, "'foo' && 3.14 --> true"); Variant v33 = v3 && v3; - t.is (v33.type (), Variant::type_boolean, "'foo' && 'foo' --> boolean"); - t.is (v33.get_bool (), true, "'foo' && 'foo' --> true"); + t.is(v33.type(), Variant::type_boolean, "'foo' && 'foo' --> boolean"); + t.is(v33.get_bool(), true, "'foo' && 'foo' --> true"); Variant v34 = v3 && v4; - t.is (v34.type (), Variant::type_boolean, "'foo' && 1234567890 --> boolean"); - t.is (v34.get_bool (), true, "'foo' && 1234567890 --> true"); + t.is(v34.type(), Variant::type_boolean, "'foo' && 1234567890 --> boolean"); + t.is(v34.get_bool(), true, "'foo' && 1234567890 --> true"); Variant v35 = v3 && v5; - t.is (v35.type (), Variant::type_boolean, "'foo' && 1200 --> boolean"); - t.is (v35.get_bool (), true, "'foo' && 1200 --> true"); + t.is(v35.type(), Variant::type_boolean, "'foo' && 1200 --> boolean"); + t.is(v35.get_bool(), true, "'foo' && 1200 --> true"); Variant v40 = v4 && v0; - t.is (v40.type (), Variant::type_boolean, "1234567890 && true --> boolean"); - t.is (v40.get_bool (), true, "1234567890 && true --> true"); + t.is(v40.type(), Variant::type_boolean, "1234567890 && true --> boolean"); + t.is(v40.get_bool(), true, "1234567890 && true --> true"); Variant v41 = v4 && v1; - t.is (v41.type (), Variant::type_boolean, "1234567890 && 42 --> boolean"); - t.is (v41.get_bool (), true, "1234567890 && 42 --> true"); + t.is(v41.type(), Variant::type_boolean, "1234567890 && 42 --> boolean"); + t.is(v41.get_bool(), true, "1234567890 && 42 --> true"); Variant v42 = v4 && v2; - t.is (v42.type (), Variant::type_boolean, "1234567890 && 3.14 --> boolean"); - t.is (v42.get_bool (), true, "1234567890 && 3.14 --> true"); + t.is(v42.type(), Variant::type_boolean, "1234567890 && 3.14 --> boolean"); + t.is(v42.get_bool(), true, "1234567890 && 3.14 --> true"); Variant v43 = v4 && v3; - t.is (v43.type (), Variant::type_boolean, "1234567890 && 'foo' --> boolean"); - t.is (v43.get_bool (), true, "1234567890 && 'foo' --> true"); + t.is(v43.type(), Variant::type_boolean, "1234567890 && 'foo' --> boolean"); + t.is(v43.get_bool(), true, "1234567890 && 'foo' --> true"); Variant v44 = v4 && v4; - t.is (v44.type (), Variant::type_boolean, "1234567890 && 1234567890 --> boolean"); - t.is (v44.get_bool (), true, "1234567890 && 1234567890 --> true"); + t.is(v44.type(), Variant::type_boolean, "1234567890 && 1234567890 --> boolean"); + t.is(v44.get_bool(), true, "1234567890 && 1234567890 --> true"); Variant v45 = v4 && v5; - t.is (v45.type (), Variant::type_boolean, "1234567890 && 1200 --> boolean"); - t.is (v45.get_bool (), true, "1234567890 && 1200 --> true"); + t.is(v45.type(), Variant::type_boolean, "1234567890 && 1200 --> boolean"); + t.is(v45.get_bool(), true, "1234567890 && 1200 --> true"); Variant v50 = v5 && v0; - t.is (v50.type (), Variant::type_boolean, "1200 && true --> boolean"); - t.is (v50.get_bool (), true, "1200 && true --> true"); + t.is(v50.type(), Variant::type_boolean, "1200 && true --> boolean"); + t.is(v50.get_bool(), true, "1200 && true --> true"); Variant v51 = v5 && v1; - t.is (v51.type (), Variant::type_boolean, "1200 && 42 --> boolean"); - t.is (v51.get_bool (), true, "1200 && 42 --> true"); + t.is(v51.type(), Variant::type_boolean, "1200 && 42 --> boolean"); + t.is(v51.get_bool(), true, "1200 && 42 --> true"); Variant v52 = v5 && v2; - t.is (v52.type (), Variant::type_boolean, "1200 && 3.14 --> boolean"); - t.is (v52.get_bool (), true, "1200 && 3.14 --> true"); + t.is(v52.type(), Variant::type_boolean, "1200 && 3.14 --> boolean"); + t.is(v52.get_bool(), true, "1200 && 3.14 --> true"); Variant v53 = v5 && v3; - t.is (v53.type (), Variant::type_boolean, "1200 && 'foo' --> boolean"); - t.is (v53.get_bool (), true, "1200 && 'foo' --> true"); + t.is(v53.type(), Variant::type_boolean, "1200 && 'foo' --> boolean"); + t.is(v53.get_bool(), true, "1200 && 'foo' --> true"); Variant v54 = v5 && v4; - t.is (v04.type (), Variant::type_boolean, "1200 && 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "1200 && 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "1200 && 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "1200 && 1234567890 --> true"); Variant v55 = v5 && v5; - t.is (v55.type (), Variant::type_boolean, "1200 && 1200 --> boolean"); - t.is (v55.get_bool (), true, "1200 && 1200 --> true"); + t.is(v55.type(), Variant::type_boolean, "1200 && 1200 --> boolean"); + t.is(v55.get_bool(), true, "1200 && 1200 --> true"); return 0; } diff --git a/test/variant_cast.test.cpp b/test/variant_cast.test.cpp index ac6c246ca..e93a82ac6 100644 --- a/test/variant_cast.test.cpp +++ b/test/variant_cast.test.cpp @@ -27,232 +27,230 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include #define EPSILON 0.001 //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (81); +int main(int, char**) { + UnitTest t(81); - time_t now = time (nullptr); + time_t now = time(nullptr); - try - { + try { // Variant::type_boolean --> * - Variant v00 (true); - v00.cast (Variant::type_boolean); - t.ok (v00.type () == Variant::type_boolean, "cast boolean --> boolean"); - t.ok (v00.get_bool () == true, "cast boolean --> boolean"); + Variant v00(true); + v00.cast(Variant::type_boolean); + t.ok(v00.type() == Variant::type_boolean, "cast boolean --> boolean"); + t.ok(v00.get_bool() == true, "cast boolean --> boolean"); - Variant v01 (true); - v01.cast (Variant::type_integer); - t.ok (v01.type () == Variant::type_integer, "cast boolean --> integer"); - t.ok (v01.get_integer () == 1, "cast boolean --> integer"); + Variant v01(true); + v01.cast(Variant::type_integer); + t.ok(v01.type() == Variant::type_integer, "cast boolean --> integer"); + t.ok(v01.get_integer() == 1, "cast boolean --> integer"); - Variant v02 (true); - v02.cast (Variant::type_real); - t.ok (v02.type () == Variant::type_real, "cast boolean --> real"); - t.is (v02.get_real (), 1.0, EPSILON, "cast boolean --> real"); + Variant v02(true); + v02.cast(Variant::type_real); + t.ok(v02.type() == Variant::type_real, "cast boolean --> real"); + t.is(v02.get_real(), 1.0, EPSILON, "cast boolean --> real"); - Variant v03 (true); - v03.cast (Variant::type_string); - t.ok (v03.type () == Variant::type_string, "cast boolean --> string"); - t.ok (v03.get_string () == "true", "cast boolean --> string"); + Variant v03(true); + v03.cast(Variant::type_string); + t.ok(v03.type() == Variant::type_string, "cast boolean --> string"); + t.ok(v03.get_string() == "true", "cast boolean --> string"); - Variant v04 (true); - v04.cast (Variant::type_date); - t.ok (v04.type () == Variant::type_date, "cast boolean --> date"); - t.is (v04.get_date (), 1, "cast boolean --> date"); + Variant v04(true); + v04.cast(Variant::type_date); + t.ok(v04.type() == Variant::type_date, "cast boolean --> date"); + t.is(v04.get_date(), 1, "cast boolean --> date"); - Variant v05 (true); - v05.cast (Variant::type_duration); - t.ok (v05.type () == Variant::type_duration, "cast boolean --> duration"); - t.is (v05.get_duration (), 1, "cast boolean --> duration"); + Variant v05(true); + v05.cast(Variant::type_duration); + t.ok(v05.type() == Variant::type_duration, "cast boolean --> duration"); + t.is(v05.get_duration(), 1, "cast boolean --> duration"); // Variant::type_integer --> * - Variant v10 (42); - v10.cast (Variant::type_boolean); - t.ok (v10.type () == Variant::type_boolean, "cast integer --> boolean"); - t.ok (v10.get_bool () == true, "cast integer --> boolean"); + Variant v10(42); + v10.cast(Variant::type_boolean); + t.ok(v10.type() == Variant::type_boolean, "cast integer --> boolean"); + t.ok(v10.get_bool() == true, "cast integer --> boolean"); - Variant v11 (42); - v11.cast (Variant::type_integer); - t.ok (v11.type () == Variant::type_integer, "cast integer --> integer"); - t.ok (v11.get_integer () == 42, "cast integer --> integer"); + Variant v11(42); + v11.cast(Variant::type_integer); + t.ok(v11.type() == Variant::type_integer, "cast integer --> integer"); + t.ok(v11.get_integer() == 42, "cast integer --> integer"); - Variant v12 (42); - v12.cast (Variant::type_real); - t.ok (v12.type () == Variant::type_real, "cast integer --> real"); - t.is (v12.get_real (), 42.0, EPSILON, "cast integer --> real"); + Variant v12(42); + v12.cast(Variant::type_real); + t.ok(v12.type() == Variant::type_real, "cast integer --> real"); + t.is(v12.get_real(), 42.0, EPSILON, "cast integer --> real"); - Variant v13 (42); - v13.cast (Variant::type_string); - t.is (v13.type (), Variant::type_string, "cast integer --> string"); - t.is (v13.get_string (), "42", "cast integer --> string"); + Variant v13(42); + v13.cast(Variant::type_string); + t.is(v13.type(), Variant::type_string, "cast integer --> string"); + t.is(v13.get_string(), "42", "cast integer --> string"); - Variant v14 (42); - v14.cast (Variant::type_date); - t.ok (v14.type () == Variant::type_date, "cast integer --> date"); - t.ok (v14.get_date () == 42, "cast integer --> date"); + Variant v14(42); + v14.cast(Variant::type_date); + t.ok(v14.type() == Variant::type_date, "cast integer --> date"); + t.ok(v14.get_date() == 42, "cast integer --> date"); - Variant v15 (42); - v15.cast (Variant::type_duration); - t.is (v15.type (), Variant::type_duration, "cast integer --> duration"); - t.is (v15.get_duration (), 42, "cast integer --> duration"); + Variant v15(42); + v15.cast(Variant::type_duration); + t.is(v15.type(), Variant::type_duration, "cast integer --> duration"); + t.is(v15.get_duration(), 42, "cast integer --> duration"); // Variant::type_real --> * - Variant v20 (3.14); - v20.cast (Variant::type_boolean); - t.ok (v20.type () == Variant::type_boolean, "cast real --> boolean"); - t.ok (v20.get_bool () == true, "cast real --> boolean"); + Variant v20(3.14); + v20.cast(Variant::type_boolean); + t.ok(v20.type() == Variant::type_boolean, "cast real --> boolean"); + t.ok(v20.get_bool() == true, "cast real --> boolean"); - Variant v21 (3.14); - v21.cast (Variant::type_integer); - t.ok (v21.type () == Variant::type_integer, "cast real --> integer"); - t.ok (v21.get_integer () == 3, "cast real --> integer"); + Variant v21(3.14); + v21.cast(Variant::type_integer); + t.ok(v21.type() == Variant::type_integer, "cast real --> integer"); + t.ok(v21.get_integer() == 3, "cast real --> integer"); - Variant v22 (3.14); - v22.cast (Variant::type_real); - t.ok (v22.type () == Variant::type_real, "cast real --> real"); - t.is (v22.get_real (), 3.14, EPSILON, "cast real --> real"); + Variant v22(3.14); + v22.cast(Variant::type_real); + t.ok(v22.type() == Variant::type_real, "cast real --> real"); + t.is(v22.get_real(), 3.14, EPSILON, "cast real --> real"); - Variant v23 (3.14); - v23.cast (Variant::type_string); - t.ok (v23.type () == Variant::type_string, "cast real --> string"); - t.ok (v23.get_string () == "3.14", "cast real --> string"); + Variant v23(3.14); + v23.cast(Variant::type_string); + t.ok(v23.type() == Variant::type_string, "cast real --> string"); + t.ok(v23.get_string() == "3.14", "cast real --> string"); - Variant v24 (3.14); - v24.cast (Variant::type_date); - t.ok (v24.type () == Variant::type_date, "cast real --> date"); - t.ok (v24.get_date () == 3, "cast real --> date"); + Variant v24(3.14); + v24.cast(Variant::type_date); + t.ok(v24.type() == Variant::type_date, "cast real --> date"); + t.ok(v24.get_date() == 3, "cast real --> date"); - Variant v25 (3.14); - v25.cast (Variant::type_duration); - t.ok (v25.type () == Variant::type_duration, "cast real --> duration"); - t.ok (v25.get_duration () == 3, "cast real --> duration"); + Variant v25(3.14); + v25.cast(Variant::type_duration); + t.ok(v25.type() == Variant::type_duration, "cast real --> duration"); + t.ok(v25.get_duration() == 3, "cast real --> duration"); // Variant::type_string --> * - Variant v30 ("foo"); - v30.cast (Variant::type_boolean); - t.ok (v30.type () == Variant::type_boolean, "cast string --> boolean"); - t.ok (v30.get_bool () == true, "cast string --> boolean"); + Variant v30("foo"); + v30.cast(Variant::type_boolean); + t.ok(v30.type() == Variant::type_boolean, "cast string --> boolean"); + t.ok(v30.get_bool() == true, "cast string --> boolean"); - Variant v31 ("42"); - v31.cast (Variant::type_integer); - t.ok (v31.type () == Variant::type_integer, "cast string --> integer"); - t.ok (v31.get_integer () == 42, "cast string --> integer"); + Variant v31("42"); + v31.cast(Variant::type_integer); + t.ok(v31.type() == Variant::type_integer, "cast string --> integer"); + t.ok(v31.get_integer() == 42, "cast string --> integer"); - Variant v31h ("0x20"); - v31h.cast (Variant::type_integer); - t.ok (v31h.type () == Variant::type_integer, "cast string(hex) --> integer"); - t.ok (v31h.get_integer () == 32, "cast string(hex) --> integer"); + Variant v31h("0x20"); + v31h.cast(Variant::type_integer); + t.ok(v31h.type() == Variant::type_integer, "cast string(hex) --> integer"); + t.ok(v31h.get_integer() == 32, "cast string(hex) --> integer"); - Variant v32 ("3.14"); - v32.cast (Variant::type_real); - t.ok (v32.type () == Variant::type_real, "cast string --> real"); - t.is (v32.get_real (), 3.14, EPSILON, "cast string --> real"); + Variant v32("3.14"); + v32.cast(Variant::type_real); + t.ok(v32.type() == Variant::type_real, "cast string --> real"); + t.is(v32.get_real(), 3.14, EPSILON, "cast string --> real"); - Variant v33 ("foo"); - v33.cast (Variant::type_string); - t.ok (v33.type () == Variant::type_string, "cast string --> string"); - t.ok (v33.get_string () == "foo", "cast string --> string"); + Variant v33("foo"); + v33.cast(Variant::type_string); + t.ok(v33.type() == Variant::type_string, "cast string --> string"); + t.ok(v33.get_string() == "foo", "cast string --> string"); - Variant v34 ("2013-12-07T16:33:00-05:00"); - v34.cast (Variant::type_date); - t.ok (v34.type () == Variant::type_date, "cast string --> date"); - t.ok (v34.get_date () == 1386451980, "cast string --> date"); + Variant v34("2013-12-07T16:33:00-05:00"); + v34.cast(Variant::type_date); + t.ok(v34.type() == Variant::type_date, "cast string --> date"); + t.ok(v34.get_date() == 1386451980, "cast string --> date"); - Variant v35 ("42 days"); - v35.cast (Variant::type_duration); - t.ok (v35.type () == Variant::type_duration, "cast string --> duration"); - t.is (v35.get_duration (), 3628800, "cast string --> duration"); + Variant v35("42 days"); + v35.cast(Variant::type_duration); + t.ok(v35.type() == Variant::type_duration, "cast string --> duration"); + t.is(v35.get_duration(), 3628800, "cast string --> duration"); - Variant v35b ("P42D"); - v35b.cast (Variant::type_duration); - t.ok (v35b.type () == Variant::type_duration, "cast string --> duration"); - t.is (v35b.get_duration (), 3628800, "cast string --> duration"); + Variant v35b("P42D"); + v35b.cast(Variant::type_duration); + t.ok(v35b.type() == Variant::type_duration, "cast string --> duration"); + t.is(v35b.get_duration(), 3628800, "cast string --> duration"); // Variant::type_date --> * - Variant v40 ((time_t) 1234567890, Variant::type_date); - v40.cast (Variant::type_boolean); - t.ok (v40.type () == Variant::type_boolean, "cast date --> boolean"); - t.ok (v40.get_bool () == true, "cast date --> boolean"); + Variant v40((time_t)1234567890, Variant::type_date); + v40.cast(Variant::type_boolean); + t.ok(v40.type() == Variant::type_boolean, "cast date --> boolean"); + t.ok(v40.get_bool() == true, "cast date --> boolean"); - Variant v41 ((time_t) 1234567890, Variant::type_date); - v41.cast (Variant::type_integer); - t.ok (v41.type () == Variant::type_integer, "cast date --> integer"); - t.ok (v41.get_integer () == 1234567890, "cast date --> integer"); + Variant v41((time_t)1234567890, Variant::type_date); + v41.cast(Variant::type_integer); + t.ok(v41.type() == Variant::type_integer, "cast date --> integer"); + t.ok(v41.get_integer() == 1234567890, "cast date --> integer"); - Variant v42 ((time_t) 1234567890, Variant::type_date); - v42.cast (Variant::type_real); - t.ok (v42.type () == Variant::type_real, "cast date --> real"); - t.is (v42.get_real (), 1234567890.0, EPSILON, "cast date --> real"); + Variant v42((time_t)1234567890, Variant::type_date); + v42.cast(Variant::type_real); + t.ok(v42.type() == Variant::type_real, "cast date --> real"); + t.is(v42.get_real(), 1234567890.0, EPSILON, "cast date --> real"); // YYYY-MM-DDThh:mm:ss // ^ ^ ^ ^ ^ - Variant v43 ((time_t) 1234567890, Variant::type_date); - v43.cast (Variant::type_string); - t.ok (v43.type () == Variant::type_string, "cast date --> string"); - std::string s = v43.get_string (); - t.is ((int)s[4], (int)'-', "cast date --> string"); - t.is ((int)s[7], (int)'-', "cast date --> string"); - t.is ((int)s[10], (int)'T', "cast date --> string"); - t.is ((int)s[13], (int)':', "cast date --> string"); - t.is ((int)s[16], (int)':', "cast date --> string"); - t.is ((int)s.length (), 19, "cast date --> string"); + Variant v43((time_t)1234567890, Variant::type_date); + v43.cast(Variant::type_string); + t.ok(v43.type() == Variant::type_string, "cast date --> string"); + std::string s = v43.get_string(); + t.is((int)s[4], (int)'-', "cast date --> string"); + t.is((int)s[7], (int)'-', "cast date --> string"); + t.is((int)s[10], (int)'T', "cast date --> string"); + t.is((int)s[13], (int)':', "cast date --> string"); + t.is((int)s[16], (int)':', "cast date --> string"); + t.is((int)s.length(), 19, "cast date --> string"); - Variant v44 ((time_t) 1234567890, Variant::type_date); - v44.cast (Variant::type_date); - t.ok (v44.type () == Variant::type_date, "cast date --> date"); - t.ok (v44.get_date () == 1234567890, "cast date --> date"); + Variant v44((time_t)1234567890, Variant::type_date); + v44.cast(Variant::type_date); + t.ok(v44.type() == Variant::type_date, "cast date --> date"); + t.ok(v44.get_date() == 1234567890, "cast date --> date"); - Variant v45 ((time_t) 1234567890, Variant::type_date); - v45.cast (Variant::type_duration); - t.ok (v45.type () == Variant::type_duration, "cast date --> duration"); - t.ok (v45.get_duration () <= 1234567890 - now, "cast date --> duration"); + Variant v45((time_t)1234567890, Variant::type_date); + v45.cast(Variant::type_duration); + t.ok(v45.type() == Variant::type_duration, "cast date --> duration"); + t.ok(v45.get_duration() <= 1234567890 - now, "cast date --> duration"); // Assuming this unit test takes less than 10 min to execute - t.ok (v45.get_duration () > 1234567890 - now - 600, "cast date --> duration"); + t.ok(v45.get_duration() > 1234567890 - now - 600, "cast date --> duration"); // Variant::type_duration --> * - Variant v50 ((time_t) 12345, Variant::type_duration); - v50.cast (Variant::type_boolean); - t.ok (v50.type () == Variant::type_boolean, "cast duration --> boolean"); - t.ok (v50.get_bool () == true, "cast duration --> boolean"); + Variant v50((time_t)12345, Variant::type_duration); + v50.cast(Variant::type_boolean); + t.ok(v50.type() == Variant::type_boolean, "cast duration --> boolean"); + t.ok(v50.get_bool() == true, "cast duration --> boolean"); - Variant v51 ((time_t) 12345, Variant::type_duration); - v51.cast (Variant::type_integer); - t.ok (v51.type () == Variant::type_integer, "cast duration --> integer"); - t.ok (v51.get_integer () == 12345, "cast duration --> integer"); + Variant v51((time_t)12345, Variant::type_duration); + v51.cast(Variant::type_integer); + t.ok(v51.type() == Variant::type_integer, "cast duration --> integer"); + t.ok(v51.get_integer() == 12345, "cast duration --> integer"); - Variant v52 ((time_t) 12345, Variant::type_duration); - v52.cast (Variant::type_real); - t.ok (v52.type () == Variant::type_real, "cast duration --> real"); - t.is (v52.get_real (), 12345.0, EPSILON, "cast duration --> real"); + Variant v52((time_t)12345, Variant::type_duration); + v52.cast(Variant::type_real); + t.ok(v52.type() == Variant::type_real, "cast duration --> real"); + t.is(v52.get_real(), 12345.0, EPSILON, "cast duration --> real"); - Variant v53 ((time_t) 12345, Variant::type_duration); - v53.cast (Variant::type_string); - t.ok (v53.type () == Variant::type_string, "cast duration --> string"); - t.is (v53.get_string (), "PT3H25M45S", "cast duration --> string"); + Variant v53((time_t)12345, Variant::type_duration); + v53.cast(Variant::type_string); + t.ok(v53.type() == Variant::type_string, "cast duration --> string"); + t.is(v53.get_string(), "PT3H25M45S", "cast duration --> string"); - Variant v54 ((time_t) 12345, Variant::type_duration); - v54.cast (Variant::type_date); - t.ok (v54.type () == Variant::type_date, "cast duration --> date"); - t.ok (v54.get_date () >= 12345 + now, "cast duration --> duration"); - t.ok (v54.get_date () < 12345 + now + 600, "cast duration --> duration"); + Variant v54((time_t)12345, Variant::type_duration); + v54.cast(Variant::type_date); + t.ok(v54.type() == Variant::type_date, "cast duration --> date"); + t.ok(v54.get_date() >= 12345 + now, "cast duration --> duration"); + t.ok(v54.get_date() < 12345 + now + 600, "cast duration --> duration"); - Variant v55 ((time_t) 12345, Variant::type_duration); - v55.cast (Variant::type_duration); - t.ok (v55.type () == Variant::type_duration, "cast duration --> duration"); - t.is (v55.get_duration (), 12345, "cast duration --> duration"); + Variant v55((time_t)12345, Variant::type_duration); + v55.cast(Variant::type_duration); + t.ok(v55.type() == Variant::type_duration, "cast duration --> duration"); + t.is(v55.get_duration(), 12345, "cast duration --> duration"); } - catch (const std::string& e) - { - t.diag (e); + catch (const std::string& e) { + t.diag(e); } return 0; diff --git a/test/variant_divide.test.cpp b/test/variant_divide.test.cpp index 676c25870..a42c61664 100644 --- a/test/variant_divide.test.cpp +++ b/test/variant_divide.test.cpp @@ -27,175 +27,287 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include #define EPSILON 0.0001 //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (44); +int main(int, char**) { + UnitTest t(44); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // boolean / boolean -> ERROR - try {Variant v00 = v0 / v0; t.fail ("true / true --> error");} - catch (...) {t.pass ("true / true --> error");} + try { + Variant v00 = v0 / v0; + t.fail("true / true --> error"); + } catch (...) { + t.pass("true / true --> error"); + } // boolean / integer -> ERROR - try {Variant v01 = v0 / v1; t.fail ("true / 42 --> error");} - catch (...) {t.pass ("true / 42 --> error");} + try { + Variant v01 = v0 / v1; + t.fail("true / 42 --> error"); + } catch (...) { + t.pass("true / 42 --> error"); + } // boolean / real -> ERROR - try {Variant v02 = v0 / v2; t.fail ("true / 3.14 --> error");} - catch (...) {t.pass ("true / 3.14 --> error");} + try { + Variant v02 = v0 / v2; + t.fail("true / 3.14 --> error"); + } catch (...) { + t.pass("true / 3.14 --> error"); + } // boolean / string -> ERROR - try {Variant v03 = v0 / v3; t.fail ("true / foo --> error");} - catch (...) {t.pass ("true / foo --> error");} + try { + Variant v03 = v0 / v3; + t.fail("true / foo --> error"); + } catch (...) { + t.pass("true / foo --> error"); + } // boolean / date -> ERROR - try {Variant v04 = v0 / v4; t.fail ("true / 1234567890 --> error");} - catch (...) {t.pass ("true / 1234567890 --> error");} + try { + Variant v04 = v0 / v4; + t.fail("true / 1234567890 --> error"); + } catch (...) { + t.pass("true / 1234567890 --> error"); + } // boolean / duration -> ERROR - try {Variant v05 = v0 / v5; t.fail ("true / 1200 --> error");} - catch (...) {t.pass ("true / 1200 --> error");} + try { + Variant v05 = v0 / v5; + t.fail("true / 1200 --> error"); + } catch (...) { + t.pass("true / 1200 --> error"); + } // integer / boolean -> ERROR - try {Variant v10 = v1 / v0; t.fail ("42 / true --> error");} - catch (...) {t.pass ("42 / true --> error");} + try { + Variant v10 = v1 / v0; + t.fail("42 / true --> error"); + } catch (...) { + t.pass("42 / true --> error"); + } // integer / integer -> integer Variant v11 = v1 / v1; - t.is (v11.type (), Variant::type_integer, "42 / 42 --> integer"); - t.is (v11.get_integer (), 1, "42 / 42 --> 1"); + t.is(v11.type(), Variant::type_integer, "42 / 42 --> integer"); + t.is(v11.get_integer(), 1, "42 / 42 --> 1"); // integer / real -> real Variant v12 = v1 / v2; - t.is (v12.type (), Variant::type_real, "42 / 3.14 --> real"); - t.is (v12.get_real (), 13.3757, EPSILON, "42 / 3.14 --> 13.3757"); + t.is(v12.type(), Variant::type_real, "42 / 3.14 --> real"); + t.is(v12.get_real(), 13.3757, EPSILON, "42 / 3.14 --> 13.3757"); // integer / string -> ERROR - try {Variant v13 = v1 / v3; t.fail ("42 / foo --> error");} - catch (...) {t.pass ("42 / foo --> error");} + try { + Variant v13 = v1 / v3; + t.fail("42 / foo --> error"); + } catch (...) { + t.pass("42 / foo --> error"); + } // integer / date -> ERROR - try {Variant v14 = v1 / v4; t.fail ("42 / 1234567890 --> error");} - catch (...) {t.pass ("42 / 1234567890 --> error");} + try { + Variant v14 = v1 / v4; + t.fail("42 / 1234567890 --> error"); + } catch (...) { + t.pass("42 / 1234567890 --> error"); + } // integer / duration -> duration Variant v15 = v1 / v5; - t.is (v15.type (), Variant::type_duration, "42 / 1200 --> duration"); - t.is (v15.get_duration (), 0, "42 / 1200 --> 0"); + t.is(v15.type(), Variant::type_duration, "42 / 1200 --> duration"); + t.is(v15.get_duration(), 0, "42 / 1200 --> 0"); // real / boolean -> ERROR - try {Variant v20 = v2 / v0; t.fail ("3.14 / true --> error");} - catch (...) {t.pass ("3.14 / true --> error");} + try { + Variant v20 = v2 / v0; + t.fail("3.14 / true --> error"); + } catch (...) { + t.pass("3.14 / true --> error"); + } // real / integer -> real Variant v21 = v2 / v1; - t.is (v21.type (), Variant::type_real, "3.14 / 42 --> real"); - t.is (v21.get_real (), 0.0747, EPSILON, "3.14 / 42 --> 0.0747"); + t.is(v21.type(), Variant::type_real, "3.14 / 42 --> real"); + t.is(v21.get_real(), 0.0747, EPSILON, "3.14 / 42 --> 0.0747"); // real / real -> real Variant v22 = v2 / v2; - t.is (v22.type (), Variant::type_real, "3.14 / 3.14 --> real"); - t.is (v22.get_real (), 1.0, EPSILON, "3.14 / 3.14 --> 1.0"); + t.is(v22.type(), Variant::type_real, "3.14 / 3.14 --> real"); + t.is(v22.get_real(), 1.0, EPSILON, "3.14 / 3.14 --> 1.0"); // real / string -> error - try {Variant v23 = v2 / v3; t.fail ("3.14 / foo --> error");} - catch (...) {t.pass ("3.14 / foo --> error");} + try { + Variant v23 = v2 / v3; + t.fail("3.14 / foo --> error"); + } catch (...) { + t.pass("3.14 / foo --> error"); + } // real / date -> error - try {Variant v24 = v2 / v4; t.fail ("3.14 / 1234567890 --> error");} - catch (...) {t.pass ("3.14 / 1234567890 --> error");} + try { + Variant v24 = v2 / v4; + t.fail("3.14 / 1234567890 --> error"); + } catch (...) { + t.pass("3.14 / 1234567890 --> error"); + } // real / duration -> duration Variant v25 = v2 / v5; - t.is (v25.type (), Variant::type_duration, "3.14 / 1200 --> duration"); - t.is (v25.get_duration (), 0, "3.14 / 1200 --> 0"); + t.is(v25.type(), Variant::type_duration, "3.14 / 1200 --> duration"); + t.is(v25.get_duration(), 0, "3.14 / 1200 --> 0"); // string / boolean -> ERROR - try {Variant v30 = v3 / v0; t.fail ("foo / true --> error");} - catch (...) {t.pass ("foo / true --> error");} + try { + Variant v30 = v3 / v0; + t.fail("foo / true --> error"); + } catch (...) { + t.pass("foo / true --> error"); + } // string / integer -> ERROR - try {Variant v31 = v3 / v1; t.fail ("foo / 42 --> error");} - catch (...) {t.pass ("foo / 42 --> error");} + try { + Variant v31 = v3 / v1; + t.fail("foo / 42 --> error"); + } catch (...) { + t.pass("foo / 42 --> error"); + } // string / real -> ERROR - try {Variant v32 = v3 / v2; t.fail ("foo / 3.14 --> error");} - catch (...) {t.pass ("foo / 3.14 --> error");} + try { + Variant v32 = v3 / v2; + t.fail("foo / 3.14 --> error"); + } catch (...) { + t.pass("foo / 3.14 --> error"); + } // string / string -> ERROR - try {Variant v33 = v3 / v3; t.fail ("foo / foo --> error");} - catch (...) {t.pass ("foo / foo --> error");} + try { + Variant v33 = v3 / v3; + t.fail("foo / foo --> error"); + } catch (...) { + t.pass("foo / foo --> error"); + } // string / date -> ERROR - try {Variant v34 = v3 / v4; t.fail ("foo / 1234567890 --> error");} - catch (...) {t.pass ("foo / 1234567890 --> error");} + try { + Variant v34 = v3 / v4; + t.fail("foo / 1234567890 --> error"); + } catch (...) { + t.pass("foo / 1234567890 --> error"); + } // string / duration -> ERROR - try {Variant v35 = v3 / v5; t.fail ("foo / 1200 --> error");} - catch (...) {t.pass ("foo / 1200 --> error");} + try { + Variant v35 = v3 / v5; + t.fail("foo / 1200 --> error"); + } catch (...) { + t.pass("foo / 1200 --> error"); + } // date / boolean -> ERROR - try {Variant v40 = v4 / v0; t.fail ("1234567890 / true --> error");} - catch (...) {t.pass ("1234567890 / true --> error");} + try { + Variant v40 = v4 / v0; + t.fail("1234567890 / true --> error"); + } catch (...) { + t.pass("1234567890 / true --> error"); + } // date / integer -> ERROR - try {Variant v41 = v4 / v1; t.fail ("1234567890 / 42 --> error");} - catch (...) {t.pass ("1234567890 / 42 --> error");} + try { + Variant v41 = v4 / v1; + t.fail("1234567890 / 42 --> error"); + } catch (...) { + t.pass("1234567890 / 42 --> error"); + } // date / real -> ERROR - try {Variant v42 = v4 / v2; t.fail ("1234567890 / 3.14 --> error");} - catch (...) {t.pass ("1234567890 / 3.14 --> error");} + try { + Variant v42 = v4 / v2; + t.fail("1234567890 / 3.14 --> error"); + } catch (...) { + t.pass("1234567890 / 3.14 --> error"); + } // date / string -> ERROR - try {Variant v43 = v4 / v3; t.fail ("1234567890 / foo --> error");} - catch (...) {t.pass ("1234567890 / foo --> error");} + try { + Variant v43 = v4 / v3; + t.fail("1234567890 / foo --> error"); + } catch (...) { + t.pass("1234567890 / foo --> error"); + } // date / date -> ERROR - try {Variant v44 = v4 / v4; t.fail ("1234567890 / 1234567890 --> error");} - catch (...) {t.pass ("1234567890 / 1234567890 --> error");} + try { + Variant v44 = v4 / v4; + t.fail("1234567890 / 1234567890 --> error"); + } catch (...) { + t.pass("1234567890 / 1234567890 --> error"); + } // date / duration -> ERROR - try {Variant v45 = v4 / v5; t.fail ("1234567890 / 1200 --> error");} - catch (...) {t.pass ("1234567890 / 1200 --> error");} + try { + Variant v45 = v4 / v5; + t.fail("1234567890 / 1200 --> error"); + } catch (...) { + t.pass("1234567890 / 1200 --> error"); + } // duration / boolean -> ERROR - try {Variant v50 = v5 / v0; t.fail ("1200 / true --> error");} - catch (...) {t.pass ("1200 / true --> error");} + try { + Variant v50 = v5 / v0; + t.fail("1200 / true --> error"); + } catch (...) { + t.pass("1200 / true --> error"); + } // duration / integer -> duration Variant v51 = v5 / v1; - t.is (v51.type (), Variant::type_duration, "1200 / 42 --> duration"); - t.is (v51.get_duration (), 28, "1200 / 42 --> 28"); + t.is(v51.type(), Variant::type_duration, "1200 / 42 --> duration"); + t.is(v51.get_duration(), 28, "1200 / 42 --> 28"); // duration / real -> duration Variant v52 = v5 / v2; - t.is (v52.type (), Variant::type_duration, "1200 / 3.14 --> duration"); - t.is (v52.get_duration (), 382, "1200 / 3.14 --> 382"); + t.is(v52.type(), Variant::type_duration, "1200 / 3.14 --> duration"); + t.is(v52.get_duration(), 382, "1200 / 3.14 --> 382"); // duration / string -> string - try {Variant v53 = v5 / v3; t.fail ("1200 / foo --> error");} - catch (...) {t.pass ("1200 / foo --> error");} + try { + Variant v53 = v5 / v3; + t.fail("1200 / foo --> error"); + } catch (...) { + t.pass("1200 / foo --> error"); + } // duration / date -> date - try {Variant v54 = v5 / v4; t.fail ("1200 / 1234567890 --> error");} - catch (...) {t.pass ("1200 / 1234567890 --> error");} + try { + Variant v54 = v5 / v4; + t.fail("1200 / 1234567890 --> error"); + } catch (...) { + t.pass("1200 / 1234567890 --> error"); + } // duration / duration -> duration - try {Variant v55 = v5 / v5; t.fail ("1200 / 1200 --> error");} - catch (...) {t.pass ("1200 / 1200 --> error");} + try { + Variant v55 = v5 / v5; + t.fail("1200 / 1200 --> error"); + } catch (...) { + t.pass("1200 / 1200 --> error"); + } return 0; } diff --git a/test/variant_equal.test.cpp b/test/variant_equal.test.cpp index 5b8c6c530..f09f9a28d 100644 --- a/test/variant_equal.test.cpp +++ b/test/variant_equal.test.cpp @@ -27,165 +27,165 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (72); +int main(int, char**) { + UnitTest t(72); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); Variant v00 = v0 == v0; - t.is (v00.type (), Variant::type_boolean, "true == true --> boolean"); - t.is (v00.get_bool (), true, "true == true --> true"); + t.is(v00.type(), Variant::type_boolean, "true == true --> boolean"); + t.is(v00.get_bool(), true, "true == true --> true"); Variant v01 = v0 == v1; - t.is (v01.type (), Variant::type_boolean, "true == 42 --> boolean"); - t.is (v01.get_bool (), false, "true == 42 --> false"); + t.is(v01.type(), Variant::type_boolean, "true == 42 --> boolean"); + t.is(v01.get_bool(), false, "true == 42 --> false"); Variant v02 = v0 == v2; - t.is (v02.type (), Variant::type_boolean, "true == 3.14 --> boolean"); - t.is (v02.get_bool (), false, "true == 3.14 --> false"); + t.is(v02.type(), Variant::type_boolean, "true == 3.14 --> boolean"); + t.is(v02.get_bool(), false, "true == 3.14 --> false"); Variant v03 = v0 == v3; - t.is (v03.type (), Variant::type_boolean, "true == 'foo' --> boolean"); - t.is (v03.get_bool (), false, "true == 'foo' --> false"); + t.is(v03.type(), Variant::type_boolean, "true == 'foo' --> boolean"); + t.is(v03.get_bool(), false, "true == 'foo' --> false"); Variant v04 = v0 == v4; - t.is (v04.type (), Variant::type_boolean, "true == 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "true == 1234567890 --> false"); + t.is(v04.type(), Variant::type_boolean, "true == 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "true == 1234567890 --> false"); Variant v05 = v0 == v5; - t.is (v05.type (), Variant::type_boolean, "true == 1200 --> boolean"); - t.is (v05.get_bool (), false, "true == 1200 --> false"); + t.is(v05.type(), Variant::type_boolean, "true == 1200 --> boolean"); + t.is(v05.get_bool(), false, "true == 1200 --> false"); Variant v10 = v1 == v0; - t.is (v10.type (), Variant::type_boolean, "42 == true --> boolean"); - t.is (v10.get_bool (), false, "42 == true --> false"); + t.is(v10.type(), Variant::type_boolean, "42 == true --> boolean"); + t.is(v10.get_bool(), false, "42 == true --> false"); Variant v11 = v1 == v1; - t.is (v11.type (), Variant::type_boolean, "42 == 42 --> boolean"); - t.is (v11.get_bool (), true, "42 == 42 --> true"); + t.is(v11.type(), Variant::type_boolean, "42 == 42 --> boolean"); + t.is(v11.get_bool(), true, "42 == 42 --> true"); Variant v12 = v1 == v2; - t.is (v12.type (), Variant::type_boolean, "42 == 3.14 --> boolean"); - t.is (v12.get_bool (), false, "42 == 3.14 --> false"); + t.is(v12.type(), Variant::type_boolean, "42 == 3.14 --> boolean"); + t.is(v12.get_bool(), false, "42 == 3.14 --> false"); Variant v13 = v1 == v3; - t.is (v13.type (), Variant::type_boolean, "42 == 'foo' --> boolean"); - t.is (v13.get_bool (), false, "42 == 'foo' --> false"); + t.is(v13.type(), Variant::type_boolean, "42 == 'foo' --> boolean"); + t.is(v13.get_bool(), false, "42 == 'foo' --> false"); Variant v14 = v1 == v4; - t.is (v04.type (), Variant::type_boolean, "42 == 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "42 == 1234567890 --> false"); + t.is(v04.type(), Variant::type_boolean, "42 == 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "42 == 1234567890 --> false"); Variant v15 = v1 == v5; - t.is (v15.type (), Variant::type_boolean, "42 == 1200 --> boolean"); - t.is (v15.get_bool (), false, "42 == 1200 --> false"); + t.is(v15.type(), Variant::type_boolean, "42 == 1200 --> boolean"); + t.is(v15.get_bool(), false, "42 == 1200 --> false"); Variant v20 = v2 == v0; - t.is (v20.type (), Variant::type_boolean, "3.14 == true --> boolean"); - t.is (v20.get_bool (), false, "3.14 == true --> false"); + t.is(v20.type(), Variant::type_boolean, "3.14 == true --> boolean"); + t.is(v20.get_bool(), false, "3.14 == true --> false"); Variant v21 = v2 == v1; - t.is (v21.type (), Variant::type_boolean, "3.14 == 42 --> boolean"); - t.is (v21.get_bool (), false, "3.14 == 42 --> false"); + t.is(v21.type(), Variant::type_boolean, "3.14 == 42 --> boolean"); + t.is(v21.get_bool(), false, "3.14 == 42 --> false"); Variant v22 = v2 == v2; - t.is (v22.type (), Variant::type_boolean, "3.14 == 3.14 --> boolean"); - t.is (v22.get_bool (), true, "3.14 == 3.14 --> true"); + t.is(v22.type(), Variant::type_boolean, "3.14 == 3.14 --> boolean"); + t.is(v22.get_bool(), true, "3.14 == 3.14 --> true"); Variant v23 = v2 == v3; - t.is (v23.type (), Variant::type_boolean, "3.14 == 'foo' --> boolean"); - t.is (v23.get_bool (), false, "3.14 == 'foo' --> false"); + t.is(v23.type(), Variant::type_boolean, "3.14 == 'foo' --> boolean"); + t.is(v23.get_bool(), false, "3.14 == 'foo' --> false"); Variant v24 = v2 == v4; - t.is (v24.type (), Variant::type_boolean, "3.14 == 1234567890 --> boolean"); - t.is (v24.get_bool (), false, "3.14 == 1234567890 --> false"); + t.is(v24.type(), Variant::type_boolean, "3.14 == 1234567890 --> boolean"); + t.is(v24.get_bool(), false, "3.14 == 1234567890 --> false"); Variant v25 = v2 == v5; - t.is (v25.type (), Variant::type_boolean, "3.14 == 1200 --> boolean"); - t.is (v25.get_bool (), false, "3.14 == 1200 --> false"); + t.is(v25.type(), Variant::type_boolean, "3.14 == 1200 --> boolean"); + t.is(v25.get_bool(), false, "3.14 == 1200 --> false"); Variant v30 = v3 == v0; - t.is (v30.type (), Variant::type_boolean, "'foo' == true --> boolean"); - t.is (v30.get_bool (), false, "'foo' == true --> false"); + t.is(v30.type(), Variant::type_boolean, "'foo' == true --> boolean"); + t.is(v30.get_bool(), false, "'foo' == true --> false"); Variant v31 = v3 == v1; - t.is (v31.type (), Variant::type_boolean, "'foo' == 42 --> boolean"); - t.is (v31.get_bool (), false, "'foo' == 42 --> false"); + t.is(v31.type(), Variant::type_boolean, "'foo' == 42 --> boolean"); + t.is(v31.get_bool(), false, "'foo' == 42 --> false"); Variant v32 = v3 == v2; - t.is (v32.type (), Variant::type_boolean, "'foo' == 3.14 --> boolean"); - t.is (v32.get_bool (), false, "'foo' == 3.14 --> false"); + t.is(v32.type(), Variant::type_boolean, "'foo' == 3.14 --> boolean"); + t.is(v32.get_bool(), false, "'foo' == 3.14 --> false"); Variant v33 = v3 == v3; - t.is (v33.type (), Variant::type_boolean, "'foo' == 'foo' --> boolean"); - t.is (v33.get_bool (), true, "'foo' == 'foo' --> true"); + t.is(v33.type(), Variant::type_boolean, "'foo' == 'foo' --> boolean"); + t.is(v33.get_bool(), true, "'foo' == 'foo' --> true"); Variant v34 = v3 == v4; - t.is (v34.type (), Variant::type_boolean, "'foo' == 1234567890 --> boolean"); - t.is (v34.get_bool (), false, "'foo' == 1234567890 --> false"); + t.is(v34.type(), Variant::type_boolean, "'foo' == 1234567890 --> boolean"); + t.is(v34.get_bool(), false, "'foo' == 1234567890 --> false"); Variant v35 = v3 == v5; - t.is (v35.type (), Variant::type_boolean, "'foo' == 1200 --> boolean"); - t.is (v35.get_bool (), false, "'foo' == 1200 --> false"); + t.is(v35.type(), Variant::type_boolean, "'foo' == 1200 --> boolean"); + t.is(v35.get_bool(), false, "'foo' == 1200 --> false"); Variant v40 = v4 == v0; - t.is (v40.type (), Variant::type_boolean, "1234567890 == true --> boolean"); - t.is (v40.get_bool (), false, "1234567890 == true --> false"); + t.is(v40.type(), Variant::type_boolean, "1234567890 == true --> boolean"); + t.is(v40.get_bool(), false, "1234567890 == true --> false"); Variant v41 = v4 == v1; - t.is (v41.type (), Variant::type_boolean, "1234567890 == 42 --> boolean"); - t.is (v41.get_bool (), false, "1234567890 == 42 --> false"); + t.is(v41.type(), Variant::type_boolean, "1234567890 == 42 --> boolean"); + t.is(v41.get_bool(), false, "1234567890 == 42 --> false"); Variant v42 = v4 == v2; - t.is (v42.type (), Variant::type_boolean, "1234567890 == 3.14 --> boolean"); - t.is (v42.get_bool (), false, "1234567890 == 3.14 --> false"); + t.is(v42.type(), Variant::type_boolean, "1234567890 == 3.14 --> boolean"); + t.is(v42.get_bool(), false, "1234567890 == 3.14 --> false"); Variant v43 = v4 == v3; - t.is (v43.type (), Variant::type_boolean, "1234567890 == 'foo' --> boolean"); - t.is (v43.get_bool (), false, "1234567890 == 'foo' --> false"); + t.is(v43.type(), Variant::type_boolean, "1234567890 == 'foo' --> boolean"); + t.is(v43.get_bool(), false, "1234567890 == 'foo' --> false"); Variant v44 = v4 == v4; - t.is (v44.type (), Variant::type_boolean, "1234567890 == 1234567890 --> boolean"); - t.is (v44.get_bool (), true, "1234567890 == 1234567890 --> true"); + t.is(v44.type(), Variant::type_boolean, "1234567890 == 1234567890 --> boolean"); + t.is(v44.get_bool(), true, "1234567890 == 1234567890 --> true"); Variant v45 = v4 == v5; - t.is (v45.type (), Variant::type_boolean, "1234567890 == 1200 --> boolean"); - t.is (v45.get_bool (), false, "1234567890 == 1200 --> false"); + t.is(v45.type(), Variant::type_boolean, "1234567890 == 1200 --> boolean"); + t.is(v45.get_bool(), false, "1234567890 == 1200 --> false"); Variant v50 = v5 == v0; - t.is (v50.type (), Variant::type_boolean, "1200 == true --> boolean"); - t.is (v50.get_bool (), false, "1200 == true --> false"); + t.is(v50.type(), Variant::type_boolean, "1200 == true --> boolean"); + t.is(v50.get_bool(), false, "1200 == true --> false"); Variant v51 = v5 == v1; - t.is (v51.type (), Variant::type_boolean, "1200 == 42 --> boolean"); - t.is (v51.get_bool (), false, "1200 == 42 --> false"); + t.is(v51.type(), Variant::type_boolean, "1200 == 42 --> boolean"); + t.is(v51.get_bool(), false, "1200 == 42 --> false"); Variant v52 = v5 == v2; - t.is (v52.type (), Variant::type_boolean, "1200 == 3.14 --> boolean"); - t.is (v52.get_bool (), false, "1200 == 3.14 --> false"); + t.is(v52.type(), Variant::type_boolean, "1200 == 3.14 --> boolean"); + t.is(v52.get_bool(), false, "1200 == 3.14 --> false"); Variant v53 = v5 == v3; - t.is (v53.type (), Variant::type_boolean, "1200 == 'foo' --> boolean"); - t.is (v53.get_bool (), false, "1200 == 'foo' --> false"); + t.is(v53.type(), Variant::type_boolean, "1200 == 'foo' --> boolean"); + t.is(v53.get_bool(), false, "1200 == 'foo' --> false"); Variant v54 = v5 == v4; - t.is (v04.type (), Variant::type_boolean, "1200 == 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "1200 == 1234567890 --> false"); + t.is(v04.type(), Variant::type_boolean, "1200 == 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "1200 == 1234567890 --> false"); Variant v55 = v5 == v5; - t.is (v55.type (), Variant::type_boolean, "1200 == 1200 --> boolean"); - t.is (v55.get_bool (), true, "1200 == 1200 --> true"); + t.is(v55.type(), Variant::type_boolean, "1200 == 1200 --> boolean"); + t.is(v55.get_bool(), true, "1200 == 1200 --> true"); return 0; } diff --git a/test/variant_exp.test.cpp b/test/variant_exp.test.cpp index ac7c1e085..20fec4fd2 100644 --- a/test/variant_exp.test.cpp +++ b/test/variant_exp.test.cpp @@ -27,167 +27,303 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (38); +int main(int, char**) { + UnitTest t(38); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // boolean ^ boolean -> ERROR - try {Variant v00 = v0 ^ v0; t.fail ("true ^ true --> error");} - catch (...) {t.pass ("true ^ true --> error");} + try { + Variant v00 = v0 ^ v0; + t.fail("true ^ true --> error"); + } catch (...) { + t.pass("true ^ true --> error"); + } // boolean ^ integer -> ERROR - try {Variant v01 = v0 ^ v1; t.fail ("true ^ 42 --> error");} - catch (...) {t.pass ("true ^ 42 --> error");} + try { + Variant v01 = v0 ^ v1; + t.fail("true ^ 42 --> error"); + } catch (...) { + t.pass("true ^ 42 --> error"); + } // boolean ^ real -> ERROR - try {Variant v02 = v0 ^ v2; t.fail ("true ^ 3.14 --> error");} - catch (...) {t.pass ("true ^ 3.14 --> error");} + try { + Variant v02 = v0 ^ v2; + t.fail("true ^ 3.14 --> error"); + } catch (...) { + t.pass("true ^ 3.14 --> error"); + } // boolean ^ string -> ERROR - try {Variant v03 = v0 ^ v3; t.fail ("true ^ foo --> error");} - catch (...) {t.pass ("true ^ foo --> error");} + try { + Variant v03 = v0 ^ v3; + t.fail("true ^ foo --> error"); + } catch (...) { + t.pass("true ^ foo --> error"); + } // boolean ^ date -> ERROR - try {Variant v04 = v0 ^ v4; t.fail ("true ^ 1234567890 --> error");} - catch (...) {t.pass ("true ^ 1234567890 --> error");} + try { + Variant v04 = v0 ^ v4; + t.fail("true ^ 1234567890 --> error"); + } catch (...) { + t.pass("true ^ 1234567890 --> error"); + } // boolean ^ duration -> ERROR - try {Variant v05 = v0 ^ v5; t.fail ("true ^ 1200 --> error");} - catch (...) {t.pass ("true ^ 1200 --> error");} + try { + Variant v05 = v0 ^ v5; + t.fail("true ^ 1200 --> error"); + } catch (...) { + t.pass("true ^ 1200 --> error"); + } // integer ^ boolean -> ERROR - try {Variant v10 = v1 ^ v0; t.fail ("42 ^ true --> error");} - catch (...) {t.pass ("42 ^ true --> error");} + try { + Variant v10 = v1 ^ v0; + t.fail("42 ^ true --> error"); + } catch (...) { + t.pass("42 ^ true --> error"); + } // integer ^ integer -> integer - Variant v11 = v1 ^ Variant (2); - t.is (v11.type (), Variant::type_integer, "42 ^ 2 --> integer"); - t.is (v11.get_integer (), 1764, "42 ^ 2 --> 1764"); + Variant v11 = v1 ^ Variant(2); + t.is(v11.type(), Variant::type_integer, "42 ^ 2 --> integer"); + t.is(v11.get_integer(), 1764, "42 ^ 2 --> 1764"); // integer ^ real -> real - try {Variant v12 = v1 ^ v2; t.fail ("42 ^ 3.14 --> error");} - catch (...) {t.pass ("42 ^ 3.14 --> error");} + try { + Variant v12 = v1 ^ v2; + t.fail("42 ^ 3.14 --> error"); + } catch (...) { + t.pass("42 ^ 3.14 --> error"); + } // integer ^ string -> ERROR - try {Variant v13 = v1 ^ v3; t.fail ("42 ^ foo --> error");} - catch (...) {t.pass ("42 ^ foo --> error");} + try { + Variant v13 = v1 ^ v3; + t.fail("42 ^ foo --> error"); + } catch (...) { + t.pass("42 ^ foo --> error"); + } // integer ^ date -> ERROR - try {Variant v14 = v1 ^ v4; t.fail ("42 ^ 1234567890 --> error");} - catch (...) {t.pass ("42 ^ 1234567890 --> error");} + try { + Variant v14 = v1 ^ v4; + t.fail("42 ^ 1234567890 --> error"); + } catch (...) { + t.pass("42 ^ 1234567890 --> error"); + } // integer ^ duration -> ERROR - try {Variant v15 = v1 ^ v5; t.fail ("42 ^ 1200 --> error");} - catch (...) {t.pass ("42 ^ 1200 --> error");} + try { + Variant v15 = v1 ^ v5; + t.fail("42 ^ 1200 --> error"); + } catch (...) { + t.pass("42 ^ 1200 --> error"); + } // real ^ boolean -> ERROR - try {Variant v20 = v2 ^ v0; t.fail ("3.14 ^ true --> error");} - catch (...) {t.pass ("3.14 ^ true --> error");} + try { + Variant v20 = v2 ^ v0; + t.fail("3.14 ^ true --> error"); + } catch (...) { + t.pass("3.14 ^ true --> error"); + } // real ^ integer -> real - Variant v21 = v2 ^ Variant (2); - t.is (v21.type (), Variant::type_real, "3.14 ^ 2 --> real"); - t.is (v21.get_real (), 9.8596, 0.001, "3.14 ^ 2 --> 9.8596"); + Variant v21 = v2 ^ Variant(2); + t.is(v21.type(), Variant::type_real, "3.14 ^ 2 --> real"); + t.is(v21.get_real(), 9.8596, 0.001, "3.14 ^ 2 --> 9.8596"); // real ^ real -> ERROR - try {Variant v22 = v2 ^ v2; t.fail ("3.14 ^ 3.14 --> error");} - catch (...) {t.pass ("3.14 ^ 3.14 --> error");} + try { + Variant v22 = v2 ^ v2; + t.fail("3.14 ^ 3.14 --> error"); + } catch (...) { + t.pass("3.14 ^ 3.14 --> error"); + } // real ^ string -> ERROR - try {Variant v23 = v2 ^ v3; t.fail ("3.14 ^ foo --> error");} - catch (...) {t.pass ("3.14 ^ foo --> error");} + try { + Variant v23 = v2 ^ v3; + t.fail("3.14 ^ foo --> error"); + } catch (...) { + t.pass("3.14 ^ foo --> error"); + } // real ^ date -> ERROR - try {Variant v24 = v2 ^ v4; t.fail ("3.14 ^ 1234567890 --> error");} - catch (...) {t.pass ("3.14 ^ 1234567890 --> error");} + try { + Variant v24 = v2 ^ v4; + t.fail("3.14 ^ 1234567890 --> error"); + } catch (...) { + t.pass("3.14 ^ 1234567890 --> error"); + } // real ^ duration -> ERROR - try {Variant v25 = v2 ^ v5; t.fail ("3.14 ^ 1200 --> error");} - catch (...) {t.pass ("3.14 ^ 1200 --> error");} + try { + Variant v25 = v2 ^ v5; + t.fail("3.14 ^ 1200 --> error"); + } catch (...) { + t.pass("3.14 ^ 1200 --> error"); + } // string ^ boolean -> ERROR - try {Variant v30 = v3 ^ v0; t.fail ("foo ^ true --> error");} - catch (...) {t.pass ("foo ^ true --> error");} + try { + Variant v30 = v3 ^ v0; + t.fail("foo ^ true --> error"); + } catch (...) { + t.pass("foo ^ true --> error"); + } // string ^ integer -> ERROR - try {Variant v31 = v3 ^ v1; t.fail ("foo ^ 42 --> error");} - catch (...) {t.pass ("foo ^ 42 --> error");} + try { + Variant v31 = v3 ^ v1; + t.fail("foo ^ 42 --> error"); + } catch (...) { + t.pass("foo ^ 42 --> error"); + } // string ^ real -> ERROR - try {Variant v32 = v3 ^ v2; t.fail ("foo ^ 3.14 --> error");} - catch (...) {t.pass ("foo ^ 3.14 --> error");} + try { + Variant v32 = v3 ^ v2; + t.fail("foo ^ 3.14 --> error"); + } catch (...) { + t.pass("foo ^ 3.14 --> error"); + } // string ^ string -> ERROR - try {Variant v33 = v3 ^ v3; t.fail ("foo ^ foo --> error");} - catch (...) {t.pass ("foo ^ foo --> error");} + try { + Variant v33 = v3 ^ v3; + t.fail("foo ^ foo --> error"); + } catch (...) { + t.pass("foo ^ foo --> error"); + } // string ^ date -> ERROR - try {Variant v34 = v3 ^ v4; t.fail ("foo ^ 1234567890 --> error");} - catch (...) {t.pass ("foo ^ 1234567890 --> error");} + try { + Variant v34 = v3 ^ v4; + t.fail("foo ^ 1234567890 --> error"); + } catch (...) { + t.pass("foo ^ 1234567890 --> error"); + } // string ^ duration -> ERROR - try {Variant v35 = v3 ^ v5; t.fail ("foo ^ 1200 --> error");} - catch (...) {t.pass ("foo ^ 1200 --> error");} + try { + Variant v35 = v3 ^ v5; + t.fail("foo ^ 1200 --> error"); + } catch (...) { + t.pass("foo ^ 1200 --> error"); + } // date ^ boolean -> ERROR - try {Variant v40 = v4 ^ v0; t.fail ("1234567890 ^ true --> error");} - catch (...) {t.pass ("1234567890 ^ true --> error");} + try { + Variant v40 = v4 ^ v0; + t.fail("1234567890 ^ true --> error"); + } catch (...) { + t.pass("1234567890 ^ true --> error"); + } // date ^ integer -> ERROR - try {Variant v41 = v4 ^ v1; t.fail ("1234567890 ^ 42 --> error");} - catch (...) {t.pass ("1234567890 ^ 42 --> error");} + try { + Variant v41 = v4 ^ v1; + t.fail("1234567890 ^ 42 --> error"); + } catch (...) { + t.pass("1234567890 ^ 42 --> error"); + } // date ^ real -> ERROR - try {Variant v42 = v4 ^ v2; t.fail ("1234567890 ^ 3.14 --> error");} - catch (...) {t.pass ("1234567890 ^ 3.14 --> error");} + try { + Variant v42 = v4 ^ v2; + t.fail("1234567890 ^ 3.14 --> error"); + } catch (...) { + t.pass("1234567890 ^ 3.14 --> error"); + } // date ^ string -> ERROR - try {Variant v43 = v4 ^ v3; t.fail ("1234567890 ^ foo --> error");} - catch (...) {t.pass ("1234567890 ^ foo --> error");} + try { + Variant v43 = v4 ^ v3; + t.fail("1234567890 ^ foo --> error"); + } catch (...) { + t.pass("1234567890 ^ foo --> error"); + } // date ^ date -> ERROR - try {Variant v44 = v4 ^ v4; t.fail ("1234567890 ^ 1234567890 --> error");} - catch (...) {t.pass ("1234567890 ^ 1234567890 --> error");} + try { + Variant v44 = v4 ^ v4; + t.fail("1234567890 ^ 1234567890 --> error"); + } catch (...) { + t.pass("1234567890 ^ 1234567890 --> error"); + } // date ^ duration -> ERROR - try {Variant v45 = v4 ^ v5; t.fail ("1234567890 ^ 1200 --> error");} - catch (...) {t.pass ("1234567890 ^ 1200 --> error");} + try { + Variant v45 = v4 ^ v5; + t.fail("1234567890 ^ 1200 --> error"); + } catch (...) { + t.pass("1234567890 ^ 1200 --> error"); + } // duration ^ boolean -> ERROR - try {Variant v50 = v5 ^ v0; t.fail ("1200 ^ true --> error");} - catch (...) {t.pass ("1200 ^ true --> error");} + try { + Variant v50 = v5 ^ v0; + t.fail("1200 ^ true --> error"); + } catch (...) { + t.pass("1200 ^ true --> error"); + } // duration ^ integer -> ERROR - try {Variant v51 = v5 ^ v1; t.fail ("1200 ^ 42 --> error");} - catch (...) {t.pass ("1200 ^ 42 --> error");} + try { + Variant v51 = v5 ^ v1; + t.fail("1200 ^ 42 --> error"); + } catch (...) { + t.pass("1200 ^ 42 --> error"); + } // duration ^ real -> ERROR - try {Variant v52 = v5 ^ v2; t.fail ("1200 ^ 3.14 --> error");} - catch (...) {t.pass ("1200 ^ 3.14 --> error");} + try { + Variant v52 = v5 ^ v2; + t.fail("1200 ^ 3.14 --> error"); + } catch (...) { + t.pass("1200 ^ 3.14 --> error"); + } // duration ^ string -> ERROR - try {Variant v53 = v5 ^ v3; t.fail ("1200 ^ foo --> error");} - catch (...) {t.pass ("1200 ^ foo --> error");} + try { + Variant v53 = v5 ^ v3; + t.fail("1200 ^ foo --> error"); + } catch (...) { + t.pass("1200 ^ foo --> error"); + } // duration ^ date -> ERROR - try {Variant v54 = v5 ^ v4; t.fail ("1200 ^ 1234567890 --> error");} - catch (...) {t.pass ("1200 ^ 1234567890 --> error");} + try { + Variant v54 = v5 ^ v4; + t.fail("1200 ^ 1234567890 --> error"); + } catch (...) { + t.pass("1200 ^ 1234567890 --> error"); + } // duration ^ duration -> ERROR - try {Variant v55 = v5 ^ v5; t.fail ("1200 ^ 1200 --> error");} - catch (...) {t.pass ("1200 ^ 1200 --> error");} + try { + Variant v55 = v5 ^ v5; + t.fail("1200 ^ 1200 --> error"); + } catch (...) { + t.pass("1200 ^ 1200 --> error"); + } return 0; } diff --git a/test/variant_gt.test.cpp b/test/variant_gt.test.cpp index 86d2b6317..a35f3cef4 100644 --- a/test/variant_gt.test.cpp +++ b/test/variant_gt.test.cpp @@ -27,168 +27,168 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (72); +int main(int, char**) { + UnitTest t(72); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); Variant v00 = v0 > v0; - t.is (v00.type (), Variant::type_boolean, "true > true --> boolean"); - t.is (v00.get_bool (), false, "true > true --> false"); + t.is(v00.type(), Variant::type_boolean, "true > true --> boolean"); + t.is(v00.get_bool(), false, "true > true --> false"); Variant v01 = v0 > v1; - t.is (v01.type (), Variant::type_boolean, "true > 42 --> boolean"); - t.is (v01.get_bool (), false, "true > 42 --> false"); + t.is(v01.type(), Variant::type_boolean, "true > 42 --> boolean"); + t.is(v01.get_bool(), false, "true > 42 --> false"); Variant v02 = v0 > v2; - t.is (v02.type (), Variant::type_boolean, "true > 3.14 --> boolean"); - t.is (v02.get_bool (), false, "true > 3.14 --> false"); + t.is(v02.type(), Variant::type_boolean, "true > 3.14 --> boolean"); + t.is(v02.get_bool(), false, "true > 3.14 --> false"); Variant v03 = v0 > v3; - t.is (v03.type (), Variant::type_boolean, "true > 'foo' --> boolean"); - t.is (v03.get_bool (), true, "true > 'foo' --> true"); + t.is(v03.type(), Variant::type_boolean, "true > 'foo' --> boolean"); + t.is(v03.get_bool(), true, "true > 'foo' --> true"); Variant v04 = v0 > v4; - t.is (v04.type (), Variant::type_boolean, "true > 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "true > 1234567890 --> false"); + t.is(v04.type(), Variant::type_boolean, "true > 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "true > 1234567890 --> false"); Variant v05 = v0 > v5; - t.is (v05.type (), Variant::type_boolean, "true > 1200 --> boolean"); - t.is (v05.get_bool (), false, "true > 1200 --> false"); + t.is(v05.type(), Variant::type_boolean, "true > 1200 --> boolean"); + t.is(v05.get_bool(), false, "true > 1200 --> false"); Variant v10 = v1 > v0; - t.is (v10.type (), Variant::type_boolean, "42 > true --> boolean"); - t.is (v10.get_bool (), true, "42 > true --> true"); + t.is(v10.type(), Variant::type_boolean, "42 > true --> boolean"); + t.is(v10.get_bool(), true, "42 > true --> true"); Variant v11 = v1 > v1; - t.is (v11.type (), Variant::type_boolean, "42 > 42 --> boolean"); - t.is (v11.get_bool (), false, "42 > 42 --> false"); + t.is(v11.type(), Variant::type_boolean, "42 > 42 --> boolean"); + t.is(v11.get_bool(), false, "42 > 42 --> false"); Variant v12 = v1 > v2; - t.is (v12.type (), Variant::type_boolean, "42 > 3.14 --> boolean"); - t.is (v12.get_bool (), true, "42 > 3.14 --> true"); + t.is(v12.type(), Variant::type_boolean, "42 > 3.14 --> boolean"); + t.is(v12.get_bool(), true, "42 > 3.14 --> true"); Variant v13 = v1 > v3; - t.is (v13.type (), Variant::type_boolean, "42 > 'foo' --> boolean"); - t.is (v13.get_bool (), false, "42 > 'foo' --> false"); + t.is(v13.type(), Variant::type_boolean, "42 > 'foo' --> boolean"); + t.is(v13.get_bool(), false, "42 > 'foo' --> false"); Variant v14 = v1 > v4; - t.is (v04.type (), Variant::type_boolean, "42 > 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "42 > 1234567890 --> false"); + t.is(v04.type(), Variant::type_boolean, "42 > 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "42 > 1234567890 --> false"); Variant v15 = v1 > v5; - t.is (v15.type (), Variant::type_boolean, "42 > 1200 --> boolean"); - t.is (v15.get_bool (), false, "42 > 1200 --> false"); + t.is(v15.type(), Variant::type_boolean, "42 > 1200 --> boolean"); + t.is(v15.get_bool(), false, "42 > 1200 --> false"); Variant v20 = v2 > v0; - t.is (v20.type (), Variant::type_boolean, "3.14 > true --> boolean"); - t.is (v20.get_bool (), true, "3.14 > true --> true"); + t.is(v20.type(), Variant::type_boolean, "3.14 > true --> boolean"); + t.is(v20.get_bool(), true, "3.14 > true --> true"); Variant v21 = v2 > v1; - t.is (v21.type (), Variant::type_boolean, "3.14 > 42 --> boolean"); - t.is (v21.get_bool (), false, "3.14 > 42 --> false"); + t.is(v21.type(), Variant::type_boolean, "3.14 > 42 --> boolean"); + t.is(v21.get_bool(), false, "3.14 > 42 --> false"); Variant v22 = v2 > v2; - t.is (v22.type (), Variant::type_boolean, "3.14 > 3.14 --> boolean"); - t.is (v22.get_bool (), false, "3.14 > 3.14 --> false"); + t.is(v22.type(), Variant::type_boolean, "3.14 > 3.14 --> boolean"); + t.is(v22.get_bool(), false, "3.14 > 3.14 --> false"); Variant v23 = v2 > v3; - t.is (v23.type (), Variant::type_boolean, "3.14 > 'foo' --> boolean"); - t.is (v23.get_bool (), false, "3.14 > 'foo' --> false"); + t.is(v23.type(), Variant::type_boolean, "3.14 > 'foo' --> boolean"); + t.is(v23.get_bool(), false, "3.14 > 'foo' --> false"); Variant v24 = v2 > v4; - t.is (v24.type (), Variant::type_boolean, "3.14 > 1234567890 --> boolean"); - t.is (v24.get_bool (), false, "3.14 > 1234567890 --> false"); + t.is(v24.type(), Variant::type_boolean, "3.14 > 1234567890 --> boolean"); + t.is(v24.get_bool(), false, "3.14 > 1234567890 --> false"); Variant v25 = v2 > v5; - t.is (v25.type (), Variant::type_boolean, "3.14 > 1200 --> boolean"); - t.is (v25.get_bool (), false, "3.14 > 1200 --> false"); + t.is(v25.type(), Variant::type_boolean, "3.14 > 1200 --> boolean"); + t.is(v25.get_bool(), false, "3.14 > 1200 --> false"); Variant v30 = v3 > v0; - t.is (v30.type (), Variant::type_boolean, "'foo' > true --> boolean"); - t.is (v30.get_bool (), false, "'foo' > true --> false"); + t.is(v30.type(), Variant::type_boolean, "'foo' > true --> boolean"); + t.is(v30.get_bool(), false, "'foo' > true --> false"); Variant v31 = v3 > v1; - t.is (v31.type (), Variant::type_boolean, "'foo' > 42 --> boolean"); - t.is (v31.get_bool (), true, "'foo' > 42 --> true"); + t.is(v31.type(), Variant::type_boolean, "'foo' > 42 --> boolean"); + t.is(v31.get_bool(), true, "'foo' > 42 --> true"); Variant v32 = v3 > v2; - t.is (v32.type (), Variant::type_boolean, "'foo' > 3.14 --> boolean"); - t.is (v32.get_bool (), true, "'foo' > 3.14 --> true"); + t.is(v32.type(), Variant::type_boolean, "'foo' > 3.14 --> boolean"); + t.is(v32.get_bool(), true, "'foo' > 3.14 --> true"); Variant v33 = v3 > v3; - t.is (v33.type (), Variant::type_boolean, "'foo' > 'foo' --> boolean"); - t.is (v33.get_bool (), false, "'foo' > 'foo' --> false"); + t.is(v33.type(), Variant::type_boolean, "'foo' > 'foo' --> boolean"); + t.is(v33.get_bool(), false, "'foo' > 'foo' --> false"); Variant v34 = v3 > v4; - t.is (v34.type (), Variant::type_boolean, "'foo' > 1234567890 --> boolean"); - t.is (v34.get_bool (), false, "'foo' > 1234567890 --> false"); + t.is(v34.type(), Variant::type_boolean, "'foo' > 1234567890 --> boolean"); + t.is(v34.get_bool(), false, "'foo' > 1234567890 --> false"); Variant v35 = v3 > v5; - t.is (v35.type (), Variant::type_boolean, "'foo' > 1200 --> boolean"); - t.is (v35.get_bool (), false, "'foo' > 1200 --> false"); + t.is(v35.type(), Variant::type_boolean, "'foo' > 1200 --> boolean"); + t.is(v35.get_bool(), false, "'foo' > 1200 --> false"); Variant v40 = v4 > v0; - t.is (v40.type (), Variant::type_boolean, "1234567890 > true --> boolean"); - t.is (v40.get_bool (), true, "1234567890 > true --> true"); + t.is(v40.type(), Variant::type_boolean, "1234567890 > true --> boolean"); + t.is(v40.get_bool(), true, "1234567890 > true --> true"); Variant v41 = v4 > v1; - t.is (v41.type (), Variant::type_boolean, "1234567890 > 42 --> boolean"); - t.is (v41.get_bool (), true, "1234567890 > 42 --> true"); + t.is(v41.type(), Variant::type_boolean, "1234567890 > 42 --> boolean"); + t.is(v41.get_bool(), true, "1234567890 > 42 --> true"); Variant v42 = v4 > v2; - t.is (v42.type (), Variant::type_boolean, "1234567890 > 3.14 --> boolean"); - t.is (v42.get_bool (), true, "1234567890 > 3.14 --> true"); + t.is(v42.type(), Variant::type_boolean, "1234567890 > 3.14 --> boolean"); + t.is(v42.get_bool(), true, "1234567890 > 3.14 --> true"); Variant v43 = v4 > v3; - t.is (v43.type (), Variant::type_boolean, "1234567890 > 'foo' --> boolean"); - t.is (v43.get_bool (), true, "1234567890 > 'foo' --> true"); + t.is(v43.type(), Variant::type_boolean, "1234567890 > 'foo' --> boolean"); + t.is(v43.get_bool(), true, "1234567890 > 'foo' --> true"); Variant v44 = v4 > v4; - t.is (v44.type (), Variant::type_boolean, "1234567890 > 1234567890 --> boolean"); - t.is (v44.get_bool (), false, "1234567890 > 1234567890 --> false"); + t.is(v44.type(), Variant::type_boolean, "1234567890 > 1234567890 --> boolean"); + t.is(v44.get_bool(), false, "1234567890 > 1234567890 --> false"); Variant v45 = v4 > v5; // 1234567890 corresponds to Fri Feb 13 06:31:30 PM EST 2009 hence 1200 // (evaluated as now+1200s) be in future for any date after 2009-02-13 - t.is (v45.type (), Variant::type_boolean, "1234567890 > 1200 --> boolean"); - t.is (v45.get_bool (), false, "1234567890 > 1200 --> false"); + t.is(v45.type(), Variant::type_boolean, "1234567890 > 1200 --> boolean"); + t.is(v45.get_bool(), false, "1234567890 > 1200 --> false"); Variant v50 = v5 > v0; - t.is (v50.type (), Variant::type_boolean, "1200 > true --> boolean"); - t.is (v50.get_bool (), true, "1200 > true --> true"); + t.is(v50.type(), Variant::type_boolean, "1200 > true --> boolean"); + t.is(v50.get_bool(), true, "1200 > true --> true"); Variant v51 = v5 > v1; - t.is (v51.type (), Variant::type_boolean, "1200 > 42 --> boolean"); - t.is (v51.get_bool (), true, "1200 > 42 --> true"); + t.is(v51.type(), Variant::type_boolean, "1200 > 42 --> boolean"); + t.is(v51.get_bool(), true, "1200 > 42 --> true"); Variant v52 = v5 > v2; - t.is (v52.type (), Variant::type_boolean, "1200 > 3.14 --> boolean"); - t.is (v52.get_bool (), true, "1200 > 3.14 --> true"); + t.is(v52.type(), Variant::type_boolean, "1200 > 3.14 --> boolean"); + t.is(v52.get_bool(), true, "1200 > 3.14 --> true"); Variant v53 = v5 > v3; - t.is (v53.type (), Variant::type_boolean, "1200 > 'foo' --> boolean"); - t.is (v53.get_bool (), true, "1200 > 'foo' --> true"); + t.is(v53.type(), Variant::type_boolean, "1200 > 'foo' --> boolean"); + t.is(v53.get_bool(), true, "1200 > 'foo' --> true"); Variant v54 = v5 > v4; // Same reasoning as v45 - t.is (v54.type (), Variant::type_boolean, "1200 > 1234567890 --> boolean"); - t.is (v54.get_bool (), true, "1200 > 1234567890 --> true"); + t.is(v54.type(), Variant::type_boolean, "1200 > 1234567890 --> boolean"); + t.is(v54.get_bool(), true, "1200 > 1234567890 --> true"); Variant v55 = v5 > v5; - t.is (v55.type (), Variant::type_boolean, "1200 > 1200 --> boolean"); - t.is (v55.get_bool (), false, "1200 > 1200 --> false"); + t.is(v55.type(), Variant::type_boolean, "1200 > 1200 --> boolean"); + t.is(v55.get_bool(), false, "1200 > 1200 --> false"); return 0; } diff --git a/test/variant_gte.test.cpp b/test/variant_gte.test.cpp index 708a598e1..6da52047e 100644 --- a/test/variant_gte.test.cpp +++ b/test/variant_gte.test.cpp @@ -27,168 +27,168 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (72); +int main(int, char**) { + UnitTest t(72); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); Variant v00 = v0 >= v0; - t.is (v00.type (), Variant::type_boolean, "true >= true --> boolean"); - t.is (v00.get_bool (), true, "true >= true --> true"); + t.is(v00.type(), Variant::type_boolean, "true >= true --> boolean"); + t.is(v00.get_bool(), true, "true >= true --> true"); Variant v01 = v0 >= v1; - t.is (v01.type (), Variant::type_boolean, "true >= 42 --> boolean"); - t.is (v01.get_bool (), false, "true >= 42 --> false"); + t.is(v01.type(), Variant::type_boolean, "true >= 42 --> boolean"); + t.is(v01.get_bool(), false, "true >= 42 --> false"); Variant v02 = v0 >= v2; - t.is (v02.type (), Variant::type_boolean, "true >= 3.14 --> boolean"); - t.is (v02.get_bool (), false, "true >= 3.14 --> false"); + t.is(v02.type(), Variant::type_boolean, "true >= 3.14 --> boolean"); + t.is(v02.get_bool(), false, "true >= 3.14 --> false"); Variant v03 = v0 >= v3; - t.is (v03.type (), Variant::type_boolean, "true >= 'foo' --> boolean"); - t.is (v03.get_bool (), true, "true >= 'foo' --> true"); + t.is(v03.type(), Variant::type_boolean, "true >= 'foo' --> boolean"); + t.is(v03.get_bool(), true, "true >= 'foo' --> true"); Variant v04 = v0 >= v4; - t.is (v04.type (), Variant::type_boolean, "true >= 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "true >= 1234567890 --> false"); + t.is(v04.type(), Variant::type_boolean, "true >= 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "true >= 1234567890 --> false"); Variant v05 = v0 >= v5; - t.is (v05.type (), Variant::type_boolean, "true >= 1200 --> boolean"); - t.is (v05.get_bool (), false, "true >= 1200 --> false"); + t.is(v05.type(), Variant::type_boolean, "true >= 1200 --> boolean"); + t.is(v05.get_bool(), false, "true >= 1200 --> false"); Variant v10 = v1 >= v0; - t.is (v10.type (), Variant::type_boolean, "42 >= true --> boolean"); - t.is (v10.get_bool (), true, "42 >= true --> true"); + t.is(v10.type(), Variant::type_boolean, "42 >= true --> boolean"); + t.is(v10.get_bool(), true, "42 >= true --> true"); Variant v11 = v1 >= v1; - t.is (v11.type (), Variant::type_boolean, "42 >= 42 --> boolean"); - t.is (v11.get_bool (), true, "42 >= 42 --> true"); + t.is(v11.type(), Variant::type_boolean, "42 >= 42 --> boolean"); + t.is(v11.get_bool(), true, "42 >= 42 --> true"); Variant v12 = v1 >= v2; - t.is (v12.type (), Variant::type_boolean, "42 >= 3.14 --> boolean"); - t.is (v12.get_bool (), true, "42 >= 3.14 --> true"); + t.is(v12.type(), Variant::type_boolean, "42 >= 3.14 --> boolean"); + t.is(v12.get_bool(), true, "42 >= 3.14 --> true"); Variant v13 = v1 >= v3; - t.is (v13.type (), Variant::type_boolean, "42 >= 'foo' --> boolean"); - t.is (v13.get_bool (), false, "42 >= 'foo' --> false"); + t.is(v13.type(), Variant::type_boolean, "42 >= 'foo' --> boolean"); + t.is(v13.get_bool(), false, "42 >= 'foo' --> false"); Variant v14 = v1 >= v4; - t.is (v04.type (), Variant::type_boolean, "42 >= 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "42 >= 1234567890 --> false"); + t.is(v04.type(), Variant::type_boolean, "42 >= 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "42 >= 1234567890 --> false"); Variant v15 = v1 >= v5; - t.is (v15.type (), Variant::type_boolean, "42 >= 1200 --> boolean"); - t.is (v15.get_bool (), false, "42 >= 1200 --> false"); + t.is(v15.type(), Variant::type_boolean, "42 >= 1200 --> boolean"); + t.is(v15.get_bool(), false, "42 >= 1200 --> false"); Variant v20 = v2 >= v0; - t.is (v20.type (), Variant::type_boolean, "3.14 >= true --> boolean"); - t.is (v20.get_bool (), true, "3.14 >= true --> true"); + t.is(v20.type(), Variant::type_boolean, "3.14 >= true --> boolean"); + t.is(v20.get_bool(), true, "3.14 >= true --> true"); Variant v21 = v2 >= v1; - t.is (v21.type (), Variant::type_boolean, "3.14 >= 42 --> boolean"); - t.is (v21.get_bool (), false, "3.14 >= 42 --> false"); + t.is(v21.type(), Variant::type_boolean, "3.14 >= 42 --> boolean"); + t.is(v21.get_bool(), false, "3.14 >= 42 --> false"); Variant v22 = v2 >= v2; - t.is (v22.type (), Variant::type_boolean, "3.14 >= 3.14 --> boolean"); - t.is (v22.get_bool (), true, "3.14 >= 3.14 --> true"); + t.is(v22.type(), Variant::type_boolean, "3.14 >= 3.14 --> boolean"); + t.is(v22.get_bool(), true, "3.14 >= 3.14 --> true"); Variant v23 = v2 >= v3; - t.is (v23.type (), Variant::type_boolean, "3.14 >= 'foo' --> boolean"); - t.is (v23.get_bool (), false, "3.14 >= 'foo' --> false"); + t.is(v23.type(), Variant::type_boolean, "3.14 >= 'foo' --> boolean"); + t.is(v23.get_bool(), false, "3.14 >= 'foo' --> false"); Variant v24 = v2 >= v4; - t.is (v24.type (), Variant::type_boolean, "3.14 >= 1234567890 --> boolean"); - t.is (v24.get_bool (), false, "3.14 >= 1234567890 --> false"); + t.is(v24.type(), Variant::type_boolean, "3.14 >= 1234567890 --> boolean"); + t.is(v24.get_bool(), false, "3.14 >= 1234567890 --> false"); Variant v25 = v2 >= v5; - t.is (v25.type (), Variant::type_boolean, "3.14 >= 1200 --> boolean"); - t.is (v25.get_bool (), false, "3.14 >= 1200 --> false"); + t.is(v25.type(), Variant::type_boolean, "3.14 >= 1200 --> boolean"); + t.is(v25.get_bool(), false, "3.14 >= 1200 --> false"); Variant v30 = v3 >= v0; - t.is (v30.type (), Variant::type_boolean, "'foo' >= true --> boolean"); - t.is (v30.get_bool (), false, "'foo' >= true --> false"); + t.is(v30.type(), Variant::type_boolean, "'foo' >= true --> boolean"); + t.is(v30.get_bool(), false, "'foo' >= true --> false"); Variant v31 = v3 >= v1; - t.is (v31.type (), Variant::type_boolean, "'foo' >= 42 --> boolean"); - t.is (v31.get_bool (), true, "'foo' >= 42 --> true"); + t.is(v31.type(), Variant::type_boolean, "'foo' >= 42 --> boolean"); + t.is(v31.get_bool(), true, "'foo' >= 42 --> true"); Variant v32 = v3 >= v2; - t.is (v32.type (), Variant::type_boolean, "'foo' >= 3.14 --> boolean"); - t.is (v32.get_bool (), true, "'foo' >= 3.14 --> true"); + t.is(v32.type(), Variant::type_boolean, "'foo' >= 3.14 --> boolean"); + t.is(v32.get_bool(), true, "'foo' >= 3.14 --> true"); Variant v33 = v3 >= v3; - t.is (v33.type (), Variant::type_boolean, "'foo' >= 'foo' --> boolean"); - t.is (v33.get_bool (), true, "'foo' >= 'foo' --> true"); + t.is(v33.type(), Variant::type_boolean, "'foo' >= 'foo' --> boolean"); + t.is(v33.get_bool(), true, "'foo' >= 'foo' --> true"); Variant v34 = v3 >= v4; - t.is (v34.type (), Variant::type_boolean, "'foo' >= 1234567890 --> boolean"); - t.is (v34.get_bool (), false, "'foo' >= 1234567890 --> false"); + t.is(v34.type(), Variant::type_boolean, "'foo' >= 1234567890 --> boolean"); + t.is(v34.get_bool(), false, "'foo' >= 1234567890 --> false"); Variant v35 = v3 >= v5; - t.is (v35.type (), Variant::type_boolean, "'foo' >= 1200 --> boolean"); - t.is (v35.get_bool (), false, "'foo' >= 1200 --> false"); + t.is(v35.type(), Variant::type_boolean, "'foo' >= 1200 --> boolean"); + t.is(v35.get_bool(), false, "'foo' >= 1200 --> false"); Variant v40 = v4 >= v0; - t.is (v40.type (), Variant::type_boolean, "1234567890 >= true --> boolean"); - t.is (v40.get_bool (), true, "1234567890 >= true --> true"); + t.is(v40.type(), Variant::type_boolean, "1234567890 >= true --> boolean"); + t.is(v40.get_bool(), true, "1234567890 >= true --> true"); Variant v41 = v4 >= v1; - t.is (v41.type (), Variant::type_boolean, "1234567890 >= 42 --> boolean"); - t.is (v41.get_bool (), true, "1234567890 >= 42 --> true"); + t.is(v41.type(), Variant::type_boolean, "1234567890 >= 42 --> boolean"); + t.is(v41.get_bool(), true, "1234567890 >= 42 --> true"); Variant v42 = v4 >= v2; - t.is (v42.type (), Variant::type_boolean, "1234567890 >= 3.14 --> boolean"); - t.is (v42.get_bool (), true, "1234567890 >= 3.14 --> true"); + t.is(v42.type(), Variant::type_boolean, "1234567890 >= 3.14 --> boolean"); + t.is(v42.get_bool(), true, "1234567890 >= 3.14 --> true"); Variant v43 = v4 >= v3; - t.is (v43.type (), Variant::type_boolean, "1234567890 >= 'foo' --> boolean"); - t.is (v43.get_bool (), true, "1234567890 >= 'foo' --> true"); + t.is(v43.type(), Variant::type_boolean, "1234567890 >= 'foo' --> boolean"); + t.is(v43.get_bool(), true, "1234567890 >= 'foo' --> true"); Variant v44 = v4 >= v4; - t.is (v44.type (), Variant::type_boolean, "1234567890 >= 1234567890 --> boolean"); - t.is (v44.get_bool (), true, "1234567890 >= 1234567890 --> true"); + t.is(v44.type(), Variant::type_boolean, "1234567890 >= 1234567890 --> boolean"); + t.is(v44.get_bool(), true, "1234567890 >= 1234567890 --> true"); Variant v45 = v4 >= v5; // 1234567890 corresponds to Fri Feb 13 06:31:30 PM EST 2009 hence 1200 // (evaluated as now+1200s) be in future for any date after 2009-02-13 - t.is (v45.type (), Variant::type_boolean, "1234567890 >= 1200 --> boolean"); - t.is (v45.get_bool (), false, "1234567890 >= 1200 --> false"); + t.is(v45.type(), Variant::type_boolean, "1234567890 >= 1200 --> boolean"); + t.is(v45.get_bool(), false, "1234567890 >= 1200 --> false"); Variant v50 = v5 >= v0; - t.is (v50.type (), Variant::type_boolean, "1200 >= true --> boolean"); - t.is (v50.get_bool (), true, "1200 >= true --> true"); + t.is(v50.type(), Variant::type_boolean, "1200 >= true --> boolean"); + t.is(v50.get_bool(), true, "1200 >= true --> true"); Variant v51 = v5 >= v1; - t.is (v51.type (), Variant::type_boolean, "1200 >= 42 --> boolean"); - t.is (v51.get_bool (), true, "1200 >= 42 --> true"); + t.is(v51.type(), Variant::type_boolean, "1200 >= 42 --> boolean"); + t.is(v51.get_bool(), true, "1200 >= 42 --> true"); Variant v52 = v5 >= v2; - t.is (v52.type (), Variant::type_boolean, "1200 >= 3.14 --> boolean"); - t.is (v52.get_bool (), true, "1200 >= 3.14 --> true"); + t.is(v52.type(), Variant::type_boolean, "1200 >= 3.14 --> boolean"); + t.is(v52.get_bool(), true, "1200 >= 3.14 --> true"); Variant v53 = v5 >= v3; - t.is (v53.type (), Variant::type_boolean, "1200 >= 'foo' --> boolean"); - t.is (v53.get_bool (), true, "1200 >= 'foo' --> true"); + t.is(v53.type(), Variant::type_boolean, "1200 >= 'foo' --> boolean"); + t.is(v53.get_bool(), true, "1200 >= 'foo' --> true"); Variant v54 = v5 >= v4; // Same reasoning as v45 - t.is (v54.type (), Variant::type_boolean, "1200 >= 1234567890 --> boolean"); - t.is (v54.get_bool (), true, "1200 >= 1234567890 --> true"); + t.is(v54.type(), Variant::type_boolean, "1200 >= 1234567890 --> boolean"); + t.is(v54.get_bool(), true, "1200 >= 1234567890 --> true"); Variant v55 = v5 >= v5; - t.is (v55.type (), Variant::type_boolean, "1200 >= 1200 --> boolean"); - t.is (v55.get_bool (), true, "1200 >= 1200 --> true"); + t.is(v55.type(), Variant::type_boolean, "1200 >= 1200 --> boolean"); + t.is(v55.get_bool(), true, "1200 >= 1200 --> true"); return 0; } diff --git a/test/variant_inequal.test.cpp b/test/variant_inequal.test.cpp index 348b7b9a7..eb1dc0b20 100644 --- a/test/variant_inequal.test.cpp +++ b/test/variant_inequal.test.cpp @@ -27,165 +27,165 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (72); +int main(int, char**) { + UnitTest t(72); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); Variant v00 = v0 != v0; - t.is (v00.type (), Variant::type_boolean, "true != true --> boolean"); - t.is (v00.get_bool (), false, "true != true --> false"); + t.is(v00.type(), Variant::type_boolean, "true != true --> boolean"); + t.is(v00.get_bool(), false, "true != true --> false"); Variant v01 = v0 != v1; - t.is (v01.type (), Variant::type_boolean, "true != 42 --> boolean"); - t.is (v01.get_bool (), true, "true != 42 --> true"); + t.is(v01.type(), Variant::type_boolean, "true != 42 --> boolean"); + t.is(v01.get_bool(), true, "true != 42 --> true"); Variant v02 = v0 != v2; - t.is (v02.type (), Variant::type_boolean, "true != 3.14 --> boolean"); - t.is (v02.get_bool (), true, "true != 3.14 --> true"); + t.is(v02.type(), Variant::type_boolean, "true != 3.14 --> boolean"); + t.is(v02.get_bool(), true, "true != 3.14 --> true"); Variant v03 = v0 != v3; - t.is (v03.type (), Variant::type_boolean, "true != 'foo' --> boolean"); - t.is (v03.get_bool (), true, "true != 'foo' --> true"); + t.is(v03.type(), Variant::type_boolean, "true != 'foo' --> boolean"); + t.is(v03.get_bool(), true, "true != 'foo' --> true"); Variant v04 = v0 != v4; - t.is (v04.type (), Variant::type_boolean, "true != 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "true != 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "true != 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "true != 1234567890 --> true"); Variant v05 = v0 != v5; - t.is (v05.type (), Variant::type_boolean, "true != 1200 --> boolean"); - t.is (v05.get_bool (), true, "true != 1200 --> true"); + t.is(v05.type(), Variant::type_boolean, "true != 1200 --> boolean"); + t.is(v05.get_bool(), true, "true != 1200 --> true"); Variant v10 = v1 != v0; - t.is (v10.type (), Variant::type_boolean, "42 != true --> boolean"); - t.is (v10.get_bool (), true, "42 != true --> true"); + t.is(v10.type(), Variant::type_boolean, "42 != true --> boolean"); + t.is(v10.get_bool(), true, "42 != true --> true"); Variant v11 = v1 != v1; - t.is (v11.type (), Variant::type_boolean, "42 != 42 --> boolean"); - t.is (v11.get_bool (), false, "42 != 42 --> false"); + t.is(v11.type(), Variant::type_boolean, "42 != 42 --> boolean"); + t.is(v11.get_bool(), false, "42 != 42 --> false"); Variant v12 = v1 != v2; - t.is (v12.type (), Variant::type_boolean, "42 != 3.14 --> boolean"); - t.is (v12.get_bool (), true, "42 != 3.14 --> true"); + t.is(v12.type(), Variant::type_boolean, "42 != 3.14 --> boolean"); + t.is(v12.get_bool(), true, "42 != 3.14 --> true"); Variant v13 = v1 != v3; - t.is (v13.type (), Variant::type_boolean, "42 != 'foo' --> boolean"); - t.is (v13.get_bool (), true, "42 != 'foo' --> true"); + t.is(v13.type(), Variant::type_boolean, "42 != 'foo' --> boolean"); + t.is(v13.get_bool(), true, "42 != 'foo' --> true"); Variant v14 = v1 != v4; - t.is (v04.type (), Variant::type_boolean, "42 != 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "42 != 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "42 != 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "42 != 1234567890 --> true"); Variant v15 = v1 != v5; - t.is (v15.type (), Variant::type_boolean, "42 != 1200 --> boolean"); - t.is (v15.get_bool (), true, "42 != 1200 --> true"); + t.is(v15.type(), Variant::type_boolean, "42 != 1200 --> boolean"); + t.is(v15.get_bool(), true, "42 != 1200 --> true"); Variant v20 = v2 != v0; - t.is (v20.type (), Variant::type_boolean, "3.14 != true --> boolean"); - t.is (v20.get_bool (), true, "3.14 != true --> true"); + t.is(v20.type(), Variant::type_boolean, "3.14 != true --> boolean"); + t.is(v20.get_bool(), true, "3.14 != true --> true"); Variant v21 = v2 != v1; - t.is (v21.type (), Variant::type_boolean, "3.14 != 42 --> boolean"); - t.is (v21.get_bool (), true, "3.14 != 42 --> true"); + t.is(v21.type(), Variant::type_boolean, "3.14 != 42 --> boolean"); + t.is(v21.get_bool(), true, "3.14 != 42 --> true"); Variant v22 = v2 != v2; - t.is (v22.type (), Variant::type_boolean, "3.14 != 3.14 --> boolean"); - t.is (v22.get_bool (), false, "3.14 != 3.14 --> false"); + t.is(v22.type(), Variant::type_boolean, "3.14 != 3.14 --> boolean"); + t.is(v22.get_bool(), false, "3.14 != 3.14 --> false"); Variant v23 = v2 != v3; - t.is (v23.type (), Variant::type_boolean, "3.14 != 'foo' --> boolean"); - t.is (v23.get_bool (), true, "3.14 != 'foo' --> true"); + t.is(v23.type(), Variant::type_boolean, "3.14 != 'foo' --> boolean"); + t.is(v23.get_bool(), true, "3.14 != 'foo' --> true"); Variant v24 = v2 != v4; - t.is (v24.type (), Variant::type_boolean, "3.14 != 1234567890 --> boolean"); - t.is (v24.get_bool (), true, "3.14 != 1234567890 --> true"); + t.is(v24.type(), Variant::type_boolean, "3.14 != 1234567890 --> boolean"); + t.is(v24.get_bool(), true, "3.14 != 1234567890 --> true"); Variant v25 = v2 != v5; - t.is (v25.type (), Variant::type_boolean, "3.14 != 1200 --> boolean"); - t.is (v25.get_bool (), true, "3.14 != 1200 --> true"); + t.is(v25.type(), Variant::type_boolean, "3.14 != 1200 --> boolean"); + t.is(v25.get_bool(), true, "3.14 != 1200 --> true"); Variant v30 = v3 != v0; - t.is (v30.type (), Variant::type_boolean, "'foo' != true --> boolean"); - t.is (v30.get_bool (), true, "'foo' != true --> true"); + t.is(v30.type(), Variant::type_boolean, "'foo' != true --> boolean"); + t.is(v30.get_bool(), true, "'foo' != true --> true"); Variant v31 = v3 != v1; - t.is (v31.type (), Variant::type_boolean, "'foo' != 42 --> boolean"); - t.is (v31.get_bool (), true, "'foo' != 42 --> true"); + t.is(v31.type(), Variant::type_boolean, "'foo' != 42 --> boolean"); + t.is(v31.get_bool(), true, "'foo' != 42 --> true"); Variant v32 = v3 != v2; - t.is (v32.type (), Variant::type_boolean, "'foo' != 3.14 --> boolean"); - t.is (v32.get_bool (), true, "'foo' != 3.14 --> true"); + t.is(v32.type(), Variant::type_boolean, "'foo' != 3.14 --> boolean"); + t.is(v32.get_bool(), true, "'foo' != 3.14 --> true"); Variant v33 = v3 != v3; - t.is (v33.type (), Variant::type_boolean, "'foo' != 'foo' --> boolean"); - t.is (v33.get_bool (), false, "'foo' != 'foo' --> false"); + t.is(v33.type(), Variant::type_boolean, "'foo' != 'foo' --> boolean"); + t.is(v33.get_bool(), false, "'foo' != 'foo' --> false"); Variant v34 = v3 != v4; - t.is (v34.type (), Variant::type_boolean, "'foo' != 1234567890 --> boolean"); - t.is (v34.get_bool (), true, "'foo' != 1234567890 --> true"); + t.is(v34.type(), Variant::type_boolean, "'foo' != 1234567890 --> boolean"); + t.is(v34.get_bool(), true, "'foo' != 1234567890 --> true"); Variant v35 = v3 != v5; - t.is (v35.type (), Variant::type_boolean, "'foo' != 1200 --> boolean"); - t.is (v35.get_bool (), true, "'foo' != 1200 --> true"); + t.is(v35.type(), Variant::type_boolean, "'foo' != 1200 --> boolean"); + t.is(v35.get_bool(), true, "'foo' != 1200 --> true"); Variant v40 = v4 != v0; - t.is (v40.type (), Variant::type_boolean, "1234567890 != true --> boolean"); - t.is (v40.get_bool (), true, "1234567890 != true --> true"); + t.is(v40.type(), Variant::type_boolean, "1234567890 != true --> boolean"); + t.is(v40.get_bool(), true, "1234567890 != true --> true"); Variant v41 = v4 != v1; - t.is (v41.type (), Variant::type_boolean, "1234567890 != 42 --> boolean"); - t.is (v41.get_bool (), true, "1234567890 != 42 --> true"); + t.is(v41.type(), Variant::type_boolean, "1234567890 != 42 --> boolean"); + t.is(v41.get_bool(), true, "1234567890 != 42 --> true"); Variant v42 = v4 != v2; - t.is (v42.type (), Variant::type_boolean, "1234567890 != 3.14 --> boolean"); - t.is (v42.get_bool (), true, "1234567890 != 3.14 --> true"); + t.is(v42.type(), Variant::type_boolean, "1234567890 != 3.14 --> boolean"); + t.is(v42.get_bool(), true, "1234567890 != 3.14 --> true"); Variant v43 = v4 != v3; - t.is (v43.type (), Variant::type_boolean, "1234567890 != 'foo' --> boolean"); - t.is (v43.get_bool (), true, "1234567890 != 'foo' --> true"); + t.is(v43.type(), Variant::type_boolean, "1234567890 != 'foo' --> boolean"); + t.is(v43.get_bool(), true, "1234567890 != 'foo' --> true"); Variant v44 = v4 != v4; - t.is (v44.type (), Variant::type_boolean, "1234567890 != 1234567890 --> boolean"); - t.is (v44.get_bool (), false, "1234567890 != 1234567890 --> false"); + t.is(v44.type(), Variant::type_boolean, "1234567890 != 1234567890 --> boolean"); + t.is(v44.get_bool(), false, "1234567890 != 1234567890 --> false"); Variant v45 = v4 != v5; - t.is (v45.type (), Variant::type_boolean, "1234567890 != 1200 --> boolean"); - t.is (v45.get_bool (), true, "1234567890 != 1200 --> true"); + t.is(v45.type(), Variant::type_boolean, "1234567890 != 1200 --> boolean"); + t.is(v45.get_bool(), true, "1234567890 != 1200 --> true"); Variant v50 = v5 != v0; - t.is (v50.type (), Variant::type_boolean, "1200 != true --> boolean"); - t.is (v50.get_bool (), true, "1200 != true --> true"); + t.is(v50.type(), Variant::type_boolean, "1200 != true --> boolean"); + t.is(v50.get_bool(), true, "1200 != true --> true"); Variant v51 = v5 != v1; - t.is (v51.type (), Variant::type_boolean, "1200 != 42 --> boolean"); - t.is (v51.get_bool (), true, "1200 != 42 --> true"); + t.is(v51.type(), Variant::type_boolean, "1200 != 42 --> boolean"); + t.is(v51.get_bool(), true, "1200 != 42 --> true"); Variant v52 = v5 != v2; - t.is (v52.type (), Variant::type_boolean, "1200 != 3.14 --> boolean"); - t.is (v52.get_bool (), true, "1200 != 3.14 --> true"); + t.is(v52.type(), Variant::type_boolean, "1200 != 3.14 --> boolean"); + t.is(v52.get_bool(), true, "1200 != 3.14 --> true"); Variant v53 = v5 != v3; - t.is (v53.type (), Variant::type_boolean, "1200 != 'foo' --> boolean"); - t.is (v53.get_bool (), true, "1200 != 'foo' --> true"); + t.is(v53.type(), Variant::type_boolean, "1200 != 'foo' --> boolean"); + t.is(v53.get_bool(), true, "1200 != 'foo' --> true"); Variant v54 = v5 != v4; - t.is (v04.type (), Variant::type_boolean, "1200 != 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "1200 != 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "1200 != 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "1200 != 1234567890 --> true"); Variant v55 = v5 != v5; - t.is (v55.type (), Variant::type_boolean, "1200 != 1200 --> boolean"); - t.is (v55.get_bool (), false, "1200 != 1200 --> false"); + t.is(v55.type(), Variant::type_boolean, "1200 != 1200 --> boolean"); + t.is(v55.get_bool(), false, "1200 != 1200 --> false"); return 0; } diff --git a/test/variant_lt.test.cpp b/test/variant_lt.test.cpp index 897168c44..f5ae1331c 100644 --- a/test/variant_lt.test.cpp +++ b/test/variant_lt.test.cpp @@ -27,168 +27,168 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (72); +int main(int, char**) { + UnitTest t(72); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); Variant v00 = v0 < v0; - t.is (v00.type (), Variant::type_boolean, "true < true --> boolean"); - t.is (v00.get_bool (), false, "true < true --> false"); + t.is(v00.type(), Variant::type_boolean, "true < true --> boolean"); + t.is(v00.get_bool(), false, "true < true --> false"); Variant v01 = v0 < v1; - t.is (v01.type (), Variant::type_boolean, "true < 42 --> boolean"); - t.is (v01.get_bool (), true, "true < 42 --> true"); + t.is(v01.type(), Variant::type_boolean, "true < 42 --> boolean"); + t.is(v01.get_bool(), true, "true < 42 --> true"); Variant v02 = v0 < v2; - t.is (v02.type (), Variant::type_boolean, "true < 3.14 --> boolean"); - t.is (v02.get_bool (), true, "true < 3.14 --> true"); + t.is(v02.type(), Variant::type_boolean, "true < 3.14 --> boolean"); + t.is(v02.get_bool(), true, "true < 3.14 --> true"); Variant v03 = v0 < v3; - t.is (v03.type (), Variant::type_boolean, "true < 'foo' --> boolean"); - t.is (v03.get_bool (), false, "true < 'foo' --> false"); + t.is(v03.type(), Variant::type_boolean, "true < 'foo' --> boolean"); + t.is(v03.get_bool(), false, "true < 'foo' --> false"); Variant v04 = v0 < v4; - t.is (v04.type (), Variant::type_boolean, "true < 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "true < 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "true < 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "true < 1234567890 --> true"); Variant v05 = v0 < v5; - t.is (v05.type (), Variant::type_boolean, "true < 1200 --> boolean"); - t.is (v05.get_bool (), true, "true < 1200 --> true"); + t.is(v05.type(), Variant::type_boolean, "true < 1200 --> boolean"); + t.is(v05.get_bool(), true, "true < 1200 --> true"); Variant v10 = v1 < v0; - t.is (v10.type (), Variant::type_boolean, "42 < true --> boolean"); - t.is (v10.get_bool (), false, "42 < true --> false"); + t.is(v10.type(), Variant::type_boolean, "42 < true --> boolean"); + t.is(v10.get_bool(), false, "42 < true --> false"); Variant v11 = v1 < v1; - t.is (v11.type (), Variant::type_boolean, "42 < 42 --> boolean"); - t.is (v11.get_bool (), false, "42 < 42 --> false"); + t.is(v11.type(), Variant::type_boolean, "42 < 42 --> boolean"); + t.is(v11.get_bool(), false, "42 < 42 --> false"); Variant v12 = v1 < v2; - t.is (v12.type (), Variant::type_boolean, "42 < 3.14 --> boolean"); - t.is (v12.get_bool (), false, "42 < 3.14 --> false"); + t.is(v12.type(), Variant::type_boolean, "42 < 3.14 --> boolean"); + t.is(v12.get_bool(), false, "42 < 3.14 --> false"); Variant v13 = v1 < v3; - t.is (v13.type (), Variant::type_boolean, "42 < 'foo' --> boolean"); - t.is (v13.get_bool (), true, "42 < 'foo' --> true"); + t.is(v13.type(), Variant::type_boolean, "42 < 'foo' --> boolean"); + t.is(v13.get_bool(), true, "42 < 'foo' --> true"); Variant v14 = v1 < v4; - t.is (v04.type (), Variant::type_boolean, "42 < 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "42 < 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "42 < 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "42 < 1234567890 --> true"); Variant v15 = v1 < v5; - t.is (v15.type (), Variant::type_boolean, "42 < 1200 --> boolean"); - t.is (v15.get_bool (), true, "42 < 1200 --> true"); + t.is(v15.type(), Variant::type_boolean, "42 < 1200 --> boolean"); + t.is(v15.get_bool(), true, "42 < 1200 --> true"); Variant v20 = v2 < v0; - t.is (v20.type (), Variant::type_boolean, "3.14 < true --> boolean"); - t.is (v20.get_bool (), false, "3.14 < true --> false"); + t.is(v20.type(), Variant::type_boolean, "3.14 < true --> boolean"); + t.is(v20.get_bool(), false, "3.14 < true --> false"); Variant v21 = v2 < v1; - t.is (v21.type (), Variant::type_boolean, "3.14 < 42 --> boolean"); - t.is (v21.get_bool (), true, "3.14 < 42 --> true"); + t.is(v21.type(), Variant::type_boolean, "3.14 < 42 --> boolean"); + t.is(v21.get_bool(), true, "3.14 < 42 --> true"); Variant v22 = v2 < v2; - t.is (v22.type (), Variant::type_boolean, "3.14 < 3.14 --> boolean"); - t.is (v22.get_bool (), false, "3.14 < 3.14 --> false"); + t.is(v22.type(), Variant::type_boolean, "3.14 < 3.14 --> boolean"); + t.is(v22.get_bool(), false, "3.14 < 3.14 --> false"); Variant v23 = v2 < v3; - t.is (v23.type (), Variant::type_boolean, "3.14 < 'foo' --> boolean"); - t.is (v23.get_bool (), true, "3.14 < 'foo' --> true"); + t.is(v23.type(), Variant::type_boolean, "3.14 < 'foo' --> boolean"); + t.is(v23.get_bool(), true, "3.14 < 'foo' --> true"); Variant v24 = v2 < v4; - t.is (v24.type (), Variant::type_boolean, "3.14 < 1234567890 --> boolean"); - t.is (v24.get_bool (), true, "3.14 < 1234567890 --> true"); + t.is(v24.type(), Variant::type_boolean, "3.14 < 1234567890 --> boolean"); + t.is(v24.get_bool(), true, "3.14 < 1234567890 --> true"); Variant v25 = v2 < v5; - t.is (v25.type (), Variant::type_boolean, "3.14 < 1200 --> boolean"); - t.is (v25.get_bool (), true, "3.14 < 1200 --> true"); + t.is(v25.type(), Variant::type_boolean, "3.14 < 1200 --> boolean"); + t.is(v25.get_bool(), true, "3.14 < 1200 --> true"); Variant v30 = v3 < v0; - t.is (v30.type (), Variant::type_boolean, "'foo' < true --> boolean"); - t.is (v30.get_bool (), true, "'foo' < true --> true"); + t.is(v30.type(), Variant::type_boolean, "'foo' < true --> boolean"); + t.is(v30.get_bool(), true, "'foo' < true --> true"); Variant v31 = v3 < v1; - t.is (v31.type (), Variant::type_boolean, "'foo' < 42 --> boolean"); - t.is (v31.get_bool (), false, "'foo' < 42 --> false"); + t.is(v31.type(), Variant::type_boolean, "'foo' < 42 --> boolean"); + t.is(v31.get_bool(), false, "'foo' < 42 --> false"); Variant v32 = v3 < v2; - t.is (v32.type (), Variant::type_boolean, "'foo' < 3.14 --> boolean"); - t.is (v32.get_bool (), false, "'foo' < 3.14 --> false"); + t.is(v32.type(), Variant::type_boolean, "'foo' < 3.14 --> boolean"); + t.is(v32.get_bool(), false, "'foo' < 3.14 --> false"); Variant v33 = v3 < v3; - t.is (v33.type (), Variant::type_boolean, "'foo' < 'foo' --> boolean"); - t.is (v33.get_bool (), false, "'foo' < 'foo' --> false"); + t.is(v33.type(), Variant::type_boolean, "'foo' < 'foo' --> boolean"); + t.is(v33.get_bool(), false, "'foo' < 'foo' --> false"); Variant v34 = v3 < v4; - t.is (v34.type (), Variant::type_boolean, "'foo' < 1234567890 --> boolean"); - t.is (v34.get_bool (), true, "'foo' < 1234567890 --> true"); + t.is(v34.type(), Variant::type_boolean, "'foo' < 1234567890 --> boolean"); + t.is(v34.get_bool(), true, "'foo' < 1234567890 --> true"); Variant v35 = v3 < v5; - t.is (v35.type (), Variant::type_boolean, "'foo' < 1200 --> boolean"); - t.is (v35.get_bool (), true, "'foo' < 1200 --> true"); + t.is(v35.type(), Variant::type_boolean, "'foo' < 1200 --> boolean"); + t.is(v35.get_bool(), true, "'foo' < 1200 --> true"); Variant v40 = v4 < v0; - t.is (v40.type (), Variant::type_boolean, "1234567890 < true --> boolean"); - t.is (v40.get_bool (), false, "1234567890 < true --> false"); + t.is(v40.type(), Variant::type_boolean, "1234567890 < true --> boolean"); + t.is(v40.get_bool(), false, "1234567890 < true --> false"); Variant v41 = v4 < v1; - t.is (v41.type (), Variant::type_boolean, "1234567890 < 42 --> boolean"); - t.is (v41.get_bool (), false, "1234567890 < 42 --> false"); + t.is(v41.type(), Variant::type_boolean, "1234567890 < 42 --> boolean"); + t.is(v41.get_bool(), false, "1234567890 < 42 --> false"); Variant v42 = v4 < v2; - t.is (v42.type (), Variant::type_boolean, "1234567890 < 3.14 --> boolean"); - t.is (v42.get_bool (), false, "1234567890 < 3.14 --> false"); + t.is(v42.type(), Variant::type_boolean, "1234567890 < 3.14 --> boolean"); + t.is(v42.get_bool(), false, "1234567890 < 3.14 --> false"); Variant v43 = v4 < v3; - t.is (v43.type (), Variant::type_boolean, "1234567890 < 'foo' --> boolean"); - t.is (v43.get_bool (), false, "1234567890 < 'foo' --> false"); + t.is(v43.type(), Variant::type_boolean, "1234567890 < 'foo' --> boolean"); + t.is(v43.get_bool(), false, "1234567890 < 'foo' --> false"); Variant v44 = v4 < v4; - t.is (v44.type (), Variant::type_boolean, "1234567890 < 1234567890 --> boolean"); - t.is (v44.get_bool (), false, "1234567890 < 1234567890 --> false"); + t.is(v44.type(), Variant::type_boolean, "1234567890 < 1234567890 --> boolean"); + t.is(v44.get_bool(), false, "1234567890 < 1234567890 --> false"); Variant v45 = v4 < v5; // 1234567890 corresponds to Fri Feb 13 06:31:30 PM EST 2009 hence 1200 // (evaluated as now+1200s) be in future for any date after 2009-02-13 - t.is (v45.type (), Variant::type_boolean, "1234567890 < 1200 --> boolean"); - t.is (v45.get_bool (), true, "1234567890 < 1200 --> true"); + t.is(v45.type(), Variant::type_boolean, "1234567890 < 1200 --> boolean"); + t.is(v45.get_bool(), true, "1234567890 < 1200 --> true"); Variant v50 = v5 < v0; - t.is (v50.type (), Variant::type_boolean, "1200 < true --> boolean"); - t.is (v50.get_bool (), false, "1200 < true --> false"); + t.is(v50.type(), Variant::type_boolean, "1200 < true --> boolean"); + t.is(v50.get_bool(), false, "1200 < true --> false"); Variant v51 = v5 < v1; - t.is (v51.type (), Variant::type_boolean, "1200 < 42 --> boolean"); - t.is (v51.get_bool (), false, "1200 < 42 --> false"); + t.is(v51.type(), Variant::type_boolean, "1200 < 42 --> boolean"); + t.is(v51.get_bool(), false, "1200 < 42 --> false"); Variant v52 = v5 < v2; - t.is (v52.type (), Variant::type_boolean, "1200 < 3.14 --> boolean"); - t.is (v52.get_bool (), false, "1200 < 3.14 --> false"); + t.is(v52.type(), Variant::type_boolean, "1200 < 3.14 --> boolean"); + t.is(v52.get_bool(), false, "1200 < 3.14 --> false"); Variant v53 = v5 < v3; - t.is (v53.type (), Variant::type_boolean, "1200 < 'foo' --> boolean"); - t.is (v53.get_bool (), false, "1200 < 'foo' --> false"); + t.is(v53.type(), Variant::type_boolean, "1200 < 'foo' --> boolean"); + t.is(v53.get_bool(), false, "1200 < 'foo' --> false"); Variant v54 = v5 < v4; // Same logic as v45. - t.is (v54.type (), Variant::type_boolean, "1200 < 1234567890 --> boolean"); - t.is (v54.get_bool (), false, "1200 < 1234567890 --> false"); + t.is(v54.type(), Variant::type_boolean, "1200 < 1234567890 --> boolean"); + t.is(v54.get_bool(), false, "1200 < 1234567890 --> false"); Variant v55 = v5 < v5; - t.is (v55.type (), Variant::type_boolean, "1200 < 1200 --> boolean"); - t.is (v55.get_bool (), false, "1200 < 1200 --> false"); + t.is(v55.type(), Variant::type_boolean, "1200 < 1200 --> boolean"); + t.is(v55.get_bool(), false, "1200 < 1200 --> false"); return 0; } diff --git a/test/variant_lte.test.cpp b/test/variant_lte.test.cpp index 30eda6398..4b2eaba9a 100644 --- a/test/variant_lte.test.cpp +++ b/test/variant_lte.test.cpp @@ -27,165 +27,165 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (72); +int main(int, char**) { + UnitTest t(72); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_duration); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_duration); + Variant v5(1200, Variant::type_duration); Variant v00 = v0 <= v0; - t.is (v00.type (), Variant::type_boolean, "true <= true --> boolean"); - t.is (v00.get_bool (), true, "true <= true --> true"); + t.is(v00.type(), Variant::type_boolean, "true <= true --> boolean"); + t.is(v00.get_bool(), true, "true <= true --> true"); Variant v01 = v0 <= v1; - t.is (v01.type (), Variant::type_boolean, "true <= 42 --> boolean"); - t.is (v01.get_bool (), true, "true <= 42 --> true"); + t.is(v01.type(), Variant::type_boolean, "true <= 42 --> boolean"); + t.is(v01.get_bool(), true, "true <= 42 --> true"); Variant v02 = v0 <= v2; - t.is (v02.type (), Variant::type_boolean, "true <= 3.14 --> boolean"); - t.is (v02.get_bool (), true, "true <= 3.14 --> true"); + t.is(v02.type(), Variant::type_boolean, "true <= 3.14 --> boolean"); + t.is(v02.get_bool(), true, "true <= 3.14 --> true"); Variant v03 = v0 <= v3; - t.is (v03.type (), Variant::type_boolean, "true <= 'foo' --> boolean"); - t.is (v03.get_bool (), false, "true <= 'foo' --> false"); + t.is(v03.type(), Variant::type_boolean, "true <= 'foo' --> boolean"); + t.is(v03.get_bool(), false, "true <= 'foo' --> false"); Variant v04 = v0 <= v4; - t.is (v04.type (), Variant::type_boolean, "true <= 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "true <= 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "true <= 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "true <= 1234567890 --> true"); Variant v05 = v0 <= v5; - t.is (v05.type (), Variant::type_boolean, "true <= 1200 --> boolean"); - t.is (v05.get_bool (), true, "true <= 1200 --> true"); + t.is(v05.type(), Variant::type_boolean, "true <= 1200 --> boolean"); + t.is(v05.get_bool(), true, "true <= 1200 --> true"); Variant v10 = v1 <= v0; - t.is (v10.type (), Variant::type_boolean, "42 <= true --> boolean"); - t.is (v10.get_bool (), false, "42 <= true --> false"); + t.is(v10.type(), Variant::type_boolean, "42 <= true --> boolean"); + t.is(v10.get_bool(), false, "42 <= true --> false"); Variant v11 = v1 <= v1; - t.is (v11.type (), Variant::type_boolean, "42 <= 42 --> boolean"); - t.is (v11.get_bool (), true, "42 <= 42 --> true"); + t.is(v11.type(), Variant::type_boolean, "42 <= 42 --> boolean"); + t.is(v11.get_bool(), true, "42 <= 42 --> true"); Variant v12 = v1 <= v2; - t.is (v12.type (), Variant::type_boolean, "42 <= 3.14 --> boolean"); - t.is (v12.get_bool (), false, "42 <= 3.14 --> false"); + t.is(v12.type(), Variant::type_boolean, "42 <= 3.14 --> boolean"); + t.is(v12.get_bool(), false, "42 <= 3.14 --> false"); Variant v13 = v1 <= v3; - t.is (v13.type (), Variant::type_boolean, "42 <= 'foo' --> boolean"); - t.is (v13.get_bool (), true, "42 <= 'foo' --> true"); + t.is(v13.type(), Variant::type_boolean, "42 <= 'foo' --> boolean"); + t.is(v13.get_bool(), true, "42 <= 'foo' --> true"); Variant v14 = v1 <= v4; - t.is (v04.type (), Variant::type_boolean, "42 <= 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "42 <= 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "42 <= 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "42 <= 1234567890 --> true"); Variant v15 = v1 <= v5; - t.is (v15.type (), Variant::type_boolean, "42 <= 1200 --> boolean"); - t.is (v15.get_bool (), true, "42 <= 1200 --> true"); + t.is(v15.type(), Variant::type_boolean, "42 <= 1200 --> boolean"); + t.is(v15.get_bool(), true, "42 <= 1200 --> true"); Variant v20 = v2 <= v0; - t.is (v20.type (), Variant::type_boolean, "3.14 <= true --> boolean"); - t.is (v20.get_bool (), false, "3.14 <= true --> false"); + t.is(v20.type(), Variant::type_boolean, "3.14 <= true --> boolean"); + t.is(v20.get_bool(), false, "3.14 <= true --> false"); Variant v21 = v2 <= v1; - t.is (v21.type (), Variant::type_boolean, "3.14 <= 42 --> boolean"); - t.is (v21.get_bool (), true, "3.14 <= 42 --> true"); + t.is(v21.type(), Variant::type_boolean, "3.14 <= 42 --> boolean"); + t.is(v21.get_bool(), true, "3.14 <= 42 --> true"); Variant v22 = v2 <= v2; - t.is (v22.type (), Variant::type_boolean, "3.14 <= 3.14 --> boolean"); - t.is (v22.get_bool (), true, "3.14 <= 3.14 --> true"); + t.is(v22.type(), Variant::type_boolean, "3.14 <= 3.14 --> boolean"); + t.is(v22.get_bool(), true, "3.14 <= 3.14 --> true"); Variant v23 = v2 <= v3; - t.is (v23.type (), Variant::type_boolean, "3.14 <= 'foo' --> boolean"); - t.is (v23.get_bool (), true, "3.14 <= 'foo' --> true"); + t.is(v23.type(), Variant::type_boolean, "3.14 <= 'foo' --> boolean"); + t.is(v23.get_bool(), true, "3.14 <= 'foo' --> true"); Variant v24 = v2 <= v4; - t.is (v24.type (), Variant::type_boolean, "3.14 <= 1234567890 --> boolean"); - t.is (v24.get_bool (), true, "3.14 <= 1234567890 --> true"); + t.is(v24.type(), Variant::type_boolean, "3.14 <= 1234567890 --> boolean"); + t.is(v24.get_bool(), true, "3.14 <= 1234567890 --> true"); Variant v25 = v2 <= v5; - t.is (v25.type (), Variant::type_boolean, "3.14 <= 1200 --> boolean"); - t.is (v25.get_bool (), true, "3.14 <= 1200 --> true"); + t.is(v25.type(), Variant::type_boolean, "3.14 <= 1200 --> boolean"); + t.is(v25.get_bool(), true, "3.14 <= 1200 --> true"); Variant v30 = v3 <= v0; - t.is (v30.type (), Variant::type_boolean, "'foo' <= true --> boolean"); - t.is (v30.get_bool (), true, "'foo' <= true --> true"); + t.is(v30.type(), Variant::type_boolean, "'foo' <= true --> boolean"); + t.is(v30.get_bool(), true, "'foo' <= true --> true"); Variant v31 = v3 <= v1; - t.is (v31.type (), Variant::type_boolean, "'foo' <= 42 --> boolean"); - t.is (v31.get_bool (), false, "'foo' <= 42 --> false"); + t.is(v31.type(), Variant::type_boolean, "'foo' <= 42 --> boolean"); + t.is(v31.get_bool(), false, "'foo' <= 42 --> false"); Variant v32 = v3 <= v2; - t.is (v32.type (), Variant::type_boolean, "'foo' <= 3.14 --> boolean"); - t.is (v32.get_bool (), false, "'foo' <= 3.14 --> false"); + t.is(v32.type(), Variant::type_boolean, "'foo' <= 3.14 --> boolean"); + t.is(v32.get_bool(), false, "'foo' <= 3.14 --> false"); Variant v33 = v3 <= v3; - t.is (v33.type (), Variant::type_boolean, "'foo' <= 'foo' --> boolean"); - t.is (v33.get_bool (), true, "'foo' <= 'foo' --> true"); + t.is(v33.type(), Variant::type_boolean, "'foo' <= 'foo' --> boolean"); + t.is(v33.get_bool(), true, "'foo' <= 'foo' --> true"); Variant v34 = v3 <= v4; - t.is (v34.type (), Variant::type_boolean, "'foo' <= 1234567890 --> boolean"); - t.is (v34.get_bool (), true, "'foo' <= 1234567890 --> true"); + t.is(v34.type(), Variant::type_boolean, "'foo' <= 1234567890 --> boolean"); + t.is(v34.get_bool(), true, "'foo' <= 1234567890 --> true"); Variant v35 = v3 <= v5; - t.is (v35.type (), Variant::type_boolean, "'foo' <= 1200 --> boolean"); - t.is (v35.get_bool (), true, "'foo' <= 1200 --> true"); + t.is(v35.type(), Variant::type_boolean, "'foo' <= 1200 --> boolean"); + t.is(v35.get_bool(), true, "'foo' <= 1200 --> true"); Variant v40 = v4 <= v0; - t.is (v40.type (), Variant::type_boolean, "1234567890 <= true --> boolean"); - t.is (v40.get_bool (), false, "1234567890 <= true --> false"); + t.is(v40.type(), Variant::type_boolean, "1234567890 <= true --> boolean"); + t.is(v40.get_bool(), false, "1234567890 <= true --> false"); Variant v41 = v4 <= v1; - t.is (v41.type (), Variant::type_boolean, "1234567890 <= 42 --> boolean"); - t.is (v41.get_bool (), false, "1234567890 <= 42 --> false"); + t.is(v41.type(), Variant::type_boolean, "1234567890 <= 42 --> boolean"); + t.is(v41.get_bool(), false, "1234567890 <= 42 --> false"); Variant v42 = v4 <= v2; - t.is (v42.type (), Variant::type_boolean, "1234567890 <= 3.14 --> boolean"); - t.is (v42.get_bool (), false, "1234567890 <= 3.14 --> false"); + t.is(v42.type(), Variant::type_boolean, "1234567890 <= 3.14 --> boolean"); + t.is(v42.get_bool(), false, "1234567890 <= 3.14 --> false"); Variant v43 = v4 <= v3; - t.is (v43.type (), Variant::type_boolean, "1234567890 <= 'foo' --> boolean"); - t.is (v43.get_bool (), false, "1234567890 <= 'foo' --> false"); + t.is(v43.type(), Variant::type_boolean, "1234567890 <= 'foo' --> boolean"); + t.is(v43.get_bool(), false, "1234567890 <= 'foo' --> false"); Variant v44 = v4 <= v4; - t.is (v44.type (), Variant::type_boolean, "1234567890 <= 1234567890 --> boolean"); - t.is (v44.get_bool (), true, "1234567890 <= 1234567890 --> true"); + t.is(v44.type(), Variant::type_boolean, "1234567890 <= 1234567890 --> boolean"); + t.is(v44.get_bool(), true, "1234567890 <= 1234567890 --> true"); Variant v45 = v4 <= v5; - t.is (v45.type (), Variant::type_boolean, "1234567890 <= 1200 --> boolean"); - t.is (v45.get_bool (), false, "1234567890 <= 1200 --> false"); + t.is(v45.type(), Variant::type_boolean, "1234567890 <= 1200 --> boolean"); + t.is(v45.get_bool(), false, "1234567890 <= 1200 --> false"); Variant v50 = v5 <= v0; - t.is (v50.type (), Variant::type_boolean, "1200 <= true --> boolean"); - t.is (v50.get_bool (), false, "1200 <= true --> false"); + t.is(v50.type(), Variant::type_boolean, "1200 <= true --> boolean"); + t.is(v50.get_bool(), false, "1200 <= true --> false"); Variant v51 = v5 <= v1; - t.is (v51.type (), Variant::type_boolean, "1200 <= 42 --> boolean"); - t.is (v51.get_bool (), false, "1200 <= 42 --> false"); + t.is(v51.type(), Variant::type_boolean, "1200 <= 42 --> boolean"); + t.is(v51.get_bool(), false, "1200 <= 42 --> false"); Variant v52 = v5 <= v2; - t.is (v52.type (), Variant::type_boolean, "1200 <= 3.14 --> boolean"); - t.is (v52.get_bool (), false, "1200 <= 3.14 --> false"); + t.is(v52.type(), Variant::type_boolean, "1200 <= 3.14 --> boolean"); + t.is(v52.get_bool(), false, "1200 <= 3.14 --> false"); Variant v53 = v5 <= v3; - t.is (v53.type (), Variant::type_boolean, "1200 <= 'foo' --> boolean"); - t.is (v53.get_bool (), false, "1200 <= 'foo' --> false"); + t.is(v53.type(), Variant::type_boolean, "1200 <= 'foo' --> boolean"); + t.is(v53.get_bool(), false, "1200 <= 'foo' --> false"); Variant v54 = v5 <= v4; - t.is (v54.type (), Variant::type_boolean, "1200 <= 1234567890 --> boolean"); - t.is (v54.get_bool (), true, "1200 <= 1234567890 --> true"); + t.is(v54.type(), Variant::type_boolean, "1200 <= 1234567890 --> boolean"); + t.is(v54.get_bool(), true, "1200 <= 1234567890 --> true"); Variant v55 = v5 <= v5; - t.is (v55.type (), Variant::type_boolean, "1200 <= 1200 --> boolean"); - t.is (v55.get_bool (), true, "1200 <= 1200 --> true"); + t.is(v55.type(), Variant::type_boolean, "1200 <= 1200 --> boolean"); + t.is(v55.get_bool(), true, "1200 <= 1200 --> true"); return 0; } diff --git a/test/variant_match.test.cpp b/test/variant_match.test.cpp index 97a7f6432..2777eb551 100644 --- a/test/variant_match.test.cpp +++ b/test/variant_match.test.cpp @@ -27,271 +27,271 @@ #include // cmake.h include header must come first -#include -#include -#include #include +#include +#include + +#include Task task; //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (120); +int main(int, char**) { + UnitTest t(120); - Variant vs0 ("untrue"); // ~ true - Variant vs1 (8421); // ~ 42 - Variant vs2 (3.14159); // ~ 3.14 - Variant vs3 ("foolish"); // ~ foo + Variant vs0("untrue"); // ~ true + Variant vs1(8421); // ~ 42 + Variant vs2(3.14159); // ~ 3.14 + Variant vs3("foolish"); // ~ foo - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // Interesting cases. - Variant vs00 = vs0.operator_match (v0, task); - t.is (vs00.type (), Variant::type_boolean, "untrue ~ true --> boolean"); - t.is (vs00.get_bool (), true, "untrue ~ true --> true"); + Variant vs00 = vs0.operator_match(v0, task); + t.is(vs00.type(), Variant::type_boolean, "untrue ~ true --> boolean"); + t.is(vs00.get_bool(), true, "untrue ~ true --> true"); - Variant vs01 = vs0.operator_match (v1, task); - t.is (vs01.type (), Variant::type_boolean, "untrue ~ 42 --> boolean"); - t.is (vs01.get_bool (), false, "untrue ~ 42 --> false"); + Variant vs01 = vs0.operator_match(v1, task); + t.is(vs01.type(), Variant::type_boolean, "untrue ~ 42 --> boolean"); + t.is(vs01.get_bool(), false, "untrue ~ 42 --> false"); - Variant vs02 = vs0.operator_match (v2, task); - t.is (vs02.type (), Variant::type_boolean, "untrue ~ 3.14 --> boolean"); - t.is (vs02.get_bool (), false, "untrue ~ 3.14 --> false"); + Variant vs02 = vs0.operator_match(v2, task); + t.is(vs02.type(), Variant::type_boolean, "untrue ~ 3.14 --> boolean"); + t.is(vs02.get_bool(), false, "untrue ~ 3.14 --> false"); - Variant vs03 = vs0.operator_match (v3, task); - t.is (vs03.type (), Variant::type_boolean, "untrue ~ 'foo' --> boolean"); - t.is (vs03.get_bool (), false, "untrue ~ 'foo' --> false"); + Variant vs03 = vs0.operator_match(v3, task); + t.is(vs03.type(), Variant::type_boolean, "untrue ~ 'foo' --> boolean"); + t.is(vs03.get_bool(), false, "untrue ~ 'foo' --> false"); - Variant vs04 = vs0.operator_match (v4, task); - t.is (vs04.type (), Variant::type_boolean, "untrue ~ 1234567890 --> boolean"); - t.is (vs04.get_bool (), false, "untrue ~ 1234567890 --> false"); + Variant vs04 = vs0.operator_match(v4, task); + t.is(vs04.type(), Variant::type_boolean, "untrue ~ 1234567890 --> boolean"); + t.is(vs04.get_bool(), false, "untrue ~ 1234567890 --> false"); - Variant vs05 = vs0.operator_match (v5, task); - t.is (vs05.type (), Variant::type_boolean, "untrue ~ 1200 --> boolean"); - t.is (vs05.get_bool (), false, "untrue ~ 1200 --> false"); + Variant vs05 = vs0.operator_match(v5, task); + t.is(vs05.type(), Variant::type_boolean, "untrue ~ 1200 --> boolean"); + t.is(vs05.get_bool(), false, "untrue ~ 1200 --> false"); - Variant vs10 = vs1.operator_match (v0, task); - t.is (vs10.type (), Variant::type_boolean, "8421 ~ true --> boolean"); - t.is (vs10.get_bool (), false, "8421 ~ true --> false"); + Variant vs10 = vs1.operator_match(v0, task); + t.is(vs10.type(), Variant::type_boolean, "8421 ~ true --> boolean"); + t.is(vs10.get_bool(), false, "8421 ~ true --> false"); - Variant vs11 = vs1.operator_match (v1, task); - t.is (vs11.type (), Variant::type_boolean, "8421 ~ 42 --> boolean"); - t.is (vs11.get_bool (), true, "8421 ~ 42 --> true"); + Variant vs11 = vs1.operator_match(v1, task); + t.is(vs11.type(), Variant::type_boolean, "8421 ~ 42 --> boolean"); + t.is(vs11.get_bool(), true, "8421 ~ 42 --> true"); - Variant vs12 = vs1.operator_match (v2, task); - t.is (vs12.type (), Variant::type_boolean, "8421 ~ 3.14 --> boolean"); - t.is (vs12.get_bool (), false, "8421 ~ 3.14 --> false"); + Variant vs12 = vs1.operator_match(v2, task); + t.is(vs12.type(), Variant::type_boolean, "8421 ~ 3.14 --> boolean"); + t.is(vs12.get_bool(), false, "8421 ~ 3.14 --> false"); - Variant vs13 = vs1.operator_match (v3, task); - t.is (vs13.type (), Variant::type_boolean, "8421 ~ 'foo' --> boolean"); - t.is (vs13.get_bool (), false, "8421 ~ 'foo' --> false"); + Variant vs13 = vs1.operator_match(v3, task); + t.is(vs13.type(), Variant::type_boolean, "8421 ~ 'foo' --> boolean"); + t.is(vs13.get_bool(), false, "8421 ~ 'foo' --> false"); - Variant vs14 = vs1.operator_match (v4, task); - t.is (vs14.type (), Variant::type_boolean, "8421 ~ 1234567890 --> boolean"); - t.is (vs14.get_bool (), false, "8421 ~ 1234567890 --> false"); + Variant vs14 = vs1.operator_match(v4, task); + t.is(vs14.type(), Variant::type_boolean, "8421 ~ 1234567890 --> boolean"); + t.is(vs14.get_bool(), false, "8421 ~ 1234567890 --> false"); - Variant vs15 = vs1.operator_match (v5, task); - t.is (vs15.type (), Variant::type_boolean, "8421 ~ 1200 --> boolean"); - t.is (vs15.get_bool (), false, "8421 ~ 1200 --> false"); + Variant vs15 = vs1.operator_match(v5, task); + t.is(vs15.type(), Variant::type_boolean, "8421 ~ 1200 --> boolean"); + t.is(vs15.get_bool(), false, "8421 ~ 1200 --> false"); - Variant vs20 = vs2.operator_match (v0, task); - t.is (vs20.type (), Variant::type_boolean, "3.14159 ~ true --> boolean"); - t.is (vs20.get_bool (), false, "3.14159 ~ true --> false"); + Variant vs20 = vs2.operator_match(v0, task); + t.is(vs20.type(), Variant::type_boolean, "3.14159 ~ true --> boolean"); + t.is(vs20.get_bool(), false, "3.14159 ~ true --> false"); - Variant vs21 = vs2.operator_match (v1, task); - t.is (vs21.type (), Variant::type_boolean, "3.14159 ~ 42 --> boolean"); - t.is (vs21.get_bool (), false, "3.14159 ~ 42 --> false"); + Variant vs21 = vs2.operator_match(v1, task); + t.is(vs21.type(), Variant::type_boolean, "3.14159 ~ 42 --> boolean"); + t.is(vs21.get_bool(), false, "3.14159 ~ 42 --> false"); - Variant vs22 = vs2.operator_match (v2, task); - t.is (vs22.type (), Variant::type_boolean, "3.14159 ~ 3.14 --> boolean"); - t.is (vs22.get_bool (), true, "3.14159 ~ 3.14 --> true"); + Variant vs22 = vs2.operator_match(v2, task); + t.is(vs22.type(), Variant::type_boolean, "3.14159 ~ 3.14 --> boolean"); + t.is(vs22.get_bool(), true, "3.14159 ~ 3.14 --> true"); - Variant vs23 = vs2.operator_match (v3, task); - t.is (vs23.type (), Variant::type_boolean, "3.14159 ~ 'foo' --> boolean"); - t.is (vs23.get_bool (), false, "3.14159 ~ 'foo' --> false"); + Variant vs23 = vs2.operator_match(v3, task); + t.is(vs23.type(), Variant::type_boolean, "3.14159 ~ 'foo' --> boolean"); + t.is(vs23.get_bool(), false, "3.14159 ~ 'foo' --> false"); - Variant vs24 = vs2.operator_match (v4, task); - t.is (vs24.type (), Variant::type_boolean, "3.14159 ~ 1234567890 --> boolean"); - t.is (vs24.get_bool (), false, "3.14159 ~ 1234567890 --> false"); + Variant vs24 = vs2.operator_match(v4, task); + t.is(vs24.type(), Variant::type_boolean, "3.14159 ~ 1234567890 --> boolean"); + t.is(vs24.get_bool(), false, "3.14159 ~ 1234567890 --> false"); - Variant vs25 = vs2.operator_match (v5, task); - t.is (vs25.type (), Variant::type_boolean, "3.14159 ~ 1200 --> boolean"); - t.is (vs25.get_bool (), false, "3.14159 ~ 1200 --> false"); + Variant vs25 = vs2.operator_match(v5, task); + t.is(vs25.type(), Variant::type_boolean, "3.14159 ~ 1200 --> boolean"); + t.is(vs25.get_bool(), false, "3.14159 ~ 1200 --> false"); - Variant vs30 = vs3.operator_match (v0, task); - t.is (vs30.type (), Variant::type_boolean, "foolish ~ true --> boolean"); - t.is (vs30.get_bool (), false, "foolish ~ true --> false"); + Variant vs30 = vs3.operator_match(v0, task); + t.is(vs30.type(), Variant::type_boolean, "foolish ~ true --> boolean"); + t.is(vs30.get_bool(), false, "foolish ~ true --> false"); - Variant vs31 = vs3.operator_match (v1, task); - t.is (vs31.type (), Variant::type_boolean, "foolish ~ 42 --> boolean"); - t.is (vs31.get_bool (), false, "foolish ~ 42 --> false"); + Variant vs31 = vs3.operator_match(v1, task); + t.is(vs31.type(), Variant::type_boolean, "foolish ~ 42 --> boolean"); + t.is(vs31.get_bool(), false, "foolish ~ 42 --> false"); - Variant vs32 = vs3.operator_match (v2, task); - t.is (vs32.type (), Variant::type_boolean, "foolish ~ 3.14 --> boolean"); - t.is (vs32.get_bool (), false, "foolish ~ 3.14 --> false"); + Variant vs32 = vs3.operator_match(v2, task); + t.is(vs32.type(), Variant::type_boolean, "foolish ~ 3.14 --> boolean"); + t.is(vs32.get_bool(), false, "foolish ~ 3.14 --> false"); - Variant vs33 = vs3.operator_match (v3, task); - t.is (vs33.type (), Variant::type_boolean, "foolish ~ 'foo' --> boolean"); - t.is (vs33.get_bool (), true, "foolish ~ 'foo' --> true"); + Variant vs33 = vs3.operator_match(v3, task); + t.is(vs33.type(), Variant::type_boolean, "foolish ~ 'foo' --> boolean"); + t.is(vs33.get_bool(), true, "foolish ~ 'foo' --> true"); - Variant vs34 = vs3.operator_match (v4, task); - t.is (vs34.type (), Variant::type_boolean, "foolish ~ 1234567890 --> boolean"); - t.is (vs34.get_bool (), false, "foolish ~ 1234567890 --> false"); + Variant vs34 = vs3.operator_match(v4, task); + t.is(vs34.type(), Variant::type_boolean, "foolish ~ 1234567890 --> boolean"); + t.is(vs34.get_bool(), false, "foolish ~ 1234567890 --> false"); - Variant vs35 = vs3.operator_match (v5, task); - t.is (vs35.type (), Variant::type_boolean, "foolish ~ 1200 --> boolean"); - t.is (vs35.get_bool (), false, "foolish ~ 1200 --> false"); + Variant vs35 = vs3.operator_match(v5, task); + t.is(vs35.type(), Variant::type_boolean, "foolish ~ 1200 --> boolean"); + t.is(vs35.get_bool(), false, "foolish ~ 1200 --> false"); // Exhaustive comparisons. - Variant v00 = v0.operator_match (v0, task); - t.is (v00.type (), Variant::type_boolean, "true ~ true --> boolean"); - t.is (v00.get_bool (), true, "true ~ true --> true"); + Variant v00 = v0.operator_match(v0, task); + t.is(v00.type(), Variant::type_boolean, "true ~ true --> boolean"); + t.is(v00.get_bool(), true, "true ~ true --> true"); - Variant v01 = v0.operator_match (v1, task); - t.is (v01.type (), Variant::type_boolean, "true ~ 42 --> boolean"); - t.is (v01.get_bool (), false, "true ~ 42 --> false"); + Variant v01 = v0.operator_match(v1, task); + t.is(v01.type(), Variant::type_boolean, "true ~ 42 --> boolean"); + t.is(v01.get_bool(), false, "true ~ 42 --> false"); - Variant v02 = v0.operator_match (v2, task); - t.is (v02.type (), Variant::type_boolean, "true ~ 3.14 --> boolean"); - t.is (v02.get_bool (), false, "true ~ 3.14 --> false"); + Variant v02 = v0.operator_match(v2, task); + t.is(v02.type(), Variant::type_boolean, "true ~ 3.14 --> boolean"); + t.is(v02.get_bool(), false, "true ~ 3.14 --> false"); - Variant v03 = v0.operator_match (v3, task); - t.is (v03.type (), Variant::type_boolean, "true ~ 'foo' --> boolean"); - t.is (v03.get_bool (), false, "true ~ 'foo' --> false"); + Variant v03 = v0.operator_match(v3, task); + t.is(v03.type(), Variant::type_boolean, "true ~ 'foo' --> boolean"); + t.is(v03.get_bool(), false, "true ~ 'foo' --> false"); - Variant v04 = v0.operator_match (v4, task); - t.is (v04.type (), Variant::type_boolean, "true ~ 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "true ~ 1234567890 --> false"); + Variant v04 = v0.operator_match(v4, task); + t.is(v04.type(), Variant::type_boolean, "true ~ 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "true ~ 1234567890 --> false"); - Variant v05 = v0.operator_match (v5, task); - t.is (v05.type (), Variant::type_boolean, "true ~ 1200 --> boolean"); - t.is (v05.get_bool (), false, "true ~ 1200 --> false"); + Variant v05 = v0.operator_match(v5, task); + t.is(v05.type(), Variant::type_boolean, "true ~ 1200 --> boolean"); + t.is(v05.get_bool(), false, "true ~ 1200 --> false"); - Variant v10 = v1.operator_match (v0, task); - t.is (v10.type (), Variant::type_boolean, "42 ~ true --> boolean"); - t.is (v10.get_bool (), false, "42 ~ true --> false"); + Variant v10 = v1.operator_match(v0, task); + t.is(v10.type(), Variant::type_boolean, "42 ~ true --> boolean"); + t.is(v10.get_bool(), false, "42 ~ true --> false"); - Variant v11 = v1.operator_match (v1, task); - t.is (v11.type (), Variant::type_boolean, "42 ~ 42 --> boolean"); - t.is (v11.get_bool (), true, "42 ~ 42 --> true"); + Variant v11 = v1.operator_match(v1, task); + t.is(v11.type(), Variant::type_boolean, "42 ~ 42 --> boolean"); + t.is(v11.get_bool(), true, "42 ~ 42 --> true"); - Variant v12 = v1.operator_match (v2, task); - t.is (v12.type (), Variant::type_boolean, "42 ~ 3.14 --> boolean"); - t.is (v12.get_bool (), false, "42 ~ 3.14 --> false"); + Variant v12 = v1.operator_match(v2, task); + t.is(v12.type(), Variant::type_boolean, "42 ~ 3.14 --> boolean"); + t.is(v12.get_bool(), false, "42 ~ 3.14 --> false"); - Variant v13 = v1.operator_match (v3, task); - t.is (v13.type (), Variant::type_boolean, "42 ~ 'foo' --> boolean"); - t.is (v13.get_bool (), false, "42 ~ 'foo' --> false"); + Variant v13 = v1.operator_match(v3, task); + t.is(v13.type(), Variant::type_boolean, "42 ~ 'foo' --> boolean"); + t.is(v13.get_bool(), false, "42 ~ 'foo' --> false"); - Variant v14 = v1.operator_match (v4, task); - t.is (v04.type (), Variant::type_boolean, "42 ~ 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "42 ~ 1234567890 --> false"); + Variant v14 = v1.operator_match(v4, task); + t.is(v04.type(), Variant::type_boolean, "42 ~ 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "42 ~ 1234567890 --> false"); - Variant v15 = v1.operator_match (v5, task); - t.is (v15.type (), Variant::type_boolean, "42 ~ 1200 --> boolean"); - t.is (v15.get_bool (), false, "42 ~ 1200 --> false"); + Variant v15 = v1.operator_match(v5, task); + t.is(v15.type(), Variant::type_boolean, "42 ~ 1200 --> boolean"); + t.is(v15.get_bool(), false, "42 ~ 1200 --> false"); - Variant v20 = v2.operator_match (v0, task); - t.is (v20.type (), Variant::type_boolean, "3.14 ~ true --> boolean"); - t.is (v20.get_bool (), false, "3.14 ~ true --> false"); + Variant v20 = v2.operator_match(v0, task); + t.is(v20.type(), Variant::type_boolean, "3.14 ~ true --> boolean"); + t.is(v20.get_bool(), false, "3.14 ~ true --> false"); - Variant v21 = v2.operator_match (v1, task); - t.is (v21.type (), Variant::type_boolean, "3.14 ~ 42 --> boolean"); - t.is (v21.get_bool (), false, "3.14 ~ 42 --> false"); + Variant v21 = v2.operator_match(v1, task); + t.is(v21.type(), Variant::type_boolean, "3.14 ~ 42 --> boolean"); + t.is(v21.get_bool(), false, "3.14 ~ 42 --> false"); - Variant v22 = v2.operator_match (v2, task); - t.is (v22.type (), Variant::type_boolean, "3.14 ~ 3.14 --> boolean"); - t.is (v22.get_bool (), true, "3.14 ~ 3.14 --> true"); + Variant v22 = v2.operator_match(v2, task); + t.is(v22.type(), Variant::type_boolean, "3.14 ~ 3.14 --> boolean"); + t.is(v22.get_bool(), true, "3.14 ~ 3.14 --> true"); - Variant v23 = v2.operator_match (v3, task); - t.is (v23.type (), Variant::type_boolean, "3.14 ~ 'foo' --> boolean"); - t.is (v23.get_bool (), false, "3.14 ~ 'foo' --> false"); + Variant v23 = v2.operator_match(v3, task); + t.is(v23.type(), Variant::type_boolean, "3.14 ~ 'foo' --> boolean"); + t.is(v23.get_bool(), false, "3.14 ~ 'foo' --> false"); - Variant v24 = v2.operator_match (v4, task); - t.is (v24.type (), Variant::type_boolean, "3.14 ~ 1234567890 --> boolean"); - t.is (v24.get_bool (), false, "3.14 ~ 1234567890 --> false"); + Variant v24 = v2.operator_match(v4, task); + t.is(v24.type(), Variant::type_boolean, "3.14 ~ 1234567890 --> boolean"); + t.is(v24.get_bool(), false, "3.14 ~ 1234567890 --> false"); - Variant v25 = v2.operator_match (v5, task); - t.is (v25.type (), Variant::type_boolean, "3.14 ~ 1200 --> boolean"); - t.is (v25.get_bool (), false, "3.14 ~ 1200 --> false"); + Variant v25 = v2.operator_match(v5, task); + t.is(v25.type(), Variant::type_boolean, "3.14 ~ 1200 --> boolean"); + t.is(v25.get_bool(), false, "3.14 ~ 1200 --> false"); - Variant v30 = v3.operator_match (v0, task); - t.is (v30.type (), Variant::type_boolean, "'foo' ~ true --> boolean"); - t.is (v30.get_bool (), false, "'foo' ~ true --> false"); + Variant v30 = v3.operator_match(v0, task); + t.is(v30.type(), Variant::type_boolean, "'foo' ~ true --> boolean"); + t.is(v30.get_bool(), false, "'foo' ~ true --> false"); - Variant v31 = v3.operator_match (v1, task); - t.is (v31.type (), Variant::type_boolean, "'foo' ~ 42 --> boolean"); - t.is (v31.get_bool (), false, "'foo' ~ 42 --> false"); + Variant v31 = v3.operator_match(v1, task); + t.is(v31.type(), Variant::type_boolean, "'foo' ~ 42 --> boolean"); + t.is(v31.get_bool(), false, "'foo' ~ 42 --> false"); - Variant v32 = v3.operator_match (v2, task); - t.is (v32.type (), Variant::type_boolean, "'foo' ~ 3.14 --> boolean"); - t.is (v32.get_bool (), false, "'foo' ~ 3.14 --> false"); + Variant v32 = v3.operator_match(v2, task); + t.is(v32.type(), Variant::type_boolean, "'foo' ~ 3.14 --> boolean"); + t.is(v32.get_bool(), false, "'foo' ~ 3.14 --> false"); - Variant v33 = v3.operator_match (v3, task); - t.is (v33.type (), Variant::type_boolean, "'foo' ~ 'foo' --> boolean"); - t.is (v33.get_bool (), true, "'foo' ~ 'foo' --> true"); + Variant v33 = v3.operator_match(v3, task); + t.is(v33.type(), Variant::type_boolean, "'foo' ~ 'foo' --> boolean"); + t.is(v33.get_bool(), true, "'foo' ~ 'foo' --> true"); - Variant v34 = v3.operator_match (v4, task); - t.is (v34.type (), Variant::type_boolean, "'foo' ~ 1234567890 --> boolean"); - t.is (v34.get_bool (), false, "'foo' ~ 1234567890 --> false"); + Variant v34 = v3.operator_match(v4, task); + t.is(v34.type(), Variant::type_boolean, "'foo' ~ 1234567890 --> boolean"); + t.is(v34.get_bool(), false, "'foo' ~ 1234567890 --> false"); - Variant v35 = v3.operator_match (v5, task); - t.is (v35.type (), Variant::type_boolean, "'foo' ~ 1200 --> boolean"); - t.is (v35.get_bool (), false, "'foo' ~ 1200 --> false"); + Variant v35 = v3.operator_match(v5, task); + t.is(v35.type(), Variant::type_boolean, "'foo' ~ 1200 --> boolean"); + t.is(v35.get_bool(), false, "'foo' ~ 1200 --> false"); - Variant v40 = v4.operator_match (v0, task); - t.is (v40.type (), Variant::type_boolean, "1234567890 ~ true --> boolean"); - t.is (v40.get_bool (), false, "1234567890 ~ true --> false"); + Variant v40 = v4.operator_match(v0, task); + t.is(v40.type(), Variant::type_boolean, "1234567890 ~ true --> boolean"); + t.is(v40.get_bool(), false, "1234567890 ~ true --> false"); - Variant v41 = v4.operator_match (v1, task); - t.is (v41.type (), Variant::type_boolean, "1234567890 ~ 42 --> boolean"); - t.is (v41.get_bool (), false, "1234567890 ~ 42 --> false"); + Variant v41 = v4.operator_match(v1, task); + t.is(v41.type(), Variant::type_boolean, "1234567890 ~ 42 --> boolean"); + t.is(v41.get_bool(), false, "1234567890 ~ 42 --> false"); - Variant v42 = v4.operator_match (v2, task); - t.is (v42.type (), Variant::type_boolean, "1234567890 ~ 3.14 --> boolean"); - t.is (v42.get_bool (), false, "1234567890 ~ 3.14 --> false"); + Variant v42 = v4.operator_match(v2, task); + t.is(v42.type(), Variant::type_boolean, "1234567890 ~ 3.14 --> boolean"); + t.is(v42.get_bool(), false, "1234567890 ~ 3.14 --> false"); - Variant v43 = v4.operator_match (v3, task); - t.is (v43.type (), Variant::type_boolean, "1234567890 ~ 'foo' --> boolean"); - t.is (v43.get_bool (), false, "1234567890 ~ 'foo' --> false"); + Variant v43 = v4.operator_match(v3, task); + t.is(v43.type(), Variant::type_boolean, "1234567890 ~ 'foo' --> boolean"); + t.is(v43.get_bool(), false, "1234567890 ~ 'foo' --> false"); - Variant v44 = v4.operator_match (v4, task); - t.is (v44.type (), Variant::type_boolean, "1234567890 ~ 1234567890 --> boolean"); - t.is (v44.get_bool (), true, "1234567890 ~ 1234567890 --> true"); + Variant v44 = v4.operator_match(v4, task); + t.is(v44.type(), Variant::type_boolean, "1234567890 ~ 1234567890 --> boolean"); + t.is(v44.get_bool(), true, "1234567890 ~ 1234567890 --> true"); - Variant v45 = v4.operator_match (v5, task); - t.is (v45.type (), Variant::type_boolean, "1234567890 ~ 1200 --> boolean"); - t.is (v45.get_bool (), false, "1234567890 ~ 1200 --> false"); + Variant v45 = v4.operator_match(v5, task); + t.is(v45.type(), Variant::type_boolean, "1234567890 ~ 1200 --> boolean"); + t.is(v45.get_bool(), false, "1234567890 ~ 1200 --> false"); - Variant v50 = v5.operator_match (v0, task); - t.is (v50.type (), Variant::type_boolean, "1200 ~ true --> boolean"); - t.is (v50.get_bool (), false, "1200 ~ true --> false"); + Variant v50 = v5.operator_match(v0, task); + t.is(v50.type(), Variant::type_boolean, "1200 ~ true --> boolean"); + t.is(v50.get_bool(), false, "1200 ~ true --> false"); - Variant v51 = v5.operator_match (v1, task); - t.is (v51.type (), Variant::type_boolean, "1200 ~ 42 --> boolean"); - t.is (v51.get_bool (), false, "1200 ~ 42 --> false"); + Variant v51 = v5.operator_match(v1, task); + t.is(v51.type(), Variant::type_boolean, "1200 ~ 42 --> boolean"); + t.is(v51.get_bool(), false, "1200 ~ 42 --> false"); - Variant v52 = v5.operator_match (v2, task); - t.is (v52.type (), Variant::type_boolean, "1200 ~ 3.14 --> boolean"); - t.is (v52.get_bool (), false, "1200 ~ 3.14 --> false"); + Variant v52 = v5.operator_match(v2, task); + t.is(v52.type(), Variant::type_boolean, "1200 ~ 3.14 --> boolean"); + t.is(v52.get_bool(), false, "1200 ~ 3.14 --> false"); - Variant v53 = v5.operator_match (v3, task); - t.is (v53.type (), Variant::type_boolean, "1200 ~ 'foo' --> boolean"); - t.is (v53.get_bool (), false, "1200 ~ 'foo' --> false"); + Variant v53 = v5.operator_match(v3, task); + t.is(v53.type(), Variant::type_boolean, "1200 ~ 'foo' --> boolean"); + t.is(v53.get_bool(), false, "1200 ~ 'foo' --> false"); - Variant v54 = v5.operator_match (v4, task); - t.is (v04.type (), Variant::type_boolean, "1200 ~ 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "1200 ~ 1234567890 --> false"); + Variant v54 = v5.operator_match(v4, task); + t.is(v04.type(), Variant::type_boolean, "1200 ~ 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "1200 ~ 1234567890 --> false"); - Variant v55 = v5.operator_match (v5, task); - t.is (v55.type (), Variant::type_boolean, "1200 ~ 1200 --> boolean"); - t.is (v55.get_bool (), true, "1200 ~ 1200 --> true"); + Variant v55 = v5.operator_match(v5, task); + t.is(v55.type(), Variant::type_boolean, "1200 ~ 1200 --> boolean"); + t.is(v55.get_bool(), true, "1200 ~ 1200 --> true"); return 0; } diff --git a/test/variant_math.test.cpp b/test/variant_math.test.cpp index f247d4c69..417a13adc 100644 --- a/test/variant_math.test.cpp +++ b/test/variant_math.test.cpp @@ -27,20 +27,20 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include #define EPSILON 0.001 //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (1); +int main(int, char**) { + UnitTest t(1); - Variant v0 (10.0); - v0.sqrt (); - t.is (v0.get_real (), 3.1622, EPSILON, "math sqrt 10 -> 3.1622"); + Variant v0(10.0); + v0.sqrt(); + t.is(v0.get_real(), 3.1622, EPSILON, "math sqrt 10 -> 3.1622"); return 0; } diff --git a/test/variant_modulo.test.cpp b/test/variant_modulo.test.cpp index 1f0e53c52..90a6da82c 100644 --- a/test/variant_modulo.test.cpp +++ b/test/variant_modulo.test.cpp @@ -27,171 +27,299 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include #define EPSILON 0.0001 //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (40); +int main(int, char**) { + UnitTest t(40); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // boolean / boolean -> ERROR - try {Variant v00 = v0 % v0; t.fail ("true % true --> error");} - catch (...) {t.pass ("true % true --> error");} + try { + Variant v00 = v0 % v0; + t.fail("true % true --> error"); + } catch (...) { + t.pass("true % true --> error"); + } // boolean / integer -> ERROR - try {Variant v01 = v0 % v1; t.fail ("true % 42 --> error");} - catch (...) {t.pass ("true % 42 --> error");} + try { + Variant v01 = v0 % v1; + t.fail("true % 42 --> error"); + } catch (...) { + t.pass("true % 42 --> error"); + } // boolean / real -> ERROR - try {Variant v02 = v0 % v2; t.fail ("true % 3.14 --> error");} - catch (...) {t.pass ("true % 3.14 --> error");} + try { + Variant v02 = v0 % v2; + t.fail("true % 3.14 --> error"); + } catch (...) { + t.pass("true % 3.14 --> error"); + } // boolean / string -> ERROR - try {Variant v03 = v0 % v3; t.fail ("true % foo --> error");} - catch (...) {t.pass ("true % foo --> error");} + try { + Variant v03 = v0 % v3; + t.fail("true % foo --> error"); + } catch (...) { + t.pass("true % foo --> error"); + } // boolean / date -> ERROR - try {Variant v04 = v0 % v4; t.fail ("true % 1234567890 --> error");} - catch (...) {t.pass ("true % 1234567890 --> error");} + try { + Variant v04 = v0 % v4; + t.fail("true % 1234567890 --> error"); + } catch (...) { + t.pass("true % 1234567890 --> error"); + } // boolean / duration -> ERROR - try {Variant v05 = v0 % v5; t.fail ("true % 1200 --> error");} - catch (...) {t.pass ("true % 1200 --> error");} + try { + Variant v05 = v0 % v5; + t.fail("true % 1200 --> error"); + } catch (...) { + t.pass("true % 1200 --> error"); + } // integer / boolean -> ERROR - try {Variant v10 = v1 % v0; t.fail ("42 % true --> error");} - catch (...) {t.pass ("42 % true --> error");} + try { + Variant v10 = v1 % v0; + t.fail("42 % true --> error"); + } catch (...) { + t.pass("42 % true --> error"); + } // integer / integer -> integer Variant v11 = v1 % v1; - t.is (v11.type (), Variant::type_integer, "42 % 42 --> integer"); - t.is (v11.get_integer (), 0, "42 % 42 --> 0"); + t.is(v11.type(), Variant::type_integer, "42 % 42 --> integer"); + t.is(v11.get_integer(), 0, "42 % 42 --> 0"); // integer / real -> real Variant v12 = v1 % v2; - t.is (v12.type (), Variant::type_real, "42 % 3.14 --> real"); - t.is (v12.get_real (), 1.18, EPSILON, "42 % 3.14 --> 1.18"); + t.is(v12.type(), Variant::type_real, "42 % 3.14 --> real"); + t.is(v12.get_real(), 1.18, EPSILON, "42 % 3.14 --> 1.18"); // integer / string -> ERROR - try {Variant v13 = v1 % v3; t.fail ("42 % foo --> error");} - catch (...) {t.pass ("42 % foo --> error");} + try { + Variant v13 = v1 % v3; + t.fail("42 % foo --> error"); + } catch (...) { + t.pass("42 % foo --> error"); + } // integer / date -> ERROR - try {Variant v14 = v1 % v4; t.fail ("42 % 1234567890 --> error");} - catch (...) {t.pass ("42 % 1234567890 --> error");} + try { + Variant v14 = v1 % v4; + t.fail("42 % 1234567890 --> error"); + } catch (...) { + t.pass("42 % 1234567890 --> error"); + } // integer / duration -> duration - try {Variant v15 = v1 % v5; t.fail ("42 % 1200 --> error");} - catch (...) {t.pass ("42 % 1200 --> error");} + try { + Variant v15 = v1 % v5; + t.fail("42 % 1200 --> error"); + } catch (...) { + t.pass("42 % 1200 --> error"); + } // real / boolean -> ERROR - try {Variant v20 = v2 % v0; t.fail ("3.14 % true --> error");} - catch (...) {t.pass ("3.14 % true --> error");} + try { + Variant v20 = v2 % v0; + t.fail("3.14 % true --> error"); + } catch (...) { + t.pass("3.14 % true --> error"); + } // real / integer -> real Variant v21 = v2 % v1; - t.is (v21.type (), Variant::type_real, "3.14 % 42 --> real"); - t.is (v21.get_real (), 3.14, EPSILON, "3.14 % 42 --> 3.14"); + t.is(v21.type(), Variant::type_real, "3.14 % 42 --> real"); + t.is(v21.get_real(), 3.14, EPSILON, "3.14 % 42 --> 3.14"); // real / real -> real Variant v22 = v2 % v2; - t.is (v22.type (), Variant::type_real, "3.14 % 3.14 --> real"); - t.is (v22.get_real (), 0.0, EPSILON, "3.14 % 3.14 --> 0.0"); + t.is(v22.type(), Variant::type_real, "3.14 % 3.14 --> real"); + t.is(v22.get_real(), 0.0, EPSILON, "3.14 % 3.14 --> 0.0"); // real / string -> error - try {Variant v23 = v2 % v3; t.fail ("3.14 % foo --> error");} - catch (...) {t.pass ("3.14 % foo --> error");} + try { + Variant v23 = v2 % v3; + t.fail("3.14 % foo --> error"); + } catch (...) { + t.pass("3.14 % foo --> error"); + } // real / date -> error - try {Variant v24 = v2 % v4; t.fail ("3.14 % 1234567890 --> error");} - catch (...) {t.pass ("3.14 % 1234567890 --> error");} + try { + Variant v24 = v2 % v4; + t.fail("3.14 % 1234567890 --> error"); + } catch (...) { + t.pass("3.14 % 1234567890 --> error"); + } // real / duration -> duration - try {Variant v25 = v2 % v5; t.fail ("3.14 % 1200 --> error");} - catch (...) {t.pass ("3.14 % 1200 --> error");} + try { + Variant v25 = v2 % v5; + t.fail("3.14 % 1200 --> error"); + } catch (...) { + t.pass("3.14 % 1200 --> error"); + } // string / boolean -> ERROR - try {Variant v30 = v3 % v0; t.fail ("foo % true --> error");} - catch (...) {t.pass ("foo % true --> error");} + try { + Variant v30 = v3 % v0; + t.fail("foo % true --> error"); + } catch (...) { + t.pass("foo % true --> error"); + } // string / integer -> ERROR - try {Variant v31 = v3 % v1; t.fail ("foo % 42 --> error");} - catch (...) {t.pass ("foo % 42 --> error");} + try { + Variant v31 = v3 % v1; + t.fail("foo % 42 --> error"); + } catch (...) { + t.pass("foo % 42 --> error"); + } // string / real -> ERROR - try {Variant v32 = v3 % v2; t.fail ("foo % 3.14 --> error");} - catch (...) {t.pass ("foo % 3.14 --> error");} + try { + Variant v32 = v3 % v2; + t.fail("foo % 3.14 --> error"); + } catch (...) { + t.pass("foo % 3.14 --> error"); + } // string / string -> ERROR - try {Variant v33 = v3 % v3; t.fail ("foo % foo --> error");} - catch (...) {t.pass ("foo % foo --> error");} + try { + Variant v33 = v3 % v3; + t.fail("foo % foo --> error"); + } catch (...) { + t.pass("foo % foo --> error"); + } // string / date -> ERROR - try {Variant v34 = v3 % v4; t.fail ("foo % 1234567890 --> error");} - catch (...) {t.pass ("foo % 1234567890 --> error");} + try { + Variant v34 = v3 % v4; + t.fail("foo % 1234567890 --> error"); + } catch (...) { + t.pass("foo % 1234567890 --> error"); + } // string / duration -> ERROR - try {Variant v35 = v3 % v5; t.fail ("foo % 1200 --> error");} - catch (...) {t.pass ("foo % 1200 --> error");} + try { + Variant v35 = v3 % v5; + t.fail("foo % 1200 --> error"); + } catch (...) { + t.pass("foo % 1200 --> error"); + } // date / boolean -> ERROR - try {Variant v40 = v4 % v0; t.fail ("1234567890 % true --> error");} - catch (...) {t.pass ("1234567890 % true --> error");} + try { + Variant v40 = v4 % v0; + t.fail("1234567890 % true --> error"); + } catch (...) { + t.pass("1234567890 % true --> error"); + } // date / integer -> ERROR - try {Variant v41 = v4 % v1; t.fail ("1234567890 % 42 --> error");} - catch (...) {t.pass ("1234567890 % 42 --> error");} + try { + Variant v41 = v4 % v1; + t.fail("1234567890 % 42 --> error"); + } catch (...) { + t.pass("1234567890 % 42 --> error"); + } // date / real -> ERROR - try {Variant v42 = v4 % v2; t.fail ("1234567890 % 3.14 --> error");} - catch (...) {t.pass ("1234567890 % 3.14 --> error");} + try { + Variant v42 = v4 % v2; + t.fail("1234567890 % 3.14 --> error"); + } catch (...) { + t.pass("1234567890 % 3.14 --> error"); + } // date / string -> ERROR - try {Variant v43 = v4 % v3; t.fail ("1234567890 % foo --> error");} - catch (...) {t.pass ("1234567890 % foo --> error");} + try { + Variant v43 = v4 % v3; + t.fail("1234567890 % foo --> error"); + } catch (...) { + t.pass("1234567890 % foo --> error"); + } // date / date -> ERROR - try {Variant v44 = v4 % v4; t.fail ("1234567890 % 1234567890 --> error");} - catch (...) {t.pass ("1234567890 % 1234567890 --> error");} + try { + Variant v44 = v4 % v4; + t.fail("1234567890 % 1234567890 --> error"); + } catch (...) { + t.pass("1234567890 % 1234567890 --> error"); + } // date / duration -> ERROR - try {Variant v45 = v4 % v5; t.fail ("1234567890 % 1200 --> error");} - catch (...) {t.pass ("1234567890 % 1200 --> error");} + try { + Variant v45 = v4 % v5; + t.fail("1234567890 % 1200 --> error"); + } catch (...) { + t.pass("1234567890 % 1200 --> error"); + } // duration / boolean -> ERROR - try {Variant v50 = v5 % v0; t.fail ("1200 % true --> error");} - catch (...) {t.pass ("1200 % true --> error");} + try { + Variant v50 = v5 % v0; + t.fail("1200 % true --> error"); + } catch (...) { + t.pass("1200 % true --> error"); + } // duration / integer -> ERROR - try {Variant v51 = v5 % v1; t.fail ("1200 % 42 --> error");} - catch (...) {t.pass ("1200 % 42 --> error");} + try { + Variant v51 = v5 % v1; + t.fail("1200 % 42 --> error"); + } catch (...) { + t.pass("1200 % 42 --> error"); + } // duration / real -> ERROR - try {Variant v52 = v5 % v2; t.fail ("1200 % 3.14 --> error");} - catch (...) {t.pass ("1200 % 3.14 --> error");} + try { + Variant v52 = v5 % v2; + t.fail("1200 % 3.14 --> error"); + } catch (...) { + t.pass("1200 % 3.14 --> error"); + } // duration / string -> ERROR - try {Variant v53 = v5 % v3; t.fail ("1200 % foo --> error");} - catch (...) {t.pass ("1200 % foo --> error");} + try { + Variant v53 = v5 % v3; + t.fail("1200 % foo --> error"); + } catch (...) { + t.pass("1200 % foo --> error"); + } // duration / date -> ERROR - try {Variant v54 = v5 % v4; t.fail ("1200 % 1234567890 --> error");} - catch (...) {t.pass ("1200 % 1234567890 --> error");} + try { + Variant v54 = v5 % v4; + t.fail("1200 % 1234567890 --> error"); + } catch (...) { + t.pass("1200 % 1234567890 --> error"); + } // duration / duration -> ERROR - try {Variant v55 = v5 % v5; t.fail ("1200 % 1200 --> error");} - catch (...) {t.pass ("1200 % 1200 --> error");} + try { + Variant v55 = v5 % v5; + t.fail("1200 % 1200 --> error"); + } catch (...) { + t.pass("1200 % 1200 --> error"); + } return 0; } diff --git a/test/variant_multiply.test.cpp b/test/variant_multiply.test.cpp index 6dcb20159..6d253ecbe 100644 --- a/test/variant_multiply.test.cpp +++ b/test/variant_multiply.test.cpp @@ -27,186 +27,256 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include #define EPSILON 0.0001 //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (54); +int main(int, char**) { + UnitTest t(54); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // boolean * boolean -> ERROR - try {Variant v00 = v0 * v0; t.fail ("true * true --> error");} - catch (...) {t.pass ("true * true --> error");} + try { + Variant v00 = v0 * v0; + t.fail("true * true --> error"); + } catch (...) { + t.pass("true * true --> error"); + } // boolean * integer -> integer Variant v01 = v0 * v1; - t.is (v01.type (), Variant::type_integer, "true * 42 --> integer"); - t.is (v01.get_integer (), 42, "true * 42 --> 42"); + t.is(v01.type(), Variant::type_integer, "true * 42 --> integer"); + t.is(v01.get_integer(), 42, "true * 42 --> 42"); // boolean * real -> real Variant v02 = v0 * v2; - t.is (v02.type (), Variant::type_real, "true * 3.14 --> real"); - t.is (v02.get_real (), 3.14, EPSILON, "true * 3.14 --> 3.14"); + t.is(v02.type(), Variant::type_real, "true * 3.14 --> real"); + t.is(v02.get_real(), 3.14, EPSILON, "true * 3.14 --> 3.14"); // boolean * string -> string Variant v03 = v0 * v3; - t.is (v03.type (), Variant::type_string, "true * foo --> real"); - t.is (v03.get_string (), "foo", "true * foo --> foo"); + t.is(v03.type(), Variant::type_string, "true * foo --> real"); + t.is(v03.get_string(), "foo", "true * foo --> foo"); // boolean * date -> ERROR - try {Variant v04 = v0 * v4; t.fail ("true * 1234567890 --> error");} - catch (...) {t.pass ("true * 1234567890 --> error");} + try { + Variant v04 = v0 * v4; + t.fail("true * 1234567890 --> error"); + } catch (...) { + t.pass("true * 1234567890 --> error"); + } // boolean * duration -> duration Variant v05 = v0 * v5; - t.is (v05.type (), Variant::type_duration, "true * 1200 --> duration"); - t.is (v05.get_duration (), 1200, "true * 1200 --> 1200"); + t.is(v05.type(), Variant::type_duration, "true * 1200 --> duration"); + t.is(v05.get_duration(), 1200, "true * 1200 --> 1200"); // integer * boolean -> integer Variant v10 = v1 * v0; - t.is (v10.type (), Variant::type_integer, "42 * true --> integer"); - t.is (v10.get_integer (), 42, "42 * true --> 42"); + t.is(v10.type(), Variant::type_integer, "42 * true --> integer"); + t.is(v10.get_integer(), 42, "42 * true --> 42"); // integer * integer -> integer Variant v11 = v1 * v1; - t.is (v11.type (), Variant::type_integer, "42 * 42 --> integer"); - t.is (v11.get_integer (), 1764, "42 * 42 --> 1764"); + t.is(v11.type(), Variant::type_integer, "42 * 42 --> integer"); + t.is(v11.get_integer(), 1764, "42 * 42 --> 1764"); // integer * real -> real Variant v12 = v1 * v2; - t.is (v12.type (), Variant::type_real, "42 * 3.14 --> real"); - t.is (v12.get_real (), 131.88, EPSILON, "42 * 3.14 --> 131.88"); + t.is(v12.type(), Variant::type_real, "42 * 3.14 --> real"); + t.is(v12.get_real(), 131.88, EPSILON, "42 * 3.14 --> 131.88"); // integer * string -> string Variant v13 = v1 * v3; - t.is (v13.type (), Variant::type_string, "42 * foo --> string"); - t.is (v13.get_string ().substr (0, 10), "foofoofoof", - "42 * foo --> foofoofoofoo..."); + t.is(v13.type(), Variant::type_string, "42 * foo --> string"); + t.is(v13.get_string().substr(0, 10), "foofoofoof", "42 * foo --> foofoofoofoo..."); // integer * date -> error - try {Variant v14 = v1 * v4; t.fail ("42 * 1234567890 --> error");} - catch (...) {t.pass ("42 * 1234567890 --> error");} + try { + Variant v14 = v1 * v4; + t.fail("42 * 1234567890 --> error"); + } catch (...) { + t.pass("42 * 1234567890 --> error"); + } // integer * duration -> duration Variant v15 = v1 * v5; - t.is (v15.type (), Variant::type_duration, "42 * 1200 --> duration"); - t.is (v15.get_duration (), 50400, "42 * 1200 --> 50400"); + t.is(v15.type(), Variant::type_duration, "42 * 1200 --> duration"); + t.is(v15.get_duration(), 50400, "42 * 1200 --> 50400"); // real * boolean -> real Variant v20 = v2 * v0; - t.is (v20.type (), Variant::type_real, "3.14 * true --> real"); - t.is (v20.get_real (), 3.14, EPSILON, "3.14 * true --> 3.14"); + t.is(v20.type(), Variant::type_real, "3.14 * true --> real"); + t.is(v20.get_real(), 3.14, EPSILON, "3.14 * true --> 3.14"); // real * integer -> real Variant v21 = v2 * v1; - t.is (v21.type (), Variant::type_real, "3.14 * 42 --> real"); - t.is (v21.get_real (), 131.88, EPSILON, "3.14 * 42 --> 131.88"); + t.is(v21.type(), Variant::type_real, "3.14 * 42 --> real"); + t.is(v21.get_real(), 131.88, EPSILON, "3.14 * 42 --> 131.88"); // real * real -> real Variant v22 = v2 * v2; - t.is (v22.type (), Variant::type_real, "3.14 * 3.14 --> real"); - t.is (v22.get_real (), 9.8596, EPSILON, "3.14 * 3.14 --> 9.8596"); + t.is(v22.type(), Variant::type_real, "3.14 * 3.14 --> real"); + t.is(v22.get_real(), 9.8596, EPSILON, "3.14 * 3.14 --> 9.8596"); // real * string -> error - try {Variant v23 = v2 * v3; t.fail ("3.14 * foo --> error");} - catch (...) {t.pass ("3.14 * foo --> error");} + try { + Variant v23 = v2 * v3; + t.fail("3.14 * foo --> error"); + } catch (...) { + t.pass("3.14 * foo --> error"); + } // real * date -> error - try {Variant v24 = v2 * v4; t.fail ("3.14 * 1234567890 --> error");} - catch (...) {t.pass ("3.14 * 1234567890 --> error");} + try { + Variant v24 = v2 * v4; + t.fail("3.14 * 1234567890 --> error"); + } catch (...) { + t.pass("3.14 * 1234567890 --> error"); + } // real * duration -> duration Variant v25 = v2 * v5; - t.is (v25.type (), Variant::type_duration, "3.14 * 1200 --> duration"); - t.is (v25.get_duration (), 3768, "3.14 * 1200 --> 3768"); + t.is(v25.type(), Variant::type_duration, "3.14 * 1200 --> duration"); + t.is(v25.get_duration(), 3768, "3.14 * 1200 --> 3768"); // string * boolean -> string Variant v30 = v3 * v0; - t.is (v30.type (), Variant::type_string, "foo * true --> real"); - t.is (v30.get_string (), "foo", "foo * true --> foo"); + t.is(v30.type(), Variant::type_string, "foo * true --> real"); + t.is(v30.get_string(), "foo", "foo * true --> foo"); // string * integer -> string Variant v31 = v3 * v1; - t.is (v31.type (), Variant::type_string, "foo * 42 --> string"); - t.is (v31.get_string ().substr (0, 10), "foofoofoof", - "foo * 42 --> foofoofoof..."); + t.is(v31.type(), Variant::type_string, "foo * 42 --> string"); + t.is(v31.get_string().substr(0, 10), "foofoofoof", "foo * 42 --> foofoofoof..."); // string * real -> string - try {Variant v32 = v3 * v2; t.fail ("foo * 3.14 --> error");} - catch (...) {t.pass ("foo * 3.14 --> error");} + try { + Variant v32 = v3 * v2; + t.fail("foo * 3.14 --> error"); + } catch (...) { + t.pass("foo * 3.14 --> error"); + } // string * string -> string - try {Variant v33 = v3 * v3; t.fail ("foo * foo --> error");} - catch (...) {t.pass ("foo * foo --> error");} + try { + Variant v33 = v3 * v3; + t.fail("foo * foo --> error"); + } catch (...) { + t.pass("foo * foo --> error"); + } // string * date -> string - try {Variant v34 = v3 * v4; t.fail ("foo * 1234567890 --> error");} - catch (...) {t.pass ("foo * 1234567890 --> error");} + try { + Variant v34 = v3 * v4; + t.fail("foo * 1234567890 --> error"); + } catch (...) { + t.pass("foo * 1234567890 --> error"); + } // string * duration -> string - try {Variant v35 = v3 * v5; t.fail ("foo * 1200 --> error");} - catch (...) {t.pass ("foo * 1200 --> error");} + try { + Variant v35 = v3 * v5; + t.fail("foo * 1200 --> error"); + } catch (...) { + t.pass("foo * 1200 --> error"); + } // date * boolean -> ERROR - try {Variant v40 = v4 * v0; t.fail ("1234567890 * true --> error");} - catch (...) {t.pass ("1234567890 * true --> error");} + try { + Variant v40 = v4 * v0; + t.fail("1234567890 * true --> error"); + } catch (...) { + t.pass("1234567890 * true --> error"); + } // date * integer -> ERROR - try {Variant v41 = v4 * v1; t.fail ("1234567890 * 42 --> error");} - catch (...) {t.pass ("1234567890 * 42 --> error");} + try { + Variant v41 = v4 * v1; + t.fail("1234567890 * 42 --> error"); + } catch (...) { + t.pass("1234567890 * 42 --> error"); + } // date * real -> ERROR - try {Variant v42 = v4 * v2; t.fail ("1234567890 * 3.14 --> error");} - catch (...) {t.pass ("1234567890 * 3.14 --> error");} + try { + Variant v42 = v4 * v2; + t.fail("1234567890 * 3.14 --> error"); + } catch (...) { + t.pass("1234567890 * 3.14 --> error"); + } // date * string -> ERROR - try {Variant v43 = v4 * v3; t.fail ("1234567890 * foo --> error");} - catch (...) {t.pass ("1234567890 * foo --> error");} + try { + Variant v43 = v4 * v3; + t.fail("1234567890 * foo --> error"); + } catch (...) { + t.pass("1234567890 * foo --> error"); + } // date * date -> ERROR - try {Variant v44 = v4 * v4; t.fail ("1234567890 * 1234567890 --> error");} - catch (...) {t.pass ("1234567890 * 1234567890 --> error");} + try { + Variant v44 = v4 * v4; + t.fail("1234567890 * 1234567890 --> error"); + } catch (...) { + t.pass("1234567890 * 1234567890 --> error"); + } // date * duration -> ERROR - try {Variant v45 = v4 * v5; t.fail ("1234567890 * 1200 --> error");} - catch (...) {t.pass ("1234567890 * 1200 --> error");} + try { + Variant v45 = v4 * v5; + t.fail("1234567890 * 1200 --> error"); + } catch (...) { + t.pass("1234567890 * 1200 --> error"); + } // duration * boolean -> duration Variant v50 = v5 * v0; - t.is (v50.type (), Variant::type_duration, "1200 * true --> duration"); - t.is (v50.get_duration (), 1200, "1200 * true --> 1200"); + t.is(v50.type(), Variant::type_duration, "1200 * true --> duration"); + t.is(v50.get_duration(), 1200, "1200 * true --> 1200"); // duration * integer -> duration Variant v51 = v5 * v1; - t.is (v51.type (), Variant::type_duration, "1200 * 42 --> duration"); - t.is (v51.get_duration (), 50400, "1200 * 42 --> 50400"); + t.is(v51.type(), Variant::type_duration, "1200 * 42 --> duration"); + t.is(v51.get_duration(), 50400, "1200 * 42 --> 50400"); // duration * real -> duration Variant v52 = v5 * v2; - t.is (v52.type (), Variant::type_duration, "1200 * 3.14 --> duration"); - t.is (v52.get_duration (), 3768, "1200 * 3.14 --> 3768"); + t.is(v52.type(), Variant::type_duration, "1200 * 3.14 --> duration"); + t.is(v52.get_duration(), 3768, "1200 * 3.14 --> 3768"); // duration * string -> string - try {Variant v53 = v5 * v3; t.fail ("1200 * foo --> error");} - catch (...) {t.pass ("1200 * foo --> error");} + try { + Variant v53 = v5 * v3; + t.fail("1200 * foo --> error"); + } catch (...) { + t.pass("1200 * foo --> error"); + } // duration * date -> date - try {Variant v54 = v5 * v4; t.fail ("1200 * 1234567890 --> error");} - catch (...) {t.pass ("1200 * 1234567890 --> error");} + try { + Variant v54 = v5 * v4; + t.fail("1200 * 1234567890 --> error"); + } catch (...) { + t.pass("1200 * 1234567890 --> error"); + } // duration * duration -> duration - try {Variant v55 = v5 * v5; t.fail ("1200 * 1200 --> error");} - catch (...) {t.pass ("1200 * 1200 --> error");} + try { + Variant v55 = v5 * v5; + t.fail("1200 * 1200 --> error"); + } catch (...) { + t.pass("1200 * 1200 --> error"); + } return 0; } diff --git a/test/variant_nomatch.test.cpp b/test/variant_nomatch.test.cpp index 0f069f9d9..827facb25 100644 --- a/test/variant_nomatch.test.cpp +++ b/test/variant_nomatch.test.cpp @@ -27,271 +27,271 @@ #include // cmake.h include header must come first -#include -#include -#include #include +#include +#include + +#include Task task; //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (120); +int main(int, char**) { + UnitTest t(120); - Variant vs0 ("untrue"); // !~ true - Variant vs1 (8421); // !~ 42 - Variant vs2 (3.14159); // !~ 3.14 - Variant vs3 ("foolish"); // !~ foo + Variant vs0("untrue"); // !~ true + Variant vs1(8421); // !~ 42 + Variant vs2(3.14159); // !~ 3.14 + Variant vs3("foolish"); // !~ foo - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // Interesting cases. - Variant vs00 = vs0.operator_nomatch (v0, task); - t.is (vs00.type (), Variant::type_boolean, "untrue !~ true --> boolean"); - t.is (vs00.get_bool (), false, "untrue !~ true --> false"); + Variant vs00 = vs0.operator_nomatch(v0, task); + t.is(vs00.type(), Variant::type_boolean, "untrue !~ true --> boolean"); + t.is(vs00.get_bool(), false, "untrue !~ true --> false"); - Variant vs01 = vs0.operator_nomatch (v1, task); - t.is (vs01.type (), Variant::type_boolean, "untrue !~ 42 --> boolean"); - t.is (vs01.get_bool (), true, "untrue !~ 42 --> true"); + Variant vs01 = vs0.operator_nomatch(v1, task); + t.is(vs01.type(), Variant::type_boolean, "untrue !~ 42 --> boolean"); + t.is(vs01.get_bool(), true, "untrue !~ 42 --> true"); - Variant vs02 = vs0.operator_nomatch (v2, task); - t.is (vs02.type (), Variant::type_boolean, "untrue !~ 3.14 --> boolean"); - t.is (vs02.get_bool (), true, "untrue !~ 3.14 --> true"); + Variant vs02 = vs0.operator_nomatch(v2, task); + t.is(vs02.type(), Variant::type_boolean, "untrue !~ 3.14 --> boolean"); + t.is(vs02.get_bool(), true, "untrue !~ 3.14 --> true"); - Variant vs03 = vs0.operator_nomatch (v3, task); - t.is (vs03.type (), Variant::type_boolean, "untrue !~ 'foo' --> boolean"); - t.is (vs03.get_bool (), true, "untrue !~ 'foo' --> true"); + Variant vs03 = vs0.operator_nomatch(v3, task); + t.is(vs03.type(), Variant::type_boolean, "untrue !~ 'foo' --> boolean"); + t.is(vs03.get_bool(), true, "untrue !~ 'foo' --> true"); - Variant vs04 = vs0.operator_nomatch (v4, task); - t.is (vs04.type (), Variant::type_boolean, "untrue !~ 1234567890 --> boolean"); - t.is (vs04.get_bool (), true, "untrue !~ 1234567890 --> true"); + Variant vs04 = vs0.operator_nomatch(v4, task); + t.is(vs04.type(), Variant::type_boolean, "untrue !~ 1234567890 --> boolean"); + t.is(vs04.get_bool(), true, "untrue !~ 1234567890 --> true"); - Variant vs05 = vs0.operator_nomatch (v5, task); - t.is (vs05.type (), Variant::type_boolean, "untrue !~ 1200 --> boolean"); - t.is (vs05.get_bool (), true, "untrue !~ 1200 --> true"); + Variant vs05 = vs0.operator_nomatch(v5, task); + t.is(vs05.type(), Variant::type_boolean, "untrue !~ 1200 --> boolean"); + t.is(vs05.get_bool(), true, "untrue !~ 1200 --> true"); - Variant vs10 = vs1.operator_nomatch (v0, task); - t.is (vs10.type (), Variant::type_boolean, "8421 !~ true --> boolean"); - t.is (vs10.get_bool (), true, "8421 !~ true --> true"); + Variant vs10 = vs1.operator_nomatch(v0, task); + t.is(vs10.type(), Variant::type_boolean, "8421 !~ true --> boolean"); + t.is(vs10.get_bool(), true, "8421 !~ true --> true"); - Variant vs11 = vs1.operator_nomatch (v1, task); - t.is (vs11.type (), Variant::type_boolean, "8421 !~ 42 --> boolean"); - t.is (vs11.get_bool (), false, "8421 !~ 42 --> false"); + Variant vs11 = vs1.operator_nomatch(v1, task); + t.is(vs11.type(), Variant::type_boolean, "8421 !~ 42 --> boolean"); + t.is(vs11.get_bool(), false, "8421 !~ 42 --> false"); - Variant vs12 = vs1.operator_nomatch (v2, task); - t.is (vs12.type (), Variant::type_boolean, "8421 !~ 3.14 --> boolean"); - t.is (vs12.get_bool (), true, "8421 !~ 3.14 --> true"); + Variant vs12 = vs1.operator_nomatch(v2, task); + t.is(vs12.type(), Variant::type_boolean, "8421 !~ 3.14 --> boolean"); + t.is(vs12.get_bool(), true, "8421 !~ 3.14 --> true"); - Variant vs13 = vs1.operator_nomatch (v3, task); - t.is (vs13.type (), Variant::type_boolean, "8421 !~ 'foo' --> boolean"); - t.is (vs13.get_bool (), true, "8421 !~ 'foo' --> true"); + Variant vs13 = vs1.operator_nomatch(v3, task); + t.is(vs13.type(), Variant::type_boolean, "8421 !~ 'foo' --> boolean"); + t.is(vs13.get_bool(), true, "8421 !~ 'foo' --> true"); - Variant vs14 = vs1.operator_nomatch (v4, task); - t.is (vs14.type (), Variant::type_boolean, "8421 !~ 1234567890 --> boolean"); - t.is (vs14.get_bool (), true, "8421 !~ 1234567890 --> true"); + Variant vs14 = vs1.operator_nomatch(v4, task); + t.is(vs14.type(), Variant::type_boolean, "8421 !~ 1234567890 --> boolean"); + t.is(vs14.get_bool(), true, "8421 !~ 1234567890 --> true"); - Variant vs15 = vs1.operator_nomatch (v5, task); - t.is (vs15.type (), Variant::type_boolean, "8421 !~ 1200 --> boolean"); - t.is (vs15.get_bool (), true, "8421 !~ 1200 --> true"); + Variant vs15 = vs1.operator_nomatch(v5, task); + t.is(vs15.type(), Variant::type_boolean, "8421 !~ 1200 --> boolean"); + t.is(vs15.get_bool(), true, "8421 !~ 1200 --> true"); - Variant vs20 = vs2.operator_nomatch (v0, task); - t.is (vs20.type (), Variant::type_boolean, "3.14159 !~ true --> boolean"); - t.is (vs20.get_bool (), true, "3.14159 !~ true --> true"); + Variant vs20 = vs2.operator_nomatch(v0, task); + t.is(vs20.type(), Variant::type_boolean, "3.14159 !~ true --> boolean"); + t.is(vs20.get_bool(), true, "3.14159 !~ true --> true"); - Variant vs21 = vs2.operator_nomatch (v1, task); - t.is (vs21.type (), Variant::type_boolean, "3.14159 !~ 42 --> boolean"); - t.is (vs21.get_bool (), true, "3.14159 !~ 42 --> true"); + Variant vs21 = vs2.operator_nomatch(v1, task); + t.is(vs21.type(), Variant::type_boolean, "3.14159 !~ 42 --> boolean"); + t.is(vs21.get_bool(), true, "3.14159 !~ 42 --> true"); - Variant vs22 = vs2.operator_nomatch (v2, task); - t.is (vs22.type (), Variant::type_boolean, "3.14159 !~ 3.14 --> boolean"); - t.is (vs22.get_bool (), false, "3.14159 !~ 3.14 --> false"); + Variant vs22 = vs2.operator_nomatch(v2, task); + t.is(vs22.type(), Variant::type_boolean, "3.14159 !~ 3.14 --> boolean"); + t.is(vs22.get_bool(), false, "3.14159 !~ 3.14 --> false"); - Variant vs23 = vs2.operator_nomatch (v3, task); - t.is (vs23.type (), Variant::type_boolean, "3.14159 !~ 'foo' --> boolean"); - t.is (vs23.get_bool (), true, "3.14159 !~ 'foo' --> true"); + Variant vs23 = vs2.operator_nomatch(v3, task); + t.is(vs23.type(), Variant::type_boolean, "3.14159 !~ 'foo' --> boolean"); + t.is(vs23.get_bool(), true, "3.14159 !~ 'foo' --> true"); - Variant vs24 = vs2.operator_nomatch (v4, task); - t.is (vs24.type (), Variant::type_boolean, "3.14159 !~ 1234567890 --> boolean"); - t.is (vs24.get_bool (), true, "3.14159 !~ 1234567890 --> true"); + Variant vs24 = vs2.operator_nomatch(v4, task); + t.is(vs24.type(), Variant::type_boolean, "3.14159 !~ 1234567890 --> boolean"); + t.is(vs24.get_bool(), true, "3.14159 !~ 1234567890 --> true"); - Variant vs25 = vs2.operator_nomatch (v5, task); - t.is (vs25.type (), Variant::type_boolean, "3.14159 !~ 1200 --> boolean"); - t.is (vs25.get_bool (), true, "3.14159 !~ 1200 --> true"); + Variant vs25 = vs2.operator_nomatch(v5, task); + t.is(vs25.type(), Variant::type_boolean, "3.14159 !~ 1200 --> boolean"); + t.is(vs25.get_bool(), true, "3.14159 !~ 1200 --> true"); - Variant vs30 = vs3.operator_nomatch (v0, task); - t.is (vs30.type (), Variant::type_boolean, "foolish !~ true --> boolean"); - t.is (vs30.get_bool (), true, "foolish !~ true --> true"); + Variant vs30 = vs3.operator_nomatch(v0, task); + t.is(vs30.type(), Variant::type_boolean, "foolish !~ true --> boolean"); + t.is(vs30.get_bool(), true, "foolish !~ true --> true"); - Variant vs31 = vs3.operator_nomatch (v1, task); - t.is (vs31.type (), Variant::type_boolean, "foolish !~ 42 --> boolean"); - t.is (vs31.get_bool (), true, "foolish !~ 42 --> true"); + Variant vs31 = vs3.operator_nomatch(v1, task); + t.is(vs31.type(), Variant::type_boolean, "foolish !~ 42 --> boolean"); + t.is(vs31.get_bool(), true, "foolish !~ 42 --> true"); - Variant vs32 = vs3.operator_nomatch (v2, task); - t.is (vs32.type (), Variant::type_boolean, "foolish !~ 3.14 --> boolean"); - t.is (vs32.get_bool (), true, "foolish !~ 3.14 --> true"); + Variant vs32 = vs3.operator_nomatch(v2, task); + t.is(vs32.type(), Variant::type_boolean, "foolish !~ 3.14 --> boolean"); + t.is(vs32.get_bool(), true, "foolish !~ 3.14 --> true"); - Variant vs33 = vs3.operator_nomatch (v3, task); - t.is (vs33.type (), Variant::type_boolean, "foolish !~ 'foo' --> boolean"); - t.is (vs33.get_bool (), false, "foolish !~ 'foo' --> false"); + Variant vs33 = vs3.operator_nomatch(v3, task); + t.is(vs33.type(), Variant::type_boolean, "foolish !~ 'foo' --> boolean"); + t.is(vs33.get_bool(), false, "foolish !~ 'foo' --> false"); - Variant vs34 = vs3.operator_nomatch (v4, task); - t.is (vs34.type (), Variant::type_boolean, "foolish !~ 1234567890 --> boolean"); - t.is (vs34.get_bool (), true, "foolish !~ 1234567890 --> true"); + Variant vs34 = vs3.operator_nomatch(v4, task); + t.is(vs34.type(), Variant::type_boolean, "foolish !~ 1234567890 --> boolean"); + t.is(vs34.get_bool(), true, "foolish !~ 1234567890 --> true"); - Variant vs35 = vs3.operator_nomatch (v5, task); - t.is (vs35.type (), Variant::type_boolean, "foolish !~ 1200 --> boolean"); - t.is (vs35.get_bool (), true, "foolish !~ 1200 --> true"); + Variant vs35 = vs3.operator_nomatch(v5, task); + t.is(vs35.type(), Variant::type_boolean, "foolish !~ 1200 --> boolean"); + t.is(vs35.get_bool(), true, "foolish !~ 1200 --> true"); // Exhaustive comparisons. - Variant v00 = v0.operator_nomatch (v0, task); - t.is (v00.type (), Variant::type_boolean, "true !~ true --> boolean"); - t.is (v00.get_bool (), false, "true !~ true --> false"); + Variant v00 = v0.operator_nomatch(v0, task); + t.is(v00.type(), Variant::type_boolean, "true !~ true --> boolean"); + t.is(v00.get_bool(), false, "true !~ true --> false"); - Variant v01 = v0.operator_nomatch (v1, task); - t.is (v01.type (), Variant::type_boolean, "true !~ 42 --> boolean"); - t.is (v01.get_bool (), true, "true !~ 42 --> true"); + Variant v01 = v0.operator_nomatch(v1, task); + t.is(v01.type(), Variant::type_boolean, "true !~ 42 --> boolean"); + t.is(v01.get_bool(), true, "true !~ 42 --> true"); - Variant v02 = v0.operator_nomatch (v2, task); - t.is (v02.type (), Variant::type_boolean, "true !~ 3.14 --> boolean"); - t.is (v02.get_bool (), true, "true !~ 3.14 --> true"); + Variant v02 = v0.operator_nomatch(v2, task); + t.is(v02.type(), Variant::type_boolean, "true !~ 3.14 --> boolean"); + t.is(v02.get_bool(), true, "true !~ 3.14 --> true"); - Variant v03 = v0.operator_nomatch (v3, task); - t.is (v03.type (), Variant::type_boolean, "true !~ 'foo' --> boolean"); - t.is (v03.get_bool (), true, "true !~ 'foo' --> true"); + Variant v03 = v0.operator_nomatch(v3, task); + t.is(v03.type(), Variant::type_boolean, "true !~ 'foo' --> boolean"); + t.is(v03.get_bool(), true, "true !~ 'foo' --> true"); - Variant v04 = v0.operator_nomatch (v4, task); - t.is (v04.type (), Variant::type_boolean, "true !~ 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "true !~ 1234567890 --> true"); + Variant v04 = v0.operator_nomatch(v4, task); + t.is(v04.type(), Variant::type_boolean, "true !~ 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "true !~ 1234567890 --> true"); - Variant v05 = v0.operator_nomatch (v5, task); - t.is (v05.type (), Variant::type_boolean, "true !~ 1200 --> boolean"); - t.is (v05.get_bool (), true, "true !~ 1200 --> true"); + Variant v05 = v0.operator_nomatch(v5, task); + t.is(v05.type(), Variant::type_boolean, "true !~ 1200 --> boolean"); + t.is(v05.get_bool(), true, "true !~ 1200 --> true"); - Variant v10 = v1.operator_nomatch (v0, task); - t.is (v10.type (), Variant::type_boolean, "42 !~ true --> boolean"); - t.is (v10.get_bool (), true, "42 !~ true --> true"); + Variant v10 = v1.operator_nomatch(v0, task); + t.is(v10.type(), Variant::type_boolean, "42 !~ true --> boolean"); + t.is(v10.get_bool(), true, "42 !~ true --> true"); - Variant v11 = v1.operator_nomatch (v1, task); - t.is (v11.type (), Variant::type_boolean, "42 !~ 42 --> boolean"); - t.is (v11.get_bool (), false, "42 !~ 42 --> false"); + Variant v11 = v1.operator_nomatch(v1, task); + t.is(v11.type(), Variant::type_boolean, "42 !~ 42 --> boolean"); + t.is(v11.get_bool(), false, "42 !~ 42 --> false"); - Variant v12 = v1.operator_nomatch (v2, task); - t.is (v12.type (), Variant::type_boolean, "42 !~ 3.14 --> boolean"); - t.is (v12.get_bool (), true, "42 !~ 3.14 --> true"); + Variant v12 = v1.operator_nomatch(v2, task); + t.is(v12.type(), Variant::type_boolean, "42 !~ 3.14 --> boolean"); + t.is(v12.get_bool(), true, "42 !~ 3.14 --> true"); - Variant v13 = v1.operator_nomatch (v3, task); - t.is (v13.type (), Variant::type_boolean, "42 !~ 'foo' --> boolean"); - t.is (v13.get_bool (), true, "42 !~ 'foo' --> true"); + Variant v13 = v1.operator_nomatch(v3, task); + t.is(v13.type(), Variant::type_boolean, "42 !~ 'foo' --> boolean"); + t.is(v13.get_bool(), true, "42 !~ 'foo' --> true"); - Variant v14 = v1.operator_nomatch (v4, task); - t.is (v04.type (), Variant::type_boolean, "42 !~ 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "42 !~ 1234567890 --> true"); + Variant v14 = v1.operator_nomatch(v4, task); + t.is(v04.type(), Variant::type_boolean, "42 !~ 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "42 !~ 1234567890 --> true"); - Variant v15 = v1.operator_nomatch (v5, task); - t.is (v15.type (), Variant::type_boolean, "42 !~ 1200 --> boolean"); - t.is (v15.get_bool (), true, "42 !~ 1200 --> true"); + Variant v15 = v1.operator_nomatch(v5, task); + t.is(v15.type(), Variant::type_boolean, "42 !~ 1200 --> boolean"); + t.is(v15.get_bool(), true, "42 !~ 1200 --> true"); - Variant v20 = v2.operator_nomatch (v0, task); - t.is (v20.type (), Variant::type_boolean, "3.14 !~ true --> boolean"); - t.is (v20.get_bool (), true, "3.14 !~ true --> true"); + Variant v20 = v2.operator_nomatch(v0, task); + t.is(v20.type(), Variant::type_boolean, "3.14 !~ true --> boolean"); + t.is(v20.get_bool(), true, "3.14 !~ true --> true"); - Variant v21 = v2.operator_nomatch (v1, task); - t.is (v21.type (), Variant::type_boolean, "3.14 !~ 42 --> boolean"); - t.is (v21.get_bool (), true, "3.14 !~ 42 --> true"); + Variant v21 = v2.operator_nomatch(v1, task); + t.is(v21.type(), Variant::type_boolean, "3.14 !~ 42 --> boolean"); + t.is(v21.get_bool(), true, "3.14 !~ 42 --> true"); - Variant v22 = v2.operator_nomatch (v2, task); - t.is (v22.type (), Variant::type_boolean, "3.14 !~ 3.14 --> boolean"); - t.is (v22.get_bool (), false, "3.14 !~ 3.14 --> false"); + Variant v22 = v2.operator_nomatch(v2, task); + t.is(v22.type(), Variant::type_boolean, "3.14 !~ 3.14 --> boolean"); + t.is(v22.get_bool(), false, "3.14 !~ 3.14 --> false"); - Variant v23 = v2.operator_nomatch (v3, task); - t.is (v23.type (), Variant::type_boolean, "3.14 !~ 'foo' --> boolean"); - t.is (v23.get_bool (), true, "3.14 !~ 'foo' --> true"); + Variant v23 = v2.operator_nomatch(v3, task); + t.is(v23.type(), Variant::type_boolean, "3.14 !~ 'foo' --> boolean"); + t.is(v23.get_bool(), true, "3.14 !~ 'foo' --> true"); - Variant v24 = v2.operator_nomatch (v4, task); - t.is (v24.type (), Variant::type_boolean, "3.14 !~ 1234567890 --> boolean"); - t.is (v24.get_bool (), true, "3.14 !~ 1234567890 --> true"); + Variant v24 = v2.operator_nomatch(v4, task); + t.is(v24.type(), Variant::type_boolean, "3.14 !~ 1234567890 --> boolean"); + t.is(v24.get_bool(), true, "3.14 !~ 1234567890 --> true"); - Variant v25 = v2.operator_nomatch (v5, task); - t.is (v25.type (), Variant::type_boolean, "3.14 !~ 1200 --> boolean"); - t.is (v25.get_bool (), true, "3.14 !~ 1200 --> true"); + Variant v25 = v2.operator_nomatch(v5, task); + t.is(v25.type(), Variant::type_boolean, "3.14 !~ 1200 --> boolean"); + t.is(v25.get_bool(), true, "3.14 !~ 1200 --> true"); - Variant v30 = v3.operator_nomatch (v0, task); - t.is (v30.type (), Variant::type_boolean, "'foo' !~ true --> boolean"); - t.is (v30.get_bool (), true, "'foo' !~ true --> true"); + Variant v30 = v3.operator_nomatch(v0, task); + t.is(v30.type(), Variant::type_boolean, "'foo' !~ true --> boolean"); + t.is(v30.get_bool(), true, "'foo' !~ true --> true"); - Variant v31 = v3.operator_nomatch (v1, task); - t.is (v31.type (), Variant::type_boolean, "'foo' !~ 42 --> boolean"); - t.is (v31.get_bool (), true, "'foo' !~ 42 --> true"); + Variant v31 = v3.operator_nomatch(v1, task); + t.is(v31.type(), Variant::type_boolean, "'foo' !~ 42 --> boolean"); + t.is(v31.get_bool(), true, "'foo' !~ 42 --> true"); - Variant v32 = v3.operator_nomatch (v2, task); - t.is (v32.type (), Variant::type_boolean, "'foo' !~ 3.14 --> boolean"); - t.is (v32.get_bool (), true, "'foo' !~ 3.14 --> true"); + Variant v32 = v3.operator_nomatch(v2, task); + t.is(v32.type(), Variant::type_boolean, "'foo' !~ 3.14 --> boolean"); + t.is(v32.get_bool(), true, "'foo' !~ 3.14 --> true"); - Variant v33 = v3.operator_nomatch (v3, task); - t.is (v33.type (), Variant::type_boolean, "'foo' !~ 'foo' --> boolean"); - t.is (v33.get_bool (), false, "'foo' !~ 'foo' --> false"); + Variant v33 = v3.operator_nomatch(v3, task); + t.is(v33.type(), Variant::type_boolean, "'foo' !~ 'foo' --> boolean"); + t.is(v33.get_bool(), false, "'foo' !~ 'foo' --> false"); - Variant v34 = v3.operator_nomatch (v4, task); - t.is (v34.type (), Variant::type_boolean, "'foo' !~ 1234567890 --> boolean"); - t.is (v34.get_bool (), true, "'foo' !~ 1234567890 --> true"); + Variant v34 = v3.operator_nomatch(v4, task); + t.is(v34.type(), Variant::type_boolean, "'foo' !~ 1234567890 --> boolean"); + t.is(v34.get_bool(), true, "'foo' !~ 1234567890 --> true"); - Variant v35 = v3.operator_nomatch (v5, task); - t.is (v35.type (), Variant::type_boolean, "'foo' !~ 1200 --> boolean"); - t.is (v35.get_bool (), true, "'foo' !~ 1200 --> true"); + Variant v35 = v3.operator_nomatch(v5, task); + t.is(v35.type(), Variant::type_boolean, "'foo' !~ 1200 --> boolean"); + t.is(v35.get_bool(), true, "'foo' !~ 1200 --> true"); - Variant v40 = v4.operator_nomatch (v0, task); - t.is (v40.type (), Variant::type_boolean, "1234567890 !~ true --> boolean"); - t.is (v40.get_bool (), true, "1234567890 !~ true --> true"); + Variant v40 = v4.operator_nomatch(v0, task); + t.is(v40.type(), Variant::type_boolean, "1234567890 !~ true --> boolean"); + t.is(v40.get_bool(), true, "1234567890 !~ true --> true"); - Variant v41 = v4.operator_nomatch (v1, task); - t.is (v41.type (), Variant::type_boolean, "1234567890 !~ 42 --> boolean"); - t.is (v41.get_bool (), true, "1234567890 !~ 42 --> true"); + Variant v41 = v4.operator_nomatch(v1, task); + t.is(v41.type(), Variant::type_boolean, "1234567890 !~ 42 --> boolean"); + t.is(v41.get_bool(), true, "1234567890 !~ 42 --> true"); - Variant v42 = v4.operator_nomatch (v2, task); - t.is (v42.type (), Variant::type_boolean, "1234567890 !~ 3.14 --> boolean"); - t.is (v42.get_bool (), true, "1234567890 !~ 3.14 --> true"); + Variant v42 = v4.operator_nomatch(v2, task); + t.is(v42.type(), Variant::type_boolean, "1234567890 !~ 3.14 --> boolean"); + t.is(v42.get_bool(), true, "1234567890 !~ 3.14 --> true"); - Variant v43 = v4.operator_nomatch (v3, task); - t.is (v43.type (), Variant::type_boolean, "1234567890 !~ 'foo' --> boolean"); - t.is (v43.get_bool (), true, "1234567890 !~ 'foo' --> true"); + Variant v43 = v4.operator_nomatch(v3, task); + t.is(v43.type(), Variant::type_boolean, "1234567890 !~ 'foo' --> boolean"); + t.is(v43.get_bool(), true, "1234567890 !~ 'foo' --> true"); - Variant v44 = v4.operator_nomatch (v4, task); - t.is (v44.type (), Variant::type_boolean, "1234567890 !~ 1234567890 --> boolean"); - t.is (v44.get_bool (), false, "1234567890 !~ 1234567890 --> false"); + Variant v44 = v4.operator_nomatch(v4, task); + t.is(v44.type(), Variant::type_boolean, "1234567890 !~ 1234567890 --> boolean"); + t.is(v44.get_bool(), false, "1234567890 !~ 1234567890 --> false"); - Variant v45 = v4.operator_nomatch (v5, task); - t.is (v45.type (), Variant::type_boolean, "1234567890 !~ 1200 --> boolean"); - t.is (v45.get_bool (), true, "1234567890 !~ 1200 --> true"); + Variant v45 = v4.operator_nomatch(v5, task); + t.is(v45.type(), Variant::type_boolean, "1234567890 !~ 1200 --> boolean"); + t.is(v45.get_bool(), true, "1234567890 !~ 1200 --> true"); - Variant v50 = v5.operator_nomatch (v0, task); - t.is (v50.type (), Variant::type_boolean, "1200 !~ true --> boolean"); - t.is (v50.get_bool (), true, "1200 !~ true --> true"); + Variant v50 = v5.operator_nomatch(v0, task); + t.is(v50.type(), Variant::type_boolean, "1200 !~ true --> boolean"); + t.is(v50.get_bool(), true, "1200 !~ true --> true"); - Variant v51 = v5.operator_nomatch (v1, task); - t.is (v51.type (), Variant::type_boolean, "1200 !~ 42 --> boolean"); - t.is (v51.get_bool (), true, "1200 !~ 42 --> true"); + Variant v51 = v5.operator_nomatch(v1, task); + t.is(v51.type(), Variant::type_boolean, "1200 !~ 42 --> boolean"); + t.is(v51.get_bool(), true, "1200 !~ 42 --> true"); - Variant v52 = v5.operator_nomatch (v2, task); - t.is (v52.type (), Variant::type_boolean, "1200 !~ 3.14 --> boolean"); - t.is (v52.get_bool (), true, "1200 !~ 3.14 --> true"); + Variant v52 = v5.operator_nomatch(v2, task); + t.is(v52.type(), Variant::type_boolean, "1200 !~ 3.14 --> boolean"); + t.is(v52.get_bool(), true, "1200 !~ 3.14 --> true"); - Variant v53 = v5.operator_nomatch (v3, task); - t.is (v53.type (), Variant::type_boolean, "1200 !~ 'foo' --> boolean"); - t.is (v53.get_bool (), true, "1200 !~ 'foo' --> true"); + Variant v53 = v5.operator_nomatch(v3, task); + t.is(v53.type(), Variant::type_boolean, "1200 !~ 'foo' --> boolean"); + t.is(v53.get_bool(), true, "1200 !~ 'foo' --> true"); - Variant v54 = v5.operator_nomatch (v4, task); - t.is (v04.type (), Variant::type_boolean, "1200 !~ 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "1200 !~ 1234567890 --> true"); + Variant v54 = v5.operator_nomatch(v4, task); + t.is(v04.type(), Variant::type_boolean, "1200 !~ 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "1200 !~ 1234567890 --> true"); - Variant v55 = v5.operator_nomatch (v5, task); - t.is (v55.type (), Variant::type_boolean, "1200 !~ 1200 --> boolean"); - t.is (v55.get_bool (), false, "1200 !~ 1200 --> false"); + Variant v55 = v5.operator_nomatch(v5, task); + t.is(v55.type(), Variant::type_boolean, "1200 !~ 1200 --> boolean"); + t.is(v55.get_bool(), false, "1200 !~ 1200 --> false"); return 0; } diff --git a/test/variant_not.test.cpp b/test/variant_not.test.cpp index aad898cc3..cafc4aa5c 100644 --- a/test/variant_not.test.cpp +++ b/test/variant_not.test.cpp @@ -27,51 +27,51 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (14); +int main(int, char**) { + UnitTest t(14); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // Truth table. - Variant vFalse (false); - Variant vTrue (true); - t.is (!vFalse, true, "!false --> true"); - t.is (!vTrue, false, "!true --> false"); + Variant vFalse(false); + Variant vTrue(true); + t.is(!vFalse, true, "!false --> true"); + t.is(!vTrue, false, "!true --> false"); - Variant v00 = ! v0; - t.is (v00.type (), Variant::type_boolean, "! true --> boolean"); - t.is (v00.get_bool (), false, "! true --> false"); + Variant v00 = !v0; + t.is(v00.type(), Variant::type_boolean, "! true --> boolean"); + t.is(v00.get_bool(), false, "! true --> false"); - Variant v01 = ! v1; - t.is (v01.type (), Variant::type_boolean, "! 42 --> boolean"); - t.is (v01.get_bool (), false, "! 42 --> false"); + Variant v01 = !v1; + t.is(v01.type(), Variant::type_boolean, "! 42 --> boolean"); + t.is(v01.get_bool(), false, "! 42 --> false"); - Variant v02 = ! v2; - t.is (v02.type (), Variant::type_boolean, "! 3.14 --> boolean"); - t.is (v02.get_bool (), false, "! 3.14 --> false"); + Variant v02 = !v2; + t.is(v02.type(), Variant::type_boolean, "! 3.14 --> boolean"); + t.is(v02.get_bool(), false, "! 3.14 --> false"); - Variant v03 = ! v3; - t.is (v03.type (), Variant::type_boolean, "! foo --> boolean"); - t.is (v03.get_bool (), false, "! foo --> false"); + Variant v03 = !v3; + t.is(v03.type(), Variant::type_boolean, "! foo --> boolean"); + t.is(v03.get_bool(), false, "! foo --> false"); - Variant v04 = ! v4; - t.is (v04.type (), Variant::type_boolean, "! 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "! 1234567890 --> false"); + Variant v04 = !v4; + t.is(v04.type(), Variant::type_boolean, "! 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "! 1234567890 --> false"); - Variant v05 = ! v5; - t.is (v05.type (), Variant::type_boolean, "! 1200 --> boolean"); - t.is (v05.get_bool (), false, "! 1200 --> false"); + Variant v05 = !v5; + t.is(v05.type(), Variant::type_boolean, "! 1200 --> boolean"); + t.is(v05.get_bool(), false, "! 1200 --> false"); return 0; } diff --git a/test/variant_or.test.cpp b/test/variant_or.test.cpp index 010406d7f..056e47a8e 100644 --- a/test/variant_or.test.cpp +++ b/test/variant_or.test.cpp @@ -27,173 +27,173 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (76); +int main(int, char**) { + UnitTest t(76); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // Truth table. - Variant vFalse (false); - Variant vTrue (true); - t.is (vFalse || vFalse, false, "false || false --> false"); - t.is (vFalse || vTrue, true, "false || true --> true"); - t.is (vTrue || vFalse, true, "true || false --> true"); - t.is (vTrue || vTrue, true, "true || true --> true"); + Variant vFalse(false); + Variant vTrue(true); + t.is(vFalse || vFalse, false, "false || false --> false"); + t.is(vFalse || vTrue, true, "false || true --> true"); + t.is(vTrue || vFalse, true, "true || false --> true"); + t.is(vTrue || vTrue, true, "true || true --> true"); Variant v00 = v0 || v0; - t.is (v00.type (), Variant::type_boolean, "true || true --> boolean"); - t.is (v00.get_bool (), true, "true || true --> true"); + t.is(v00.type(), Variant::type_boolean, "true || true --> boolean"); + t.is(v00.get_bool(), true, "true || true --> true"); Variant v01 = v0 || v1; - t.is (v01.type (), Variant::type_boolean, "true || 42 --> boolean"); - t.is (v01.get_bool (), true, "true || 42 --> true"); + t.is(v01.type(), Variant::type_boolean, "true || 42 --> boolean"); + t.is(v01.get_bool(), true, "true || 42 --> true"); Variant v02 = v0 || v2; - t.is (v02.type (), Variant::type_boolean, "true || 3.14 --> boolean"); - t.is (v02.get_bool (), true, "true || 3.14 --> true"); + t.is(v02.type(), Variant::type_boolean, "true || 3.14 --> boolean"); + t.is(v02.get_bool(), true, "true || 3.14 --> true"); Variant v03 = v0 || v3; - t.is (v03.type (), Variant::type_boolean, "true || 'foo' --> boolean"); - t.is (v03.get_bool (), true, "true || 'foo' --> true"); + t.is(v03.type(), Variant::type_boolean, "true || 'foo' --> boolean"); + t.is(v03.get_bool(), true, "true || 'foo' --> true"); Variant v04 = v0 || v4; - t.is (v04.type (), Variant::type_boolean, "true || 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "true || 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "true || 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "true || 1234567890 --> true"); Variant v05 = v0 || v5; - t.is (v05.type (), Variant::type_boolean, "true || 1200 --> boolean"); - t.is (v05.get_bool (), true, "true || 1200 --> true"); + t.is(v05.type(), Variant::type_boolean, "true || 1200 --> boolean"); + t.is(v05.get_bool(), true, "true || 1200 --> true"); Variant v10 = v1 || v0; - t.is (v10.type (), Variant::type_boolean, "42 || true --> boolean"); - t.is (v10.get_bool (), true, "42 || true --> true"); + t.is(v10.type(), Variant::type_boolean, "42 || true --> boolean"); + t.is(v10.get_bool(), true, "42 || true --> true"); Variant v11 = v1 || v1; - t.is (v11.type (), Variant::type_boolean, "42 || 42 --> boolean"); - t.is (v11.get_bool (), true, "42 || 42 --> true"); + t.is(v11.type(), Variant::type_boolean, "42 || 42 --> boolean"); + t.is(v11.get_bool(), true, "42 || 42 --> true"); Variant v12 = v1 || v2; - t.is (v12.type (), Variant::type_boolean, "42 || 3.14 --> boolean"); - t.is (v12.get_bool (), true, "42 || 3.14 --> true"); + t.is(v12.type(), Variant::type_boolean, "42 || 3.14 --> boolean"); + t.is(v12.get_bool(), true, "42 || 3.14 --> true"); Variant v13 = v1 || v3; - t.is (v13.type (), Variant::type_boolean, "42 || 'foo' --> boolean"); - t.is (v13.get_bool (), true, "42 || 'foo' --> true"); + t.is(v13.type(), Variant::type_boolean, "42 || 'foo' --> boolean"); + t.is(v13.get_bool(), true, "42 || 'foo' --> true"); Variant v14 = v1 || v4; - t.is (v04.type (), Variant::type_boolean, "42 || 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "42 || 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "42 || 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "42 || 1234567890 --> true"); Variant v15 = v1 || v5; - t.is (v15.type (), Variant::type_boolean, "42 || 1200 --> boolean"); - t.is (v15.get_bool (), true, "42 || 1200 --> true"); + t.is(v15.type(), Variant::type_boolean, "42 || 1200 --> boolean"); + t.is(v15.get_bool(), true, "42 || 1200 --> true"); Variant v20 = v2 || v0; - t.is (v20.type (), Variant::type_boolean, "3.14 || true --> boolean"); - t.is (v20.get_bool (), true, "3.14 || true --> true"); + t.is(v20.type(), Variant::type_boolean, "3.14 || true --> boolean"); + t.is(v20.get_bool(), true, "3.14 || true --> true"); Variant v21 = v2 || v1; - t.is (v21.type (), Variant::type_boolean, "3.14 || 42 --> boolean"); - t.is (v21.get_bool (), true, "3.14 || 42 --> true"); + t.is(v21.type(), Variant::type_boolean, "3.14 || 42 --> boolean"); + t.is(v21.get_bool(), true, "3.14 || 42 --> true"); Variant v22 = v2 || v2; - t.is (v22.type (), Variant::type_boolean, "3.14 || 3.14 --> boolean"); - t.is (v22.get_bool (), true, "3.14 || 3.14 --> true"); + t.is(v22.type(), Variant::type_boolean, "3.14 || 3.14 --> boolean"); + t.is(v22.get_bool(), true, "3.14 || 3.14 --> true"); Variant v23 = v2 || v3; - t.is (v23.type (), Variant::type_boolean, "3.14 || 'foo' --> boolean"); - t.is (v23.get_bool (), true, "3.14 || 'foo' --> true"); + t.is(v23.type(), Variant::type_boolean, "3.14 || 'foo' --> boolean"); + t.is(v23.get_bool(), true, "3.14 || 'foo' --> true"); Variant v24 = v2 || v4; - t.is (v24.type (), Variant::type_boolean, "3.14 || 1234567890 --> boolean"); - t.is (v24.get_bool (), true, "3.14 || 1234567890 --> true"); + t.is(v24.type(), Variant::type_boolean, "3.14 || 1234567890 --> boolean"); + t.is(v24.get_bool(), true, "3.14 || 1234567890 --> true"); Variant v25 = v2 || v5; - t.is (v25.type (), Variant::type_boolean, "3.14 || 1200 --> boolean"); - t.is (v25.get_bool (), true, "3.14 || 1200 --> true"); + t.is(v25.type(), Variant::type_boolean, "3.14 || 1200 --> boolean"); + t.is(v25.get_bool(), true, "3.14 || 1200 --> true"); Variant v30 = v3 || v0; - t.is (v30.type (), Variant::type_boolean, "'foo' || true --> boolean"); - t.is (v30.get_bool (), true, "'foo' || true --> true"); + t.is(v30.type(), Variant::type_boolean, "'foo' || true --> boolean"); + t.is(v30.get_bool(), true, "'foo' || true --> true"); Variant v31 = v3 || v1; - t.is (v31.type (), Variant::type_boolean, "'foo' || 42 --> boolean"); - t.is (v31.get_bool (), true, "'foo' || 42 --> true"); + t.is(v31.type(), Variant::type_boolean, "'foo' || 42 --> boolean"); + t.is(v31.get_bool(), true, "'foo' || 42 --> true"); Variant v32 = v3 || v2; - t.is (v32.type (), Variant::type_boolean, "'foo' || 3.14 --> boolean"); - t.is (v32.get_bool (), true, "'foo' || 3.14 --> true"); + t.is(v32.type(), Variant::type_boolean, "'foo' || 3.14 --> boolean"); + t.is(v32.get_bool(), true, "'foo' || 3.14 --> true"); Variant v33 = v3 || v3; - t.is (v33.type (), Variant::type_boolean, "'foo' || 'foo' --> boolean"); - t.is (v33.get_bool (), true, "'foo' || 'foo' --> true"); + t.is(v33.type(), Variant::type_boolean, "'foo' || 'foo' --> boolean"); + t.is(v33.get_bool(), true, "'foo' || 'foo' --> true"); Variant v34 = v3 || v4; - t.is (v34.type (), Variant::type_boolean, "'foo' || 1234567890 --> boolean"); - t.is (v34.get_bool (), true, "'foo' || 1234567890 --> true"); + t.is(v34.type(), Variant::type_boolean, "'foo' || 1234567890 --> boolean"); + t.is(v34.get_bool(), true, "'foo' || 1234567890 --> true"); Variant v35 = v3 || v5; - t.is (v35.type (), Variant::type_boolean, "'foo' || 1200 --> boolean"); - t.is (v35.get_bool (), true, "'foo' || 1200 --> true"); + t.is(v35.type(), Variant::type_boolean, "'foo' || 1200 --> boolean"); + t.is(v35.get_bool(), true, "'foo' || 1200 --> true"); Variant v40 = v4 || v0; - t.is (v40.type (), Variant::type_boolean, "1234567890 || true --> boolean"); - t.is (v40.get_bool (), true, "1234567890 || true --> true"); + t.is(v40.type(), Variant::type_boolean, "1234567890 || true --> boolean"); + t.is(v40.get_bool(), true, "1234567890 || true --> true"); Variant v41 = v4 || v1; - t.is (v41.type (), Variant::type_boolean, "1234567890 || 42 --> boolean"); - t.is (v41.get_bool (), true, "1234567890 || 42 --> true"); + t.is(v41.type(), Variant::type_boolean, "1234567890 || 42 --> boolean"); + t.is(v41.get_bool(), true, "1234567890 || 42 --> true"); Variant v42 = v4 || v2; - t.is (v42.type (), Variant::type_boolean, "1234567890 || 3.14 --> boolean"); - t.is (v42.get_bool (), true, "1234567890 || 3.14 --> true"); + t.is(v42.type(), Variant::type_boolean, "1234567890 || 3.14 --> boolean"); + t.is(v42.get_bool(), true, "1234567890 || 3.14 --> true"); Variant v43 = v4 || v3; - t.is (v43.type (), Variant::type_boolean, "1234567890 || 'foo' --> boolean"); - t.is (v43.get_bool (), true, "1234567890 || 'foo' --> true"); + t.is(v43.type(), Variant::type_boolean, "1234567890 || 'foo' --> boolean"); + t.is(v43.get_bool(), true, "1234567890 || 'foo' --> true"); Variant v44 = v4 || v4; - t.is (v44.type (), Variant::type_boolean, "1234567890 || 1234567890 --> boolean"); - t.is (v44.get_bool (), true, "1234567890 || 1234567890 --> true"); + t.is(v44.type(), Variant::type_boolean, "1234567890 || 1234567890 --> boolean"); + t.is(v44.get_bool(), true, "1234567890 || 1234567890 --> true"); Variant v45 = v4 || v5; - t.is (v45.type (), Variant::type_boolean, "1234567890 || 1200 --> boolean"); - t.is (v45.get_bool (), true, "1234567890 || 1200 --> true"); + t.is(v45.type(), Variant::type_boolean, "1234567890 || 1200 --> boolean"); + t.is(v45.get_bool(), true, "1234567890 || 1200 --> true"); Variant v50 = v5 || v0; - t.is (v50.type (), Variant::type_boolean, "1200 || true --> boolean"); - t.is (v50.get_bool (), true, "1200 || true --> true"); + t.is(v50.type(), Variant::type_boolean, "1200 || true --> boolean"); + t.is(v50.get_bool(), true, "1200 || true --> true"); Variant v51 = v5 || v1; - t.is (v51.type (), Variant::type_boolean, "1200 || 42 --> boolean"); - t.is (v51.get_bool (), true, "1200 || 42 --> true"); + t.is(v51.type(), Variant::type_boolean, "1200 || 42 --> boolean"); + t.is(v51.get_bool(), true, "1200 || 42 --> true"); Variant v52 = v5 || v2; - t.is (v52.type (), Variant::type_boolean, "1200 || 3.14 --> boolean"); - t.is (v52.get_bool (), true, "1200 || 3.14 --> true"); + t.is(v52.type(), Variant::type_boolean, "1200 || 3.14 --> boolean"); + t.is(v52.get_bool(), true, "1200 || 3.14 --> true"); Variant v53 = v5 || v3; - t.is (v53.type (), Variant::type_boolean, "1200 || 'foo' --> boolean"); - t.is (v53.get_bool (), true, "1200 || 'foo' --> true"); + t.is(v53.type(), Variant::type_boolean, "1200 || 'foo' --> boolean"); + t.is(v53.get_bool(), true, "1200 || 'foo' --> true"); Variant v54 = v5 || v4; - t.is (v04.type (), Variant::type_boolean, "1200 || 1234567890 --> boolean"); - t.is (v04.get_bool (), true, "1200 || 1234567890 --> true"); + t.is(v04.type(), Variant::type_boolean, "1200 || 1234567890 --> boolean"); + t.is(v04.get_bool(), true, "1200 || 1234567890 --> true"); Variant v55 = v5 || v5; - t.is (v55.type (), Variant::type_boolean, "1200 || 1200 --> boolean"); - t.is (v55.get_bool (), true, "1200 || 1200 --> true"); + t.is(v55.type(), Variant::type_boolean, "1200 || 1200 --> boolean"); + t.is(v55.get_bool(), true, "1200 || 1200 --> true"); return 0; } diff --git a/test/variant_partial.test.cpp b/test/variant_partial.test.cpp index 66b6385cf..78893c4a6 100644 --- a/test/variant_partial.test.cpp +++ b/test/variant_partial.test.cpp @@ -27,175 +27,175 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (72); +int main(int, char**) { + UnitTest t(72); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); - Variant v6 (1234522800, Variant::type_date); // 2009-02-13, 12.00pm UTC - Variant v7 ("2009-02-13"); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); + Variant v6(1234522800, Variant::type_date); // 2009-02-13, 12.00pm UTC + Variant v7("2009-02-13"); - Variant v00 = v0.operator_partial (v0); - t.is (v00.type (), Variant::type_boolean, "true == true --> boolean"); - t.is (v00.get_bool (), true, "true == true --> true"); + Variant v00 = v0.operator_partial(v0); + t.is(v00.type(), Variant::type_boolean, "true == true --> boolean"); + t.is(v00.get_bool(), true, "true == true --> true"); - Variant v01 = v0.operator_partial (v1); - t.is (v01.type (), Variant::type_boolean, "true == 42 --> boolean"); - t.is (v01.get_bool (), false, "true == 42 --> false"); + Variant v01 = v0.operator_partial(v1); + t.is(v01.type(), Variant::type_boolean, "true == 42 --> boolean"); + t.is(v01.get_bool(), false, "true == 42 --> false"); - Variant v02 = v0.operator_partial (v2); - t.is (v02.type (), Variant::type_boolean, "true == 3.14 --> boolean"); - t.is (v02.get_bool (), false, "true == 3.14 --> false"); + Variant v02 = v0.operator_partial(v2); + t.is(v02.type(), Variant::type_boolean, "true == 3.14 --> boolean"); + t.is(v02.get_bool(), false, "true == 3.14 --> false"); - Variant v03 = v0.operator_partial (v3); - t.is (v03.type (), Variant::type_boolean, "true == 'foo' --> boolean"); - t.is (v03.get_bool (), false, "true == 'foo' --> false"); + Variant v03 = v0.operator_partial(v3); + t.is(v03.type(), Variant::type_boolean, "true == 'foo' --> boolean"); + t.is(v03.get_bool(), false, "true == 'foo' --> false"); - Variant v04 = v0.operator_partial (v4); - t.is (v04.type (), Variant::type_boolean, "true == 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "true == 1234567890 --> false"); + Variant v04 = v0.operator_partial(v4); + t.is(v04.type(), Variant::type_boolean, "true == 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "true == 1234567890 --> false"); - Variant v05 = v0.operator_partial (v5); - t.is (v05.type (), Variant::type_boolean, "true == 1200 --> boolean"); - t.is (v05.get_bool (), false, "true == 1200 --> false"); + Variant v05 = v0.operator_partial(v5); + t.is(v05.type(), Variant::type_boolean, "true == 1200 --> boolean"); + t.is(v05.get_bool(), false, "true == 1200 --> false"); - Variant v10 = v1.operator_partial (v0); - t.is (v10.type (), Variant::type_boolean, "42 == true --> boolean"); - t.is (v10.get_bool (), false, "42 == true --> false"); + Variant v10 = v1.operator_partial(v0); + t.is(v10.type(), Variant::type_boolean, "42 == true --> boolean"); + t.is(v10.get_bool(), false, "42 == true --> false"); - Variant v11 = v1.operator_partial (v1); - t.is (v11.type (), Variant::type_boolean, "42 == 42 --> boolean"); - t.is (v11.get_bool (), true, "42 == 42 --> true"); + Variant v11 = v1.operator_partial(v1); + t.is(v11.type(), Variant::type_boolean, "42 == 42 --> boolean"); + t.is(v11.get_bool(), true, "42 == 42 --> true"); - Variant v12 = v1.operator_partial (v2); - t.is (v12.type (), Variant::type_boolean, "42 == 3.14 --> boolean"); - t.is (v12.get_bool (), false, "42 == 3.14 --> false"); + Variant v12 = v1.operator_partial(v2); + t.is(v12.type(), Variant::type_boolean, "42 == 3.14 --> boolean"); + t.is(v12.get_bool(), false, "42 == 3.14 --> false"); - Variant v13 = v1.operator_partial (v3); - t.is (v13.type (), Variant::type_boolean, "42 == 'foo' --> boolean"); - t.is (v13.get_bool (), false, "42 == 'foo' --> false"); + Variant v13 = v1.operator_partial(v3); + t.is(v13.type(), Variant::type_boolean, "42 == 'foo' --> boolean"); + t.is(v13.get_bool(), false, "42 == 'foo' --> false"); - Variant v14 = v1.operator_partial (v4); - t.is (v04.type (), Variant::type_boolean, "42 == 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "42 == 1234567890 --> false"); + Variant v14 = v1.operator_partial(v4); + t.is(v04.type(), Variant::type_boolean, "42 == 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "42 == 1234567890 --> false"); - Variant v15 = v1.operator_partial (v5); - t.is (v15.type (), Variant::type_boolean, "42 == 1200 --> boolean"); - t.is (v15.get_bool (), false, "42 == 1200 --> false"); + Variant v15 = v1.operator_partial(v5); + t.is(v15.type(), Variant::type_boolean, "42 == 1200 --> boolean"); + t.is(v15.get_bool(), false, "42 == 1200 --> false"); - Variant v20 = v2.operator_partial (v0); - t.is (v20.type (), Variant::type_boolean, "3.14 == true --> boolean"); - t.is (v20.get_bool (), false, "3.14 == true --> false"); + Variant v20 = v2.operator_partial(v0); + t.is(v20.type(), Variant::type_boolean, "3.14 == true --> boolean"); + t.is(v20.get_bool(), false, "3.14 == true --> false"); - Variant v21 = v2.operator_partial (v1); - t.is (v21.type (), Variant::type_boolean, "3.14 == 42 --> boolean"); - t.is (v21.get_bool (), false, "3.14 == 42 --> false"); + Variant v21 = v2.operator_partial(v1); + t.is(v21.type(), Variant::type_boolean, "3.14 == 42 --> boolean"); + t.is(v21.get_bool(), false, "3.14 == 42 --> false"); - Variant v22 = v2.operator_partial (v2); - t.is (v22.type (), Variant::type_boolean, "3.14 == 3.14 --> boolean"); - t.is (v22.get_bool (), true, "3.14 == 3.14 --> true"); + Variant v22 = v2.operator_partial(v2); + t.is(v22.type(), Variant::type_boolean, "3.14 == 3.14 --> boolean"); + t.is(v22.get_bool(), true, "3.14 == 3.14 --> true"); - Variant v23 = v2.operator_partial (v3); - t.is (v23.type (), Variant::type_boolean, "3.14 == 'foo' --> boolean"); - t.is (v23.get_bool (), false, "3.14 == 'foo' --> false"); + Variant v23 = v2.operator_partial(v3); + t.is(v23.type(), Variant::type_boolean, "3.14 == 'foo' --> boolean"); + t.is(v23.get_bool(), false, "3.14 == 'foo' --> false"); - Variant v24 = v2.operator_partial (v4); - t.is (v24.type (), Variant::type_boolean, "3.14 == 1234567890 --> boolean"); - t.is (v24.get_bool (), false, "3.14 == 1234567890 --> false"); + Variant v24 = v2.operator_partial(v4); + t.is(v24.type(), Variant::type_boolean, "3.14 == 1234567890 --> boolean"); + t.is(v24.get_bool(), false, "3.14 == 1234567890 --> false"); - Variant v25 = v2.operator_partial (v5); - t.is (v25.type (), Variant::type_boolean, "3.14 == 1200 --> boolean"); - t.is (v25.get_bool (), false, "3.14 == 1200 --> false"); + Variant v25 = v2.operator_partial(v5); + t.is(v25.type(), Variant::type_boolean, "3.14 == 1200 --> boolean"); + t.is(v25.get_bool(), false, "3.14 == 1200 --> false"); - Variant v30 = v3.operator_partial (v0); - t.is (v30.type (), Variant::type_boolean, "'foo' == true --> boolean"); - t.is (v30.get_bool (), false, "'foo' == true --> false"); + Variant v30 = v3.operator_partial(v0); + t.is(v30.type(), Variant::type_boolean, "'foo' == true --> boolean"); + t.is(v30.get_bool(), false, "'foo' == true --> false"); - Variant v31 = v3.operator_partial (v1); - t.is (v31.type (), Variant::type_boolean, "'foo' == 42 --> boolean"); - t.is (v31.get_bool (), false, "'foo' == 42 --> false"); + Variant v31 = v3.operator_partial(v1); + t.is(v31.type(), Variant::type_boolean, "'foo' == 42 --> boolean"); + t.is(v31.get_bool(), false, "'foo' == 42 --> false"); - Variant v32 = v3.operator_partial (v2); - t.is (v32.type (), Variant::type_boolean, "'foo' == 3.14 --> boolean"); - t.is (v32.get_bool (), false, "'foo' == 3.14 --> false"); + Variant v32 = v3.operator_partial(v2); + t.is(v32.type(), Variant::type_boolean, "'foo' == 3.14 --> boolean"); + t.is(v32.get_bool(), false, "'foo' == 3.14 --> false"); - Variant v33 = v3.operator_partial (v3); - t.is (v33.type (), Variant::type_boolean, "'foo' == 'foo' --> boolean"); - t.is (v33.get_bool (), true, "'foo' == 'foo' --> true"); + Variant v33 = v3.operator_partial(v3); + t.is(v33.type(), Variant::type_boolean, "'foo' == 'foo' --> boolean"); + t.is(v33.get_bool(), true, "'foo' == 'foo' --> true"); - Variant v34 = v3.operator_partial (v4); - t.is (v34.type (), Variant::type_boolean, "'foo' == 1234567890 --> boolean"); - t.is (v34.get_bool (), false, "'foo' == 1234567890 --> false"); + Variant v34 = v3.operator_partial(v4); + t.is(v34.type(), Variant::type_boolean, "'foo' == 1234567890 --> boolean"); + t.is(v34.get_bool(), false, "'foo' == 1234567890 --> false"); - Variant v35 = v3.operator_partial (v5); - t.is (v35.type (), Variant::type_boolean, "'foo' == 1200 --> boolean"); - t.is (v35.get_bool (), false, "'foo' == 1200 --> false"); + Variant v35 = v3.operator_partial(v5); + t.is(v35.type(), Variant::type_boolean, "'foo' == 1200 --> boolean"); + t.is(v35.get_bool(), false, "'foo' == 1200 --> false"); - Variant v40 = v4.operator_partial (v0); - t.is (v40.type (), Variant::type_boolean, "1234567890 == true --> boolean"); - t.is (v40.get_bool (), false, "1234567890 == true --> false"); + Variant v40 = v4.operator_partial(v0); + t.is(v40.type(), Variant::type_boolean, "1234567890 == true --> boolean"); + t.is(v40.get_bool(), false, "1234567890 == true --> false"); - Variant v41 = v4.operator_partial (v1); - t.is (v41.type (), Variant::type_boolean, "1234567890 == 42 --> boolean"); - t.is (v41.get_bool (), false, "1234567890 == 42 --> false"); + Variant v41 = v4.operator_partial(v1); + t.is(v41.type(), Variant::type_boolean, "1234567890 == 42 --> boolean"); + t.is(v41.get_bool(), false, "1234567890 == 42 --> false"); - Variant v42 = v4.operator_partial (v2); - t.is (v42.type (), Variant::type_boolean, "1234567890 == 3.14 --> boolean"); - t.is (v42.get_bool (), false, "1234567890 == 3.14 --> false"); + Variant v42 = v4.operator_partial(v2); + t.is(v42.type(), Variant::type_boolean, "1234567890 == 3.14 --> boolean"); + t.is(v42.get_bool(), false, "1234567890 == 3.14 --> false"); - Variant v43 = v4.operator_partial (v3); - t.is (v43.type (), Variant::type_boolean, "1234567890 == 'foo' --> boolean"); - t.is (v43.get_bool (), false, "1234567890 == 'foo' --> false"); + Variant v43 = v4.operator_partial(v3); + t.is(v43.type(), Variant::type_boolean, "1234567890 == 'foo' --> boolean"); + t.is(v43.get_bool(), false, "1234567890 == 'foo' --> false"); - Variant v44 = v4.operator_partial (v4); - t.is (v44.type (), Variant::type_boolean, "1234567890 == 1234567890 --> boolean"); - t.is (v44.get_bool (), true, "1234567890 == 1234567890 --> true"); + Variant v44 = v4.operator_partial(v4); + t.is(v44.type(), Variant::type_boolean, "1234567890 == 1234567890 --> boolean"); + t.is(v44.get_bool(), true, "1234567890 == 1234567890 --> true"); - Variant v45 = v4.operator_partial (v5); - t.is (v45.type (), Variant::type_boolean, "1234567890 == 1200 --> boolean"); - t.is (v45.get_bool (), false, "1234567890 == 1200 --> false"); + Variant v45 = v4.operator_partial(v5); + t.is(v45.type(), Variant::type_boolean, "1234567890 == 1200 --> boolean"); + t.is(v45.get_bool(), false, "1234567890 == 1200 --> false"); - Variant v50 = v5.operator_partial (v0); - t.is (v50.type (), Variant::type_boolean, "1200 == true --> boolean"); - t.is (v50.get_bool (), false, "1200 == true --> false"); + Variant v50 = v5.operator_partial(v0); + t.is(v50.type(), Variant::type_boolean, "1200 == true --> boolean"); + t.is(v50.get_bool(), false, "1200 == true --> false"); - Variant v51 = v5.operator_partial (v1); - t.is (v51.type (), Variant::type_boolean, "1200 == 42 --> boolean"); - t.is (v51.get_bool (), false, "1200 == 42 --> false"); + Variant v51 = v5.operator_partial(v1); + t.is(v51.type(), Variant::type_boolean, "1200 == 42 --> boolean"); + t.is(v51.get_bool(), false, "1200 == 42 --> false"); - Variant v52 = v5.operator_partial (v2); - t.is (v52.type (), Variant::type_boolean, "1200 == 3.14 --> boolean"); - t.is (v52.get_bool (), false, "1200 == 3.14 --> false"); + Variant v52 = v5.operator_partial(v2); + t.is(v52.type(), Variant::type_boolean, "1200 == 3.14 --> boolean"); + t.is(v52.get_bool(), false, "1200 == 3.14 --> false"); - Variant v53 = v5.operator_partial (v3); - t.is (v53.type (), Variant::type_boolean, "1200 == 'foo' --> boolean"); - t.is (v53.get_bool (), false, "1200 == 'foo' --> false"); + Variant v53 = v5.operator_partial(v3); + t.is(v53.type(), Variant::type_boolean, "1200 == 'foo' --> boolean"); + t.is(v53.get_bool(), false, "1200 == 'foo' --> false"); - Variant v54 = v5.operator_partial (v4); - t.is (v04.type (), Variant::type_boolean, "1200 == 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "1200 == 1234567890 --> false"); + Variant v54 = v5.operator_partial(v4); + t.is(v04.type(), Variant::type_boolean, "1200 == 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "1200 == 1234567890 --> false"); - Variant v55 = v5.operator_partial (v5); - t.is (v55.type (), Variant::type_boolean, "1200 == 1200 --> boolean"); - t.is (v55.get_bool (), true, "1200 == 1200 --> true"); + Variant v55 = v5.operator_partial(v5); + t.is(v55.type(), Variant::type_boolean, "1200 == 1200 --> boolean"); + t.is(v55.get_bool(), true, "1200 == 1200 --> true"); - Variant v56 = v6.operator_partial (v7); - t.is (v56.type (), Variant::type_boolean, "1234522800 == '2009-02-13' --> boolean"); - t.is (v56.get_bool (), true, "1234522800 == '2009-02-13' --> true"); + Variant v56 = v6.operator_partial(v7); + t.is(v56.type(), Variant::type_boolean, "1234522800 == '2009-02-13' --> boolean"); + t.is(v56.get_bool(), true, "1234522800 == '2009-02-13' --> true"); - Variant v57 = v7.operator_partial (v6); - t.is (v57.type (), Variant::type_boolean, "'2009-02-13' == 1234522800 --> boolean"); - t.is (v57.get_bool (), true, "'2009-02-13' == 1234522800 --> true"); + Variant v57 = v7.operator_partial(v6); + t.is(v57.type(), Variant::type_boolean, "'2009-02-13' == 1234522800 --> boolean"); + t.is(v57.get_bool(), true, "'2009-02-13' == 1234522800 --> true"); return 0; } diff --git a/test/variant_subtract.test.cpp b/test/variant_subtract.test.cpp index e545d4422..d0e405047 100644 --- a/test/variant_subtract.test.cpp +++ b/test/variant_subtract.test.cpp @@ -27,189 +27,253 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include #define EPSILON 0.001 //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (55); +int main(int, char**) { + UnitTest t(55); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // boolean - boolean -> ERROR - try {Variant v00 = v0 - v0; t.fail ("true - true --> error");} - catch (...) {t.pass ("true - true --> error");} + try { + Variant v00 = v0 - v0; + t.fail("true - true --> error"); + } catch (...) { + t.pass("true - true --> error"); + } // boolean - integer -> ERROR - try {Variant v01 = v0 - v1; t.fail ("true - 42 --> error");} - catch (...) {t.pass ("true - 42 --> error");} + try { + Variant v01 = v0 - v1; + t.fail("true - 42 --> error"); + } catch (...) { + t.pass("true - 42 --> error"); + } // boolean - real -> ERROR - try {Variant v02 = v0 - v2; t.fail ("true - 3.14 --> error");} - catch (...) {t.pass ("true - 3.14 --> error");} + try { + Variant v02 = v0 - v2; + t.fail("true - 3.14 --> error"); + } catch (...) { + t.pass("true - 3.14 --> error"); + } // boolean - string -> ERROR - try {Variant v03 = v0 - v3; t.fail ("true - foo --> error");} - catch (...) {t.pass ("true - foo --> error");} + try { + Variant v03 = v0 - v3; + t.fail("true - foo --> error"); + } catch (...) { + t.pass("true - foo --> error"); + } // boolean - date -> ERROR - try {Variant v04 = v0 - v4; t.fail ("true - 1234567890 --> error");} - catch (...) {t.pass ("true - 1234567890 --> error");} + try { + Variant v04 = v0 - v4; + t.fail("true - 1234567890 --> error"); + } catch (...) { + t.pass("true - 1234567890 --> error"); + } // boolean - duration -> ERROR - try {Variant v05 = v0 - v5; t.fail ("true - 1200 --> error");} - catch (...) {t.pass ("true - 1200 --> error");} + try { + Variant v05 = v0 - v5; + t.fail("true - 1200 --> error"); + } catch (...) { + t.pass("true - 1200 --> error"); + } // integer - boolean -> integer Variant v10 = v1 - v0; - t.is (v10.type (), Variant::type_integer, "42 - true --> integer"); - t.is (v10.get_integer (), 41, "42 - true --> 41"); + t.is(v10.type(), Variant::type_integer, "42 - true --> integer"); + t.is(v10.get_integer(), 41, "42 - true --> 41"); // integer - integer -> integer Variant v11 = v1 - v1; - t.is (v11.type (), Variant::type_integer, "42 - 42 --> integer"); - t.is (v11.get_integer (), 0, "42 - 42 --> 0"); + t.is(v11.type(), Variant::type_integer, "42 - 42 --> integer"); + t.is(v11.get_integer(), 0, "42 - 42 --> 0"); // integer - real -> real Variant v12 = v1 - v2; - t.is (v12.type (), Variant::type_real, "42 - 3.14 --> real"); - t.is (v12.get_real (), 38.86, EPSILON, "42 - 3.14 --> 38.86"); + t.is(v12.type(), Variant::type_real, "42 - 3.14 --> real"); + t.is(v12.get_real(), 38.86, EPSILON, "42 - 3.14 --> 38.86"); // integer - string -> ERROR - try {Variant v13 = v1 - v3; t.fail ("42 - foo --> error");} - catch (...) {t.pass ("42 - foo --> error");} + try { + Variant v13 = v1 - v3; + t.fail("42 - foo --> error"); + } catch (...) { + t.pass("42 - foo --> error"); + } // integer - date -> date - Variant v1a (1300000000); + Variant v1a(1300000000); Variant v14 = v1a - v4; - t.is (v14.type (), Variant::type_date, "1300000000 - 1234567890 --> date"); - t.is (v14.get_date (), 65432110, "1300000000 - 1234567890 --> 65432110"); + t.is(v14.type(), Variant::type_date, "1300000000 - 1234567890 --> date"); + t.is(v14.get_date(), 65432110, "1300000000 - 1234567890 --> 65432110"); // integer - duration -> duration Variant v15 = v1a - v5; - t.is (v15.type (), Variant::type_duration, "1300000000 - 1200 --> duration"); - t.is (v15.get_duration (), 1299998800, "1300000000 - 1200 --> 1299998800"); + t.is(v15.type(), Variant::type_duration, "1300000000 - 1200 --> duration"); + t.is(v15.get_duration(), 1299998800, "1300000000 - 1200 --> 1299998800"); // real - boolean -> real Variant v20 = v2 - v0; - t.is (v20.type (), Variant::type_real, "3.14 - true --> real"); - t.is (v20.get_real (), 2.14, EPSILON, "3.14 - true --> 2.14"); + t.is(v20.type(), Variant::type_real, "3.14 - true --> real"); + t.is(v20.get_real(), 2.14, EPSILON, "3.14 - true --> 2.14"); // real - integer -> real Variant v21 = v2 - v1; - t.is (v21.type (), Variant::type_real, "3.14 - 42 --> real"); - t.is (v21.get_real (), -38.86, EPSILON, "3.14 - 42 --> -38.86"); + t.is(v21.type(), Variant::type_real, "3.14 - 42 --> real"); + t.is(v21.get_real(), -38.86, EPSILON, "3.14 - 42 --> -38.86"); // real - real -> real Variant v22 = v2 - v2; - t.is (v22.type (), Variant::type_real, "3.14 - 3.14 --> real"); - t.is (v22.get_real (), 0.0, EPSILON, "3.14 - 3.14 --> 0.0"); + t.is(v22.type(), Variant::type_real, "3.14 - 3.14 --> real"); + t.is(v22.get_real(), 0.0, EPSILON, "3.14 - 3.14 --> 0.0"); // real - string -> ERROR - try {Variant v23 = v1 - v3; t.fail ("3.14 - foo --> error");} - catch (...) {t.pass ("3.14 - foo --> error");} + try { + Variant v23 = v1 - v3; + t.fail("3.14 - foo --> error"); + } catch (...) { + t.pass("3.14 - foo --> error"); + } // real - date -> real - Variant v2a (1300000000.0); + Variant v2a(1300000000.0); Variant v24 = v2a - v4; - t.is (v24.type (), Variant::type_real, "1300000000.0 - 1234567890 --> real"); - t.is (v24.get_real (), 65432110.0, "1300000000.0 - 1234567890 --> 65432110"); + t.is(v24.type(), Variant::type_real, "1300000000.0 - 1234567890 --> real"); + t.is(v24.get_real(), 65432110.0, "1300000000.0 - 1234567890 --> 65432110"); // real - duration -> real Variant v25 = v2a - v5; - t.is (v25.type (), Variant::type_real, "1300000000.0 - 1200 --> real"); - t.is (v25.get_real (), 1299998800.0, "1300000000.0 - 1200 --> 1299998800"); + t.is(v25.type(), Variant::type_real, "1300000000.0 - 1200 --> real"); + t.is(v25.get_real(), 1299998800.0, "1300000000.0 - 1200 --> 1299998800"); // string - boolean -> ERROR - try {Variant v30 = v3 - v0; t.fail ("foo - foo --> error");} - catch (...) {t.pass ("foo - foo --> error");} + try { + Variant v30 = v3 - v0; + t.fail("foo - foo --> error"); + } catch (...) { + t.pass("foo - foo --> error"); + } // string - integer -> ERROR - try {Variant v31 = v3 - v1; t.fail ("foo - 42 --> error");} - catch (...) {t.pass ("foo - 42 --> error");} + try { + Variant v31 = v3 - v1; + t.fail("foo - 42 --> error"); + } catch (...) { + t.pass("foo - 42 --> error"); + } // string - real -> ERROR - try {Variant v32 = v3 - v2; t.fail ("foo - 3.14 --> error");} - catch (...) {t.pass ("foo - 3.14 --> error");} + try { + Variant v32 = v3 - v2; + t.fail("foo - 3.14 --> error"); + } catch (...) { + t.pass("foo - 3.14 --> error"); + } // string - string -> concatenated string Variant v33 = v3 - v3; - t.is (v33.type (), Variant::type_string, "foo - foo --> string"); - t.is (v33.get_string (), "foo-foo", "foo - foo --> foo-foo"); + t.is(v33.type(), Variant::type_string, "foo - foo --> string"); + t.is(v33.get_string(), "foo-foo", "foo - foo --> foo-foo"); // string - date -> ERROR - try {Variant v34 = v3 - v4; t.fail ("foo - 1234567890 --> error");} - catch (...) {t.pass ("foo - 1234567890 --> error");} + try { + Variant v34 = v3 - v4; + t.fail("foo - 1234567890 --> error"); + } catch (...) { + t.pass("foo - 1234567890 --> error"); + } // string - duration -> ERROR - try {Variant v35 = v3 - v5; t.fail ("foo - 1200 --> error");} - catch (...) {t.pass ("foo - 1200 --> error");} + try { + Variant v35 = v3 - v5; + t.fail("foo - 1200 --> error"); + } catch (...) { + t.pass("foo - 1200 --> error"); + } // date - boolean -> date Variant v40 = v4 - v0; - t.is (v40.type (), Variant::type_date, "1234567890 - true --> date"); - t.is (v40.get_date (), 1234567889, "1234567890 - true --> 1234567889"); + t.is(v40.type(), Variant::type_date, "1234567890 - true --> date"); + t.is(v40.get_date(), 1234567889, "1234567890 - true --> 1234567889"); // date - integer -> date Variant v41 = v4 - v1; - t.is (v41.type (), Variant::type_date, "1234567890 - 42 --> date"); - t.is (v41.get_date (), 1234567848, "1234567890 - 42 --> 1234567848"); + t.is(v41.type(), Variant::type_date, "1234567890 - 42 --> date"); + t.is(v41.get_date(), 1234567848, "1234567890 - 42 --> 1234567848"); // date - real -> date Variant v42 = v4 - v2; - t.is (v42.type (), Variant::type_date, "1234567890 - 3.14 --> date"); - t.is (v42.get_date (), 1234567887, "1234567890 - 3.14 --> 1234567887"); + t.is(v42.type(), Variant::type_date, "1234567890 - 3.14 --> date"); + t.is(v42.get_date(), 1234567887, "1234567890 - 3.14 --> 1234567887"); // date - string -> string - try {Variant v43 = v4 - v3; t.fail ("1234567890 - foo --> error");} - catch (...) {t.pass ("1234567890 - foo --> error");} + try { + Variant v43 = v4 - v3; + t.fail("1234567890 - foo --> error"); + } catch (...) { + t.pass("1234567890 - foo --> error"); + } // date - date -> duration Variant v44 = v4 - v4; - t.is (v44.type (), Variant::type_duration, "1234567890 - 1234567890 --> duration"); - t.is (v44.get_duration (), 0, "1234567890 - 1234567890 --> 0"); + t.is(v44.type(), Variant::type_duration, "1234567890 - 1234567890 --> duration"); + t.is(v44.get_duration(), 0, "1234567890 - 1234567890 --> 0"); // date - duration -> date Variant v45 = v4 - v5; - t.is (v45.type (), Variant::type_date, "1234567890 - 1200 --> date"); - t.is (v45.get_date (), 1234566690, "1234567890 - 1200 --> 1234566690"); + t.is(v45.type(), Variant::type_date, "1234567890 - 1200 --> date"); + t.is(v45.get_date(), 1234566690, "1234567890 - 1200 --> 1234566690"); // duration - boolean -> duration Variant v50 = v5 - v0; - t.is (v50.type (), Variant::type_duration, "1200 - true --> duration"); - t.is (v50.get_duration (), 1199, "1200 - true --> 1199"); + t.is(v50.type(), Variant::type_duration, "1200 - true --> duration"); + t.is(v50.get_duration(), 1199, "1200 - true --> 1199"); // duration - integer -> duration Variant v51 = v5 - v1; - t.is (v51.type (), Variant::type_duration, "1200 - 42 --> duration"); - t.is (v51.get_duration (), 1158, "1200 - 42 --> 1158"); + t.is(v51.type(), Variant::type_duration, "1200 - 42 --> duration"); + t.is(v51.get_duration(), 1158, "1200 - 42 --> 1158"); // duration - real -> duration Variant v52 = v5 - v2; - t.is (v52.type (), Variant::type_duration, "1200 - 3.14 --> duration"); - t.is (v52.get_duration (), 1197, "1200 - 3.14 --> 1197"); + t.is(v52.type(), Variant::type_duration, "1200 - 3.14 --> duration"); + t.is(v52.get_duration(), 1197, "1200 - 3.14 --> 1197"); // duration - string -> ERROR - try {Variant v53 = v5 - v3; t.fail ("1200 - foo --> error");} - catch (...) {t.pass ("1200 - foo --> error");} + try { + Variant v53 = v5 - v3; + t.fail("1200 - foo --> error"); + } catch (...) { + t.pass("1200 - foo --> error"); + } // duration - date -> ERROR - try {Variant v54 = v5 - v4; t.fail ("1200 - 1234567890 --> error");} - catch (...) {t.pass ("1200 - 1234567890 --> error");} + try { + Variant v54 = v5 - v4; + t.fail("1200 - 1234567890 --> error"); + } catch (...) { + t.pass("1200 - 1234567890 --> error"); + } // duration - duration -> duration Variant v55 = v5 - v5; - t.is (v55.type (), Variant::type_duration, "1200 - 1200 --> duration"); - t.is (v55.get_duration (), 0, "1200 - 1200 --> 0"); + t.is(v55.type(), Variant::type_duration, "1200 - 1200 --> duration"); + t.is(v55.get_duration(), 0, "1200 - 1200 --> 0"); return 0; } diff --git a/test/variant_xor.test.cpp b/test/variant_xor.test.cpp index cd32ee24f..3e15fd212 100644 --- a/test/variant_xor.test.cpp +++ b/test/variant_xor.test.cpp @@ -27,173 +27,173 @@ #include // cmake.h include header must come first -#include -#include #include +#include + +#include //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (76); +int main(int, char**) { + UnitTest t(76); - Variant v0 (true); - Variant v1 (42); - Variant v2 (3.14); - Variant v3 ("foo"); - Variant v4 (1234567890, Variant::type_date); - Variant v5 (1200, Variant::type_duration); + Variant v0(true); + Variant v1(42); + Variant v2(3.14); + Variant v3("foo"); + Variant v4(1234567890, Variant::type_date); + Variant v5(1200, Variant::type_duration); // Truth table. - Variant vFalse (false); - Variant vTrue (true); - t.is (vFalse.operator_xor (vFalse), false, "false xor false --> false"); - t.is (vFalse.operator_xor (vTrue), true, "false xor true --> false"); - t.is (vTrue.operator_xor (vFalse), true, "true xor false --> false"); - t.is (vTrue.operator_xor (vTrue), false, "true xor true --> false"); + Variant vFalse(false); + Variant vTrue(true); + t.is(vFalse.operator_xor(vFalse), false, "false xor false --> false"); + t.is(vFalse.operator_xor(vTrue), true, "false xor true --> false"); + t.is(vTrue.operator_xor(vFalse), true, "true xor false --> false"); + t.is(vTrue.operator_xor(vTrue), false, "true xor true --> false"); - Variant v00 = v0.operator_xor (v0); - t.is (v00.type (), Variant::type_boolean, "true xor true --> boolean"); - t.is (v00.get_bool (), false, "true xor true --> false"); + Variant v00 = v0.operator_xor(v0); + t.is(v00.type(), Variant::type_boolean, "true xor true --> boolean"); + t.is(v00.get_bool(), false, "true xor true --> false"); - Variant v01 = v0.operator_xor (v1); - t.is (v01.type (), Variant::type_boolean, "true xor 42 --> boolean"); - t.is (v01.get_bool (), false, "true xor 42 --> false"); + Variant v01 = v0.operator_xor(v1); + t.is(v01.type(), Variant::type_boolean, "true xor 42 --> boolean"); + t.is(v01.get_bool(), false, "true xor 42 --> false"); - Variant v02 = v0.operator_xor (v2); - t.is (v02.type (), Variant::type_boolean, "true xor 3.14 --> boolean"); - t.is (v02.get_bool (), false, "true xor 3.14 --> false"); + Variant v02 = v0.operator_xor(v2); + t.is(v02.type(), Variant::type_boolean, "true xor 3.14 --> boolean"); + t.is(v02.get_bool(), false, "true xor 3.14 --> false"); - Variant v03 = v0.operator_xor (v3); - t.is (v03.type (), Variant::type_boolean, "true xor 'foo' --> boolean"); - t.is (v03.get_bool (), false, "true xor 'foo' --> false"); + Variant v03 = v0.operator_xor(v3); + t.is(v03.type(), Variant::type_boolean, "true xor 'foo' --> boolean"); + t.is(v03.get_bool(), false, "true xor 'foo' --> false"); - Variant v04 = v0.operator_xor (v4); - t.is (v04.type (), Variant::type_boolean, "true xor 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "true xor 1234567890 --> false"); + Variant v04 = v0.operator_xor(v4); + t.is(v04.type(), Variant::type_boolean, "true xor 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "true xor 1234567890 --> false"); - Variant v05 = v0.operator_xor (v5); - t.is (v05.type (), Variant::type_boolean, "true xor 1200 --> boolean"); - t.is (v05.get_bool (), false, "true xor 1200 --> false"); + Variant v05 = v0.operator_xor(v5); + t.is(v05.type(), Variant::type_boolean, "true xor 1200 --> boolean"); + t.is(v05.get_bool(), false, "true xor 1200 --> false"); - Variant v10 = v1.operator_xor (v0); - t.is (v10.type (), Variant::type_boolean, "42 xor true --> boolean"); - t.is (v10.get_bool (), false, "42 xor true --> false"); + Variant v10 = v1.operator_xor(v0); + t.is(v10.type(), Variant::type_boolean, "42 xor true --> boolean"); + t.is(v10.get_bool(), false, "42 xor true --> false"); - Variant v11 = v1.operator_xor (v1); - t.is (v11.type (), Variant::type_boolean, "42 xor 42 --> boolean"); - t.is (v11.get_bool (), false, "42 xor 42 --> false"); + Variant v11 = v1.operator_xor(v1); + t.is(v11.type(), Variant::type_boolean, "42 xor 42 --> boolean"); + t.is(v11.get_bool(), false, "42 xor 42 --> false"); - Variant v12 = v1.operator_xor (v2); - t.is (v12.type (), Variant::type_boolean, "42 xor 3.14 --> boolean"); - t.is (v12.get_bool (), false, "42 xor 3.14 --> false"); + Variant v12 = v1.operator_xor(v2); + t.is(v12.type(), Variant::type_boolean, "42 xor 3.14 --> boolean"); + t.is(v12.get_bool(), false, "42 xor 3.14 --> false"); - Variant v13 = v1.operator_xor (v3); - t.is (v13.type (), Variant::type_boolean, "42 xor 'foo' --> boolean"); - t.is (v13.get_bool (), false, "42 xor 'foo' --> false"); + Variant v13 = v1.operator_xor(v3); + t.is(v13.type(), Variant::type_boolean, "42 xor 'foo' --> boolean"); + t.is(v13.get_bool(), false, "42 xor 'foo' --> false"); - Variant v14 = v1.operator_xor (v4); - t.is (v04.type (), Variant::type_boolean, "42 xor 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "42 xor 1234567890 --> false"); + Variant v14 = v1.operator_xor(v4); + t.is(v04.type(), Variant::type_boolean, "42 xor 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "42 xor 1234567890 --> false"); - Variant v15 = v1.operator_xor (v5); - t.is (v15.type (), Variant::type_boolean, "42 xor 1200 --> boolean"); - t.is (v15.get_bool (), false, "42 xor 1200 --> false"); + Variant v15 = v1.operator_xor(v5); + t.is(v15.type(), Variant::type_boolean, "42 xor 1200 --> boolean"); + t.is(v15.get_bool(), false, "42 xor 1200 --> false"); - Variant v20 = v2.operator_xor (v0); - t.is (v20.type (), Variant::type_boolean, "3.14 xor true --> boolean"); - t.is (v20.get_bool (), false, "3.14 xor true --> false"); + Variant v20 = v2.operator_xor(v0); + t.is(v20.type(), Variant::type_boolean, "3.14 xor true --> boolean"); + t.is(v20.get_bool(), false, "3.14 xor true --> false"); - Variant v21 = v2.operator_xor (v1); - t.is (v21.type (), Variant::type_boolean, "3.14 xor 42 --> boolean"); - t.is (v21.get_bool (), false, "3.14 xor 42 --> false"); + Variant v21 = v2.operator_xor(v1); + t.is(v21.type(), Variant::type_boolean, "3.14 xor 42 --> boolean"); + t.is(v21.get_bool(), false, "3.14 xor 42 --> false"); - Variant v22 = v2.operator_xor (v2); - t.is (v22.type (), Variant::type_boolean, "3.14 xor 3.14 --> boolean"); - t.is (v22.get_bool (), false, "3.14 xor 3.14 --> false"); + Variant v22 = v2.operator_xor(v2); + t.is(v22.type(), Variant::type_boolean, "3.14 xor 3.14 --> boolean"); + t.is(v22.get_bool(), false, "3.14 xor 3.14 --> false"); - Variant v23 = v2.operator_xor (v3); - t.is (v23.type (), Variant::type_boolean, "3.14 xor 'foo' --> boolean"); - t.is (v23.get_bool (), false, "3.14 xor 'foo' --> false"); + Variant v23 = v2.operator_xor(v3); + t.is(v23.type(), Variant::type_boolean, "3.14 xor 'foo' --> boolean"); + t.is(v23.get_bool(), false, "3.14 xor 'foo' --> false"); - Variant v24 = v2.operator_xor (v4); - t.is (v24.type (), Variant::type_boolean, "3.14 xor 1234567890 --> boolean"); - t.is (v24.get_bool (), false, "3.14 xor 1234567890 --> false"); + Variant v24 = v2.operator_xor(v4); + t.is(v24.type(), Variant::type_boolean, "3.14 xor 1234567890 --> boolean"); + t.is(v24.get_bool(), false, "3.14 xor 1234567890 --> false"); - Variant v25 = v2.operator_xor (v5); - t.is (v25.type (), Variant::type_boolean, "3.14 xor 1200 --> boolean"); - t.is (v25.get_bool (), false, "3.14 xor 1200 --> false"); + Variant v25 = v2.operator_xor(v5); + t.is(v25.type(), Variant::type_boolean, "3.14 xor 1200 --> boolean"); + t.is(v25.get_bool(), false, "3.14 xor 1200 --> false"); - Variant v30 = v3.operator_xor (v0); - t.is (v30.type (), Variant::type_boolean, "'foo' xor true --> boolean"); - t.is (v30.get_bool (), false, "'foo' xor true --> false"); + Variant v30 = v3.operator_xor(v0); + t.is(v30.type(), Variant::type_boolean, "'foo' xor true --> boolean"); + t.is(v30.get_bool(), false, "'foo' xor true --> false"); - Variant v31 = v3.operator_xor (v1); - t.is (v31.type (), Variant::type_boolean, "'foo' xor 42 --> boolean"); - t.is (v31.get_bool (), false, "'foo' xor 42 --> false"); + Variant v31 = v3.operator_xor(v1); + t.is(v31.type(), Variant::type_boolean, "'foo' xor 42 --> boolean"); + t.is(v31.get_bool(), false, "'foo' xor 42 --> false"); - Variant v32 = v3.operator_xor (v2); - t.is (v32.type (), Variant::type_boolean, "'foo' xor 3.14 --> boolean"); - t.is (v32.get_bool (), false, "'foo' xor 3.14 --> false"); + Variant v32 = v3.operator_xor(v2); + t.is(v32.type(), Variant::type_boolean, "'foo' xor 3.14 --> boolean"); + t.is(v32.get_bool(), false, "'foo' xor 3.14 --> false"); - Variant v33 = v3.operator_xor (v3); - t.is (v33.type (), Variant::type_boolean, "'foo' xor 'foo' --> boolean"); - t.is (v33.get_bool (), false, "'foo' xor 'foo' --> false"); + Variant v33 = v3.operator_xor(v3); + t.is(v33.type(), Variant::type_boolean, "'foo' xor 'foo' --> boolean"); + t.is(v33.get_bool(), false, "'foo' xor 'foo' --> false"); - Variant v34 = v3.operator_xor (v4); - t.is (v34.type (), Variant::type_boolean, "'foo' xor 1234567890 --> boolean"); - t.is (v34.get_bool (), false, "'foo' xor 1234567890 --> false"); + Variant v34 = v3.operator_xor(v4); + t.is(v34.type(), Variant::type_boolean, "'foo' xor 1234567890 --> boolean"); + t.is(v34.get_bool(), false, "'foo' xor 1234567890 --> false"); - Variant v35 = v3.operator_xor (v5); - t.is (v35.type (), Variant::type_boolean, "'foo' xor 1200 --> boolean"); - t.is (v35.get_bool (), false, "'foo' xor 1200 --> false"); + Variant v35 = v3.operator_xor(v5); + t.is(v35.type(), Variant::type_boolean, "'foo' xor 1200 --> boolean"); + t.is(v35.get_bool(), false, "'foo' xor 1200 --> false"); - Variant v40 = v4.operator_xor (v0); - t.is (v40.type (), Variant::type_boolean, "1234567890 xor true --> boolean"); - t.is (v40.get_bool (), false, "1234567890 xor true --> false"); + Variant v40 = v4.operator_xor(v0); + t.is(v40.type(), Variant::type_boolean, "1234567890 xor true --> boolean"); + t.is(v40.get_bool(), false, "1234567890 xor true --> false"); - Variant v41 = v4.operator_xor (v1); - t.is (v41.type (), Variant::type_boolean, "1234567890 xor 42 --> boolean"); - t.is (v41.get_bool (), false, "1234567890 xor 42 --> false"); + Variant v41 = v4.operator_xor(v1); + t.is(v41.type(), Variant::type_boolean, "1234567890 xor 42 --> boolean"); + t.is(v41.get_bool(), false, "1234567890 xor 42 --> false"); - Variant v42 = v4.operator_xor (v2); - t.is (v42.type (), Variant::type_boolean, "1234567890 xor 3.14 --> boolean"); - t.is (v42.get_bool (), false, "1234567890 xor 3.14 --> false"); + Variant v42 = v4.operator_xor(v2); + t.is(v42.type(), Variant::type_boolean, "1234567890 xor 3.14 --> boolean"); + t.is(v42.get_bool(), false, "1234567890 xor 3.14 --> false"); - Variant v43 = v4.operator_xor (v3); - t.is (v43.type (), Variant::type_boolean, "1234567890 xor 'foo' --> boolean"); - t.is (v43.get_bool (), false, "1234567890 xor 'foo' --> false"); + Variant v43 = v4.operator_xor(v3); + t.is(v43.type(), Variant::type_boolean, "1234567890 xor 'foo' --> boolean"); + t.is(v43.get_bool(), false, "1234567890 xor 'foo' --> false"); - Variant v44 = v4.operator_xor (v4); - t.is (v44.type (), Variant::type_boolean, "1234567890 xor 1234567890 --> boolean"); - t.is (v44.get_bool (), false, "1234567890 xor 1234567890 --> false"); + Variant v44 = v4.operator_xor(v4); + t.is(v44.type(), Variant::type_boolean, "1234567890 xor 1234567890 --> boolean"); + t.is(v44.get_bool(), false, "1234567890 xor 1234567890 --> false"); - Variant v45 = v4.operator_xor (v5); - t.is (v45.type (), Variant::type_boolean, "1234567890 xor 1200 --> boolean"); - t.is (v45.get_bool (), false, "1234567890 xor 1200 --> false"); + Variant v45 = v4.operator_xor(v5); + t.is(v45.type(), Variant::type_boolean, "1234567890 xor 1200 --> boolean"); + t.is(v45.get_bool(), false, "1234567890 xor 1200 --> false"); - Variant v50 = v5.operator_xor (v0); - t.is (v50.type (), Variant::type_boolean, "1200 xor true --> boolean"); - t.is (v50.get_bool (), false, "1200 xor true --> false"); + Variant v50 = v5.operator_xor(v0); + t.is(v50.type(), Variant::type_boolean, "1200 xor true --> boolean"); + t.is(v50.get_bool(), false, "1200 xor true --> false"); - Variant v51 = v5.operator_xor (v1); - t.is (v51.type (), Variant::type_boolean, "1200 xor 42 --> boolean"); - t.is (v51.get_bool (), false, "1200 xor 42 --> false"); + Variant v51 = v5.operator_xor(v1); + t.is(v51.type(), Variant::type_boolean, "1200 xor 42 --> boolean"); + t.is(v51.get_bool(), false, "1200 xor 42 --> false"); - Variant v52 = v5.operator_xor (v2); - t.is (v52.type (), Variant::type_boolean, "1200 xor 3.14 --> boolean"); - t.is (v52.get_bool (), false, "1200 xor 3.14 --> false"); + Variant v52 = v5.operator_xor(v2); + t.is(v52.type(), Variant::type_boolean, "1200 xor 3.14 --> boolean"); + t.is(v52.get_bool(), false, "1200 xor 3.14 --> false"); - Variant v53 = v5.operator_xor (v3); - t.is (v53.type (), Variant::type_boolean, "1200 xor 'foo' --> boolean"); - t.is (v53.get_bool (), false, "1200 xor 'foo' --> false"); + Variant v53 = v5.operator_xor(v3); + t.is(v53.type(), Variant::type_boolean, "1200 xor 'foo' --> boolean"); + t.is(v53.get_bool(), false, "1200 xor 'foo' --> false"); - Variant v54 = v5.operator_xor (v4); - t.is (v04.type (), Variant::type_boolean, "1200 xor 1234567890 --> boolean"); - t.is (v04.get_bool (), false, "1200 xor 1234567890 --> false"); + Variant v54 = v5.operator_xor(v4); + t.is(v04.type(), Variant::type_boolean, "1200 xor 1234567890 --> boolean"); + t.is(v04.get_bool(), false, "1200 xor 1234567890 --> false"); - Variant v55 = v5.operator_xor (v5); - t.is (v55.type (), Variant::type_boolean, "1200 xor 1200 --> boolean"); - t.is (v55.get_bool (), false, "1200 xor 1200 --> false"); + Variant v55 = v5.operator_xor(v5); + t.is(v55.type(), Variant::type_boolean, "1200 xor 1200 --> boolean"); + t.is(v55.get_bool(), false, "1200 xor 1200 --> false"); return 0; } diff --git a/test/verbose.test.py b/test/verbose.test.py index b38ae25de..06b8b69bf 100755 --- a/test/verbose.test.py +++ b/test/verbose.test.py @@ -57,14 +57,16 @@ class TestVerbosity(TestCase): def test_verbosity_new_uuid(self): """Verbosity new-uuid""" code, out, err = self.t(("rc.verbose:new-uuid", "add", "Sample1")) - self.assertRegex(out, r"Created task [0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") + self.assertRegex( + out, + r"Created task [0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}", + ) def test_verbosity_label(self): """Verbosity label""" code, out, err = self.t("rc.verbose:label ls") self.assertRegex( - out, - "ID.+A.+D.+Project.+Tags.+R.+Wait.+S.+Due.+Until.+Description" + out, "ID.+A.+D.+Project.+Tags.+R.+Wait.+S.+Due.+Until.+Description" ) def test_verbosity_affected(self): @@ -86,14 +88,17 @@ class TestVerbosity(TestCase): """Verbosity special""" code, out, err = self.t("rc.verbose:special 1 mod +next") - self.assertIn("The 'next' special tag will boost the urgency of this " - "task so it appears on the 'next' report.", out) + self.assertIn( + "The 'next' special tag will boost the urgency of this " + "task so it appears on the 'next' report.", + out, + ) def test_verbosity_blank(self): """Verbosity blank""" def count_blank_lines(x): - return x.splitlines().count('') + return x.splitlines().count("") code, out, err = self.t("rc.verbose:nothing ls") self.assertEqual(count_blank_lines(out), 0) @@ -142,6 +147,7 @@ class TestVerbosity(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/version.test.py b/test/version.test.py index 3d74bd1c7..6d751a643 100755 --- a/test/version.test.py +++ b/test/version.test.py @@ -30,6 +30,7 @@ import os import unittest import re from datetime import datetime + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -58,7 +59,7 @@ class TestVersion(TestCase): def slurp(self, file=os.path.join(SOURCE_DIR, "CMakeLists.txt")): number = "\.".join(["[0-9]+"] * 3) - ver = re.compile("^ *VERSION ({0}[^\"]*)$".format(number)) + ver = re.compile('^ *VERSION ({0}[^"]*)$'.format(number)) with open(file) as fh: for line in fh: match = ver.match(line) @@ -85,12 +86,11 @@ class TestVersion(TestCase): # If we are within a git repository, check the tag version if os.path.exists("../.git"): - if 2 >= len(version) > 0: - git = version[1] - self.assertRegex(git, r'\([a-f0-9]*\)') - else: - raise ValueError("Unexpected output from _version '{0}'".format( - out)) + if 2 >= len(version) > 0: + git = version[1] + self.assertRegex(git, r"\([a-f0-9]*\)") + else: + raise ValueError("Unexpected output from _version '{0}'".format(out)) ver = version[0] ver_expected = self.slurp() @@ -102,11 +102,12 @@ class TestVersion(TestCase): def test_version_option(self): """Verify that 'task --version' returns something valid""" code, out, err = self.t("--version") - self.assertRegex(out, r'^\d\.\d+\.\d+(\.\w+)?$') + self.assertRegex(out, r"^\d\.\d+\.\d+(\.\w+)?$") if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/test/view.test.cpp b/test/view.test.cpp index 59f668298..0d8b05acc 100644 --- a/test/view.test.cpp +++ b/test/view.test.cpp @@ -27,138 +27,138 @@ #include // cmake.h include header must come first -#include -#include -#include -#include -#include #include +#include #include #include -#include #include +#include +#include +#include +#include + +#include Context context; extern std::string configurationDefaults; //////////////////////////////////////////////////////////////////////////////// -int main (int, char**) -{ - UnitTest t (1); +int main(int, char**) { + UnitTest t(1); Context context; Context::setContext(&context); // Ensure environment has no influence. - unsetenv ("TASKDATA"); - unsetenv ("TASKRC"); + unsetenv("TASKDATA"); + unsetenv("TASKRC"); - try - { + try { // Set up configuration. - context.config.parse (configurationDefaults); - context.config.set ("fontunderline", true); - context.config.set ("tag.indicator", "+"); - context.config.set ("dependency.indicator", "D"); - context.config.set ("recurrence.indicator", "R"); - context.config.set ("active.indicator", "A"); - context.config.set ("dateformat", "Y-M-D"); - context.config.set ("indent.annotation", "2"); + context.config.parse(configurationDefaults); + context.config.set("fontunderline", true); + context.config.set("tag.indicator", "+"); + context.config.set("dependency.indicator", "D"); + context.config.set("recurrence.indicator", "R"); + context.config.set("active.indicator", "A"); + context.config.set("dateformat", "Y-M-D"); + context.config.set("indent.annotation", "2"); // Two sample tasks. t.ok(true, "zero"); - Task t1 ("{" - "\"status\":\"pending\", " - "\"uuid\":\"2a64f6e0-bf8e-430d-bf71-9ec3f0d9b661\", " - "\"description\":\"This is the description text\", " - "\"project\":\"Home\", " - "\"priority\":\"H\", " - "\"annotation_1234567890\":\"This is an annotation\", " - "\"start\":\"1234567890\", " - "\"due\":\"1234567890\", " - "\"tags\":\"one,two\"" - "}"); + Task t1( + "{" + "\"status\":\"pending\", " + "\"uuid\":\"2a64f6e0-bf8e-430d-bf71-9ec3f0d9b661\", " + "\"description\":\"This is the description text\", " + "\"project\":\"Home\", " + "\"priority\":\"H\", " + "\"annotation_1234567890\":\"This is an annotation\", " + "\"start\":\"1234567890\", " + "\"due\":\"1234567890\", " + "\"tags\":\"one,two\"" + "}"); t1.id = 1; t.ok(true, "one"); - Task t2 ("{" - "\"status\":\"pending\", " - "\"uuid\":\"f30cb9c3-3fc0-483f-bfb2-3bf134f00694\", " - "\"description\":\"This is the description text\", " - "\"project\":\"Garden Care\", " - "\"recur\":\"monthly\", " - "\"depends\":\"2a64f6e0-bf8e-430d-bf71-9ec3f0d9b661\"" - "}"); + Task t2( + "{" + "\"status\":\"pending\", " + "\"uuid\":\"f30cb9c3-3fc0-483f-bfb2-3bf134f00694\", " + "\"description\":\"This is the description text\", " + "\"project\":\"Garden Care\", " + "\"recur\":\"monthly\", " + "\"depends\":\"2a64f6e0-bf8e-430d-bf71-9ec3f0d9b661\"" + "}"); t2.id = 11; t.ok(true, "two"); - Task t3 ("{" - "\"status\":\"pending\", " - "\"uuid\":\"c44cb9c3-3fc0-483f-bfb2-3bf134f05554\", " - "\"description\":\"Another description\", " - "\"project\":\"Garden\"" - "}"); + Task t3( + "{" + "\"status\":\"pending\", " + "\"uuid\":\"c44cb9c3-3fc0-483f-bfb2-3bf134f05554\", " + "\"description\":\"Another description\", " + "\"project\":\"Garden\"" + "}"); t3.id = 8; t.ok(true, "three"); - std::vector data; - data.push_back (t1); - data.push_back (t2); - data.push_back (t3); + std::vector data; + data.push_back(t1); + data.push_back(t2); + data.push_back(t3); // Sequence of tasks. - std::vector sequence; - sequence.push_back (0); - sequence.push_back (1); - sequence.push_back (2); + std::vector sequence; + sequence.push_back(0); + sequence.push_back(1); + sequence.push_back(2); - sort_tasks (data, sequence, "description+,id-"); + sort_tasks(data, sequence, "description+,id-"); // Create colors. - Color header_color (Color (Color::yellow, Color::nocolor, false, false, false)); - Color odd_color ("on gray1"); - Color even_color ("on gray0"); + Color header_color(Color(Color::yellow, Color::nocolor, false, false, false)); + Color odd_color("on gray1"); + Color even_color("on gray0"); // Create a view. std::string report = "view.t"; ViewTask view; - view.add (Column::factory ("id", report)); - view.add (Column::factory ("uuid.short", report)); - view.add (Column::factory ("project", report)); - view.add (Column::factory ("priority", report)); - view.add (Column::factory ("tags", report)); - view.add (Column::factory ("tags.count", report)); - view.add (Column::factory ("description", report)); - view.add (Column::factory ("depends.indicator", report)); - view.add (Column::factory ("recur.indicator", report)); - view.add (Column::factory ("status.short", report)); - view.add (Column::factory ("due.countdown", report)); - view.add (Column::factory ("start.active", report)); - view.add (Column::factory ("urgency", report)); - view.width (context.getWidth ()); - view.leftMargin (4); - view.extraPadding (0); - view.intraPadding (1); - view.colorHeader (header_color); - view.colorOdd (odd_color); - view.colorEven (even_color); - view.intraColorOdd (odd_color); - view.intraColorEven (even_color); + view.add(Column::factory("id", report)); + view.add(Column::factory("uuid.short", report)); + view.add(Column::factory("project", report)); + view.add(Column::factory("priority", report)); + view.add(Column::factory("tags", report)); + view.add(Column::factory("tags.count", report)); + view.add(Column::factory("description", report)); + view.add(Column::factory("depends.indicator", report)); + view.add(Column::factory("recur.indicator", report)); + view.add(Column::factory("status.short", report)); + view.add(Column::factory("due.countdown", report)); + view.add(Column::factory("start.active", report)); + view.add(Column::factory("urgency", report)); + view.width(context.getWidth()); + view.leftMargin(4); + view.extraPadding(0); + view.intraPadding(1); + view.colorHeader(header_color); + view.colorOdd(odd_color); + view.colorEven(even_color); + view.intraColorOdd(odd_color); + view.intraColorEven(even_color); // Render the view. - std::cout << view.render (data, sequence); + std::cout << view.render(data, sequence); int expected_lines = 5; - if (!isatty (fileno (stdout))) - expected_lines = 6; + if (!isatty(fileno(stdout))) expected_lines = 6; - t.is (view.lines (), expected_lines, "View::lines == 5"); + t.is(view.lines(), expected_lines, "View::lines == 5"); // Now render a string-only grid. - context.config.set ("fontunderline", false); - Color single_cell ("bold white on red"); + context.config.set("fontunderline", false); + Color single_cell("bold white on red"); } - catch (const std::string& e) - { - t.fail ("Exception thrown."); - t.diag (e); + catch (const std::string& e) { + t.fail("Exception thrown."); + t.diag(e); } return 0; diff --git a/test/wait.test.py b/test/wait.test.py index a88f5dc76..adc609730 100755 --- a/test/wait.test.py +++ b/test/wait.test.py @@ -28,6 +28,7 @@ import sys import os import unittest + # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -86,19 +87,19 @@ class Test1486(TestCase): def test_waiting(self): """1486: Verify waiting report shows waiting tasks""" - self.t('add regular') - self.t('add waited and pending wait:later') - self.t('add waited but deleted wait:later') - self.t('add waited but done wait:later') - self.t('rc.confirmation=off 3 delete') - self.t('4 done') + self.t("add regular") + self.t("add waited and pending wait:later") + self.t("add waited but deleted wait:later") + self.t("add waited but done wait:later") + self.t("rc.confirmation=off 3 delete") + self.t("4 done") - code, out, err = self.t('waiting') + code, out, err = self.t("waiting") self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code)) - self.assertIn('waited and pending', out) - self.assertNotIn('waited but deleted', out) - self.assertNotIn('waited but done', out) - self.assertNotIn('regular', out) + self.assertIn("waited and pending", out) + self.assertNotIn("waited but deleted", out) + self.assertNotIn("waited but done", out) + self.assertNotIn("regular", out) class TestFeature2563(TestCase): @@ -129,6 +130,7 @@ class TestFeature2563(TestCase): if __name__ == "__main__": from simpletap import TAPTestRunner + unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4 ft=python diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 7bf176d4c..ffcbbe838 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xtask" -version = "0.4.1" +version = "0.4.1" edition = "2021" [dependencies] From 4d058a5c5ab392ca524c60c2ddfe5d1c33efd7e3 Mon Sep 17 00:00:00 2001 From: Felix Schurk Date: Mon, 29 Jul 2024 22:36:03 +0200 Subject: [PATCH 093/242] add .git-blame-ignore-revs file to hide formating changes in git blame --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..8c8b72b3e --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# initial bulk run of formatting with pre-commit +93356b39c3086fdf8dd41d7357bb1c115ff69cb1 From 160be698520c97ab271e582e9b6774945237017a Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 4 Aug 2024 12:37:18 -0400 Subject: [PATCH 094/242] Update to docker compose v2 (#3577) --- .github/workflows/tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 85e36b216..0dd06b8e4 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -133,10 +133,10 @@ jobs: GITHUB_USER: ${{ github.actor }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CONTAINER: ${{ matrix.dockerfile }} - run: docker-compose build test-${{ env.CONTAINER }} + run: docker compose build test-${{ env.CONTAINER }} - name: Test ${{ matrix.name }} - run: docker-compose run test-${{ env.CONTAINER }} + run: docker compose run test-${{ env.CONTAINER }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CONTAINER: ${{ matrix.dockerfile }} From 5c6cc3e5229676655e93264e24a146a26b980e45 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 4 Aug 2024 12:51:11 -0400 Subject: [PATCH 095/242] Release 3.1.0 (#3574) --- CMakeLists.txt | 2 +- ChangeLog | 40 +++++++++++++++++++++++++++++++++++++--- src/commands/CmdNews.cpp | 30 +++++++++++++++++++----------- 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c1187509..8a9d0e896 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ enable_testing() set (CMAKE_EXPORT_COMPILE_COMMANDS ON) project (task - VERSION 3.0.2 + VERSION 3.1.0 DESCRIPTION "Taskwarrior - a command-line TODO list manager" HOMEPAGE_URL https://taskwarrior.org/) diff --git a/ChangeLog b/ChangeLog index 6a8b78bb5..7b3e87c8e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,13 +1,47 @@ ------ current release --------------------------- +3.1.0 - + + - Support for `task purge` has been restored, and new support added for automatically + expiring old tasks. (#3540, #3546, #3556) + - `task news` is now better behaved, and can be completely disabled. + - Multiple imports of the same UUID will now generate a warning. (#3560) + - The `sync.server.url` config replaces `sync.server.origin` and allows a URL + containing a path. (#3423) + - The new `bubblegum-256.theme` has improved legibility and contrast over + others. (#3505) + - Warnings regarding `.data` files are only show for reports. (#3473) + - Inherited urgency is correctly calculated to make parents more urgent than + children (#2941) + - Task completion commands no longer trigger hooks (#3133) + +Thanks to the following people for contributions to this release: + + - Adrian Galilea + - Adrian Sadłocha + - Andonome + - Christian Clauss + - Dominik Rehák + - Dustin J. Mitchell + - Felix Schurk + - Hector Dearman + - Joseph Coffa + - koleesch + - Maarten Aertsen + - mattsmida + - Philipp Oberdiek + - Sebastian Carlos + - sleepy_nols + - Steve Dondley + - Will R S Hansen + +------ old releases ------------------------------ + 3.0.2 - - Fix an accidentally-included debug print which polluted output of reports with the Taskwarrior version (#3389) ------- old releases ------------------------------ - - 3.0.1 - - Fix an error in creation of the 3.0.0 tarball which caused builds to fail (#3302) diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index cb1e8a3c0..7a3fbb7dc 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -470,25 +470,34 @@ void NewsItem::version3_0_0(std::vector& items) { void NewsItem::version3_1_0(std::vector& items) { Version version("3.1.0"); - NewsItem sync{ + NewsItem purge{ version, /*title=*/"Purging Tasks, Manually or Automatically", /*bg_title=*/"", /*background=*/"", /*punchline=*/ - "Support for `task purge` has been restored, and new support added for automatically " - "expiring\n" - "old tasks.\n\n" + "Support for `task purge` has been restored, and new support added for automatically\n" + "expiring old tasks.\n\n", /*update=*/ - "The `task purge` command removes tasks entirely, in contrast to `task delete` which merely " - "sets\n" - "the task status to 'Deleted'. This functionality existed in versions 2.x but was " - "temporarily\n" - "removed in 3.0.\n\n" + "The `task purge` command removes tasks entirely, in contrast to `task delete` which merely\n" + "sets the task status to 'Deleted'. This functionality existed in versions 2.x but was\n" + "temporarily removed in 3.0.\n\n" "The new `purge.on-sync` configuration parameter controls automatic purging of old tasks.\n" "An old task is one with status 'Deleted' that has not been modified in 180 days. This\n" "functionality is optional and not enabled by default."}; - items.push_back(sync); + items.push_back(purge); + NewsItem news{ + version, + /*title=*/"Improved 'task news'", + /*bg_title=*/"", + /*background=*/"", + /*punchline=*/ + "The news you are reading now is improved.\n\n", + /*update=*/ + "The `task news` command now always shows all new information, not just 'major' news,\n" + "and will only show that news once. New installs will assume all news has been read.\n" + "Finally, news can be completely hidden by removing 'news' from the 'verbose' config."}; + items.push_back(news); } //////////////////////////////////////////////////////////////////////////////// @@ -555,7 +564,6 @@ int CmdNews::execute(std::string& output) { // Set a mark in the config to remember which version's release notes were displayed if (news_version != current_version) { - std::cout << "UPDATING\n"; CmdConfig::setConfigVariable("news.version", std::string(current_version), false); // Revert back to default signal handling after displaying the outro From c0b708d1f3e1584174175574d7fe06a2f765b28d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:20:35 -0400 Subject: [PATCH 096/242] [pre-commit.ci] pre-commit autoupdate (#3587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 24.4.2 → 24.8.0](https://github.com/psf/black/compare/24.4.2...24.8.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e17239bcc..013d08459 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,6 @@ repos: - id: clang-format types_or: [c++, c] - repo: https://github.com/psf/black - rev: 24.4.2 + rev: 24.8.0 hooks: - id: black From 17889a3f25b509e314f644c6b268281e961765a1 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 6 Aug 2024 20:44:12 -0400 Subject: [PATCH 097/242] Actually run shell tests (#3583) Two of these used EXPFAIL which, because nothing is interpreting the TAP output, does not work. So, that functionality is removed, and the expected-to-fail bits are commented out or removed. There was a conditional on the filename in `bash_tap.sh` which caused it to not actually do anything and just run the test as a simple shell script. That, too, has been removed. --- test/CMakeLists.txt | 40 ++++++++++++++++++++++++-- test/bash_tap.sh | 68 ++++++++++++++++++-------------------------- test/bash_tap_tw.sh | 3 +- test/tw-2124.test.sh | 6 ---- test/tw-2514.test.sh | 6 ++-- 5 files changed, 69 insertions(+), 54 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 57c81bf5f..5af30aba0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -60,9 +60,6 @@ foreach (src_FILE ${test_SRCS}) ) endforeach (src_FILE) -configure_file(bash_tap.sh bash_tap.sh COPYONLY) -configure_file(bash_tap_tw.sh bash_tap_tw.sh COPYONLY) - add_subdirectory(basetest) add_subdirectory(simpletap) @@ -198,3 +195,40 @@ foreach (python_Test ${pythonTests}) WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) endforeach(python_Test) + +# -- Shell tests + +set (shell_SRCS + tw-1637.test.sh + tw-1643.test.sh + tw-1688.test.sh + tw-1715.test.sh + tw-1718.test.sh + tw-1804.test.sh + tw-1883.test.sh + tw-1895.test.sh + tw-1938.test.sh + tw-2124.test.sh + tw-2189.test.sh + tw-2257.test.sh + tw-2386.test.sh + tw-2392.test.sh + tw-2429.test.sh + tw-2451.test.sh + tw-2514.test.sh + tw-2530.test.sh + tw-2550.test.sh + tw-2581.test.sh + tw-3102.test.sh + tw-3109.test.sh +) + +configure_file(bash_tap.sh bash_tap.sh COPYONLY) +configure_file(bash_tap_tw.sh bash_tap_tw.sh COPYONLY) + +foreach (shell_Test ${shell_SRCS}) + add_test(NAME ${shell_Test} + COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/${shell_Test} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +endforeach(shell_Test) diff --git a/test/bash_tap.sh b/test/bash_tap.sh index ebf51684a..c60343ca1 100644 --- a/test/bash_tap.sh +++ b/test/bash_tap.sh @@ -8,13 +8,7 @@ function bashtap_on_error { # $bashtap_line contains the last executed line, or an error. echo -n "$bashtap_output" - # Determine if this failure was expected - if [[ ! -z "$EXPFAIL" ]] - then - todo_suffix=" # TODO" - fi - - echo "not ok 1 - ${bashtap_line}${todo_suffix}" + echo "not ok 1 - ${bashtap_line}" bashtap_clean_tmpdir } @@ -81,41 +75,35 @@ function bashtap_get_absolute_path { bashtap_org_pwd=$(pwd) bashtap_org_script=$(bashtap_get_absolute_path "$0") -if [ "${0:(-2)}" == ".t" ] || [ "$1" == "-t" ]; then - # Make sure any failing commands are caught. - set -e - set -o pipefail +# Make sure any failing commands are caught. +set -e +set -o pipefail - # TAP header. Hardcoded number of tests, 1. - echo "1..1" +# TAP header. Hardcoded number of tests, 1. +echo "1..1" - # Output TAP failure on early exit. - trap bashtap_on_error EXIT +# Output TAP failure on early exit. +trap bashtap_on_error EXIT - # The different calls to mktemp are necessary for OSX compatibility. - bashtap_tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'bash_tap') - if [ ! -z "$bashtap_tmpdir" ]; then - cd "$bashtap_tmpdir" - else - bashtap_line="Unable to create temporary directory." - exit 1 - fi - - # Scripts sourced before bash_tap.sh may declare this function. - if declare -f bashtap_setup >/dev/null; then - bashtap_setup - fi - - # Run test file interpreting failing commands as a test failure. - bashtap_run_testcase && echo "ok 1" - - # Since we're in a sourced file and just ran the parent script, - # exit without running it a second time. - trap - EXIT - bashtap_clean_tmpdir - exit +# The different calls to mktemp are necessary for OSX compatibility. +bashtap_tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'bash_tap') +if [ ! -z "$bashtap_tmpdir" ]; then + cd "$bashtap_tmpdir" else - if declare -f bashtap_setup >/dev/null; then - bashtap_setup - fi + bashtap_line="Unable to create temporary directory." + exit 1 fi + +# Scripts sourced before bash_tap.sh may declare this function. +if declare -f bashtap_setup >/dev/null; then + bashtap_setup +fi + +# Run test file interpreting failing commands as a test failure. +bashtap_run_testcase && echo "ok 1" + +# Since we're in a sourced file and just ran the parent script, +# exit without running it a second time. +trap - EXIT +bashtap_clean_tmpdir +exit diff --git a/test/bash_tap_tw.sh b/test/bash_tap_tw.sh index 4cdf6033e..98da797c9 100644 --- a/test/bash_tap_tw.sh +++ b/test/bash_tap_tw.sh @@ -13,7 +13,7 @@ function setup_taskrc { # Configuration - for i in pending.data completed.data undo.data backlog.data taskrc; do + for i in taskchampion.sqlite3 taskrc; do if [ -f "$i" ]; then rm "$i" 2>&1 >/dev/null fi @@ -26,6 +26,7 @@ function setup_taskrc { echo 'color.header=rgb025' >> taskrc echo 'color.footer=rgb025' >> taskrc echo 'color.error=bold white on red' >> taskrc + echo 'news.version=99.0.0' >> taskrc } function find_task_binary { diff --git a/test/tw-2124.test.sh b/test/tw-2124.test.sh index 2c75a63a1..34ccf1cfc 100755 --- a/test/tw-2124.test.sh +++ b/test/tw-2124.test.sh @@ -7,9 +7,3 @@ # Filtering for description with a dash works task add foo-bar task foo-bar list | grep foo-bar - -# Filtering for tag with dash does not work right now -export EXPFAIL=true - -task add test +one-two -task +one-two list diff --git a/test/tw-2514.test.sh b/test/tw-2514.test.sh index 47a93fa20..71343ed6e 100755 --- a/test/tw-2514.test.sh +++ b/test/tw-2514.test.sh @@ -7,8 +7,6 @@ task 1 mod start:yesterday+18h task 1 done end:yesterday+20h # this does not work without journal.info -export EXPFAIL=true - # Check that 2 hour interval is reported by task info -task info | grep -F "Start deleted" -[[ ! -z `task info | grep -F "Start deleted (duration: 2:00:00)."` ]] +#task info | grep -F "Start deleted" +#[[ ! -z `task info | grep -F "Start deleted (duration: 2:00:00)."` ]] From 49e09a9783e6c2a0751d086c906996e77d2c1a33 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 7 Aug 2024 23:11:56 -0400 Subject: [PATCH 098/242] Remove accidentally-included sqlite3 file (#3589) --- test/taskchampion.sqlite3 | Bin 32768 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/taskchampion.sqlite3 diff --git a/test/taskchampion.sqlite3 b/test/taskchampion.sqlite3 deleted file mode 100644 index 5eac7974080f673a711b79acad48d56f00cece28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI&PivcC9Kdm(Hqma7q&IiVQ*ud27=&GR88jPG!rHX6?hqJ=Yn}~BqHWZ*g~7;M z*qhi}*~^--HI0Yv;7#}n#^;aE@A>(8ygZo8Pp5vY6ss!`sU0Jkr)1Rt!b~_SB=(N&1n49TEAPz%|ESrv)}xgRyhzr009ILKmY**5I_I{ z1WE{ahGBL(V*MiazAn^lxSIRHcQ;b;PVwM_ZH*jDI-~cemfY!(U4JIW6URESYMpujq#IG!OX4uAyICZ6m|cu7`-a(Wi?xwW9^D65?oy>)d7)NFV%sdcb9MhX zT``Xz-Xc+XBEy<)TJ7f&*+z4R+hRRkqHJ$`v8S8;zL-Ab_3#Xlx=B=Ur5+zCukIIf zEZ*A*SEES0&fB*srAbz^+5I_I{1Q0*~0R#|0pjrZa|F711ku3rUAb Date: Fri, 9 Aug 2024 19:05:19 +0200 Subject: [PATCH 099/242] Optionally use system provided corrosion (#3590) --- src/tc/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tc/CMakeLists.txt b/src/tc/CMakeLists.txt index ff19a08cd..907ecbad6 100644 --- a/src/tc/CMakeLists.txt +++ b/src/tc/CMakeLists.txt @@ -1,6 +1,11 @@ cmake_minimum_required (VERSION 3.22) -add_subdirectory(${CMAKE_SOURCE_DIR}/src/tc/corrosion) +OPTION(SYSTEM_CORROSION "Use system provided corrosion instead of vendored version" OFF) +if(SYSTEM_CORROSION) + find_package(Corrosion REQUIRED) +else() + add_subdirectory(${CMAKE_SOURCE_DIR}/src/tc/corrosion) +endif() # Import taskchampion-lib as a CMake library. corrosion_import_crate( From 0f96fd31bf198fa7d6ebdd15bbc459f33e77b700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Sat, 10 Aug 2024 03:24:12 +0200 Subject: [PATCH 100/242] Update google-cloud-auth to drop ring@0.16.20 (#3591) * Update google-cloud-auth to drop ring@0.16.20 ring@0.16.20 doesn't build on ppc and risc-v, and updating google-cloud-auth pulls in a newer version of jsonwebtoken, which in turn depends on a newer version of ring that we depend on already either way. This necessitated an MSRV bump to 1.73.0 --- .github/workflows/checks.yml | 4 +- Cargo.lock | 99 +++++++++--------------------------- 2 files changed, 26 insertions(+), 77 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 15183b975..4fbccafd9 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -29,7 +29,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: - toolchain: "1.70.0" # MSRV + toolchain: "1.73.0" # MSRV override: true - uses: actions-rs/cargo@v1.0.3 @@ -82,7 +82,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: - toolchain: "1.70.0" # MSRV + toolchain: "1.73.0" # MSRV override: true - uses: actions-rs/cargo@v1.0.3 diff --git a/Cargo.lock b/Cargo.lock index d3d6f3b81..80491d09e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,12 +119,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -455,8 +449,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -467,9 +463,9 @@ checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "google-cloud-auth" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1087f1fbd2dd3f58c17c7574ddd99cd61cbbbc2c4dc81114b8687209b196cb" +checksum = "3bf7cb7864f08a92e77c26bb230d021ea57691788fb5dd51793f96965d19e7f9" dependencies = [ "async-trait", "base64 0.21.7", @@ -518,7 +514,7 @@ dependencies = [ "pkcs8", "regex", "reqwest", - "ring 0.17.8", + "ring", "serde", "serde_json", "sha2", @@ -750,13 +746,14 @@ dependencies = [ [[package]] name = "jsonwebtoken" -version = "8.3.0" +version = "9.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" dependencies = [ "base64 0.21.7", + "js-sys", "pem", - "ring 0.16.20", + "ring", "serde", "serde_json", "simple_asn1", @@ -941,11 +938,12 @@ dependencies = [ [[package]] name = "pem" -version = "1.1.1" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.13.1", + "base64 0.22.1", + "serde", ] [[package]] @@ -1107,21 +1105,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.8" @@ -1132,8 +1115,8 @@ dependencies = [ "cfg-if", "getrandom", "libc", - "spin 0.9.8", - "untrusted 0.9.0", + "spin", + "untrusted", "windows-sys 0.52.0", ] @@ -1164,7 +1147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.8", + "ring", "rustls-webpki 0.101.7", "sct", ] @@ -1176,7 +1159,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring 0.17.8", + "ring", "rustls-pki-types", "rustls-webpki 0.102.4", "subtle", @@ -1204,8 +1187,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -1214,9 +1197,9 @@ version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ - "ring 0.17.8", + "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] @@ -1243,8 +1226,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -1338,12 +1321,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -1446,7 +1423,7 @@ dependencies = [ "flate2", "google-cloud-storage", "log", - "ring 0.17.8", + "ring", "rusqlite", "serde", "serde_json", @@ -1667,12 +1644,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -1854,28 +1825,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-core" version = "0.52.0" From 4ff63a796087c9f04f7d6dccd03cda0afdce1f40 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sat, 10 Aug 2024 22:06:00 -0400 Subject: [PATCH 101/242] Use TaskChampion 0.7.0, now via cxx instead of hand-rolled FFI (#3588) TC 0.7.0 introduces a new `TaskData` type that maps to Taskwarrior's `Task` type more cleanly. It also introduces the idea of gathering lists of operations and "committing" them to a replica. A consequence of this change is that TaskChampion no longer automatically maintains dependency information, so Taskwarrior must do so, with its `TDB2::dependency_sync` method. This method does a very similar thing to what TaskChampion had been doing, so this is a shift of responsibility but not a major performance difference. Cxx is .. not great. It is missing a lot of useful things that make a general-purpose bridge impractical: - no support for trait objects - no support for `Option` (https://github.com/dtolnay/cxx/issues/87) - no support for `Vec>` As a result, some creativity is required in writing the bridge, for example returning a `Vec` from `all_task_data` to allow individual `TaskData` values to be "taken" from the vector. That said, Cxx is the current state-of-the-art, and does a good job of ensuring memory safety, at the cost of some slightly awkward APIs. Subsequent work can remove the "TDB2" layer and allow commands and other parts of Taskwarrior to interface directly with the `Replica`. --- .cargo/config.toml | 1 - .github/workflows/checks.yml | 36 - .gitmodules | 4 +- CMakeLists.txt | 6 +- Cargo.lock | 235 ++-- Cargo.toml | 3 +- config.toml | 2 - doc/devel/contrib/rust-and-c++.md | 9 +- src/CMakeLists.txt | 10 +- src/Context.cpp | 14 +- src/TDB2.cpp | 373 ++++--- src/TDB2.h | 28 +- src/Task.cpp | 51 +- src/Task.h | 8 +- src/columns/CMakeLists.txt | 3 +- src/commands/CMakeLists.txt | 3 +- src/commands/CmdImport.h | 1 + src/commands/CmdSync.cpp | 66 +- src/main.cpp | 6 + src/{tc => taskchampion-cpp}/.gitignore | 0 src/taskchampion-cpp/CMakeLists.txt | 21 + src/taskchampion-cpp/Cargo.toml | 15 + src/taskchampion-cpp/build.rs | 6 + src/{tc => taskchampion-cpp}/corrosion | 0 src/taskchampion-cpp/src/lib.rs | 974 +++++++++++++++++ src/tc/CMakeLists.txt | 35 - src/tc/Replica.cpp | 273 ----- src/tc/Replica.h | 127 --- src/tc/Server.cpp | 109 -- src/tc/Server.h | 85 -- src/tc/Task.cpp | 162 --- src/tc/Task.h | 130 --- src/tc/WorkingSet.cpp | 87 -- src/tc/WorkingSet.h | 74 -- src/tc/ffi.h | 36 - src/tc/lib/Cargo.toml | 18 - src/tc/lib/src/annotation.rs | 186 ---- src/tc/lib/src/atomic.rs | 34 - src/tc/lib/src/kv.rs | 155 --- src/tc/lib/src/lib.rs | 172 --- src/tc/lib/src/replica.rs | 958 ----------------- src/tc/lib/src/result.rs | 25 - src/tc/lib/src/server.rs | 234 ---- src/tc/lib/src/status.rs | 73 -- src/tc/lib/src/string.rs | 773 -------------- src/tc/lib/src/task.rs | 1304 ----------------------- src/tc/lib/src/traits.rs | 338 ------ src/tc/lib/src/uda.rs | 188 ---- src/tc/lib/src/util.rs | 31 - src/tc/lib/src/uuid.rs | 239 ----- src/tc/lib/src/workingset.rs | 141 --- src/tc/lib/taskchampion.h | 937 ---------------- src/tc/util.cpp | 79 -- src/tc/util.h | 52 - test/CMakeLists.txt | 3 +- test/feature.559.test.py | 6 +- test/tc.test.cpp | 179 ++-- xtask/Cargo.toml | 9 - xtask/src/main.rs | 35 - 59 files changed, 1531 insertions(+), 7631 deletions(-) delete mode 120000 .cargo/config.toml delete mode 100644 config.toml rename src/{tc => taskchampion-cpp}/.gitignore (100%) create mode 100644 src/taskchampion-cpp/CMakeLists.txt create mode 100644 src/taskchampion-cpp/Cargo.toml create mode 100644 src/taskchampion-cpp/build.rs rename src/{tc => taskchampion-cpp}/corrosion (100%) create mode 100644 src/taskchampion-cpp/src/lib.rs delete mode 100644 src/tc/CMakeLists.txt delete mode 100644 src/tc/Replica.cpp delete mode 100644 src/tc/Replica.h delete mode 100644 src/tc/Server.cpp delete mode 100644 src/tc/Server.h delete mode 100644 src/tc/Task.cpp delete mode 100644 src/tc/Task.h delete mode 100644 src/tc/WorkingSet.cpp delete mode 100644 src/tc/WorkingSet.h delete mode 100644 src/tc/ffi.h delete mode 100644 src/tc/lib/Cargo.toml delete mode 100644 src/tc/lib/src/annotation.rs delete mode 100644 src/tc/lib/src/atomic.rs delete mode 100644 src/tc/lib/src/kv.rs delete mode 100644 src/tc/lib/src/lib.rs delete mode 100644 src/tc/lib/src/replica.rs delete mode 100644 src/tc/lib/src/result.rs delete mode 100644 src/tc/lib/src/server.rs delete mode 100644 src/tc/lib/src/status.rs delete mode 100644 src/tc/lib/src/string.rs delete mode 100644 src/tc/lib/src/task.rs delete mode 100644 src/tc/lib/src/traits.rs delete mode 100644 src/tc/lib/src/uda.rs delete mode 100644 src/tc/lib/src/util.rs delete mode 100644 src/tc/lib/src/uuid.rs delete mode 100644 src/tc/lib/src/workingset.rs delete mode 100644 src/tc/lib/taskchampion.h delete mode 100644 src/tc/util.cpp delete mode 100644 src/tc/util.h delete mode 100644 xtask/Cargo.toml delete mode 100644 xtask/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 120000 index e1fd6d8b1..000000000 --- a/.cargo/config.toml +++ /dev/null @@ -1 +0,0 @@ -../config.toml \ No newline at end of file diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 4fbccafd9..0e8621486 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -61,39 +61,3 @@ jobs: with: command: fmt args: --all -- --check - - codegen: - runs-on: ubuntu-latest - name: "codegen" - steps: - - uses: actions/checkout@v4 - - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo build - uses: actions/cache@v4 - with: - path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - uses: actions-rs/toolchain@v1 - with: - toolchain: "1.73.0" # MSRV - override: true - - - uses: actions-rs/cargo@v1.0.3 - with: - command: xtask - args: codegen - - - name: check for changes - run: | - if ! git diff; then - echo "Generated code not up-to-date; - run `cargo run --package xtask -- codegen` and commit the result"; - exit 1; - fi diff --git a/.gitmodules b/.gitmodules index cddc791b0..bcd2aa1c5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "src/libshared"] path = src/libshared url = https://github.com/GothenburgBitFactory/libshared.git -[submodule "src/tc/corrosion"] - path = src/tc/corrosion +[submodule "src/taskchampion-cpp/corrosion"] + path = src/taskchampion-cpp/corrosion url = https://github.com/corrosion-rs/corrosion.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a9d0e896..e12fcf675 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,13 +26,13 @@ if (ENABLE_WASM) endif (ENABLE_WASM) message ("-- Looking for git submodules") -if (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src AND EXISTS ${CMAKE_SOURCE_DIR}/src/tc/corrosion) +if (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src AND EXISTS ${CMAKE_SOURCE_DIR}/src/taskchampion-cpp/corrosion) message ("-- Found git submodules") else (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src) message ("-- Cloning git submodules") execute_process (COMMAND git submodule update --init WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -endif (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src AND EXISTS ${CMAKE_SOURCE_DIR}/src/tc/corrosion) +endif (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src AND EXISTS ${CMAKE_SOURCE_DIR}/src/taskchampion-cpp/corrosion) message ("-- Looking for SHA1 references") if (EXISTS ${CMAKE_SOURCE_DIR}/.git/index) @@ -142,7 +142,7 @@ configure_file ( add_subdirectory (src) add_subdirectory (src/commands) -add_subdirectory (src/tc) +add_subdirectory (src/taskchampion-cpp) add_subdirectory (src/columns) add_subdirectory (doc) add_subdirectory (scripts) diff --git a/Cargo.lock b/Cargo.lock index 80491d09e..100bc047d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,7 +84,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -95,7 +95,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -203,6 +203,16 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -253,6 +263,50 @@ dependencies = [ "typenum", ] +[[package]] +name = "cxx" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "273dcfd3acd4e1e276af13ed2a43eea7001318823e7a726a6b3ed39b4acc0b82" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0c11acd0e63bae27dcd2afced407063312771212b7a823b4fd72d633be30fb" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "839fcd5e43464614ffaa989eaf1c139ef1f0c51672a1ed08023307fa1b909ccd" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2c1c1776b986979be68bb2285da855f8d8a35851a769fca8740df7c3d07877" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "der" version = "0.7.9" @@ -274,12 +328,6 @@ dependencies = [ "serde", ] -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - [[package]] name = "digest" version = "0.10.7" @@ -290,12 +338,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "either" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" - [[package]] name = "encoding_rs" version = "0.8.34" @@ -323,29 +365,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "ffizz-header" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a1a52e9f00aa4c639059d977e1289a13963117d8e60ccb25e86cca2aab98538" -dependencies = [ - "ffizz-macros", - "itertools", - "linkme", -] - -[[package]] -name = "ffizz-macros" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af208cea557bab3ec4f05fb0c26460f1c61fdb204f3738f471b6b1ecd58d7a04" -dependencies = [ - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "flate2" version = "1.0.30" @@ -400,7 +419,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -720,15 +739,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.11" @@ -777,23 +787,12 @@ dependencies = [ ] [[package]] -name = "linkme" -version = "0.3.27" +name = "link-cplusplus" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb76662d78edc9f9bf56360d6919bdacc8b7761227727e5082f128eeb90bbf5" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" dependencies = [ - "linkme-impl", -] - -[[package]] -name = "linkme-impl" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dccda732e04fa3baf2e17cf835bfe2601c7c2edafd64417c627dabae3a8cda" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", + "cc", ] [[package]] @@ -995,16 +994,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "pretty_assertions" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" -dependencies = [ - "diff", - "yansi", -] - [[package]] name = "proc-macro2" version = "1.0.85" @@ -1152,20 +1141,6 @@ dependencies = [ "sct", ] -[[package]] -name = "rustls" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" -dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki 0.102.4", - "subtle", - "zeroize", -] - [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -1193,9 +1168,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "ring", "rustls-pki-types", @@ -1220,6 +1195,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scratch" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" + [[package]] name = "sct" version = "0.7.1" @@ -1247,7 +1228,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -1353,7 +1334,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.66", + "syn", ] [[package]] @@ -1362,17 +1343,6 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d0208408ba0c3df17ed26eb06992cb1a1268d41b2c0e12e65203fbe3972cee5" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.66" @@ -1413,9 +1383,9 @@ dependencies = [ [[package]] name = "taskchampion" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55b167a2bea718f6f75f68c8d29f1550a6095d8917504d3b9c62626f4c4ef7cb" +checksum = "664ff35fa1777f8a7aabec512bf43926e14fb073de947b588355bea1814c3577" dependencies = [ "anyhow", "byteorder", @@ -1440,13 +1410,20 @@ dependencies = [ name = "taskchampion-lib" version = "0.1.0" dependencies = [ - "anyhow", - "ffizz-header", - "libc", - "pretty_assertions", + "cxx", + "cxx-build", "taskchampion", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.61" @@ -1464,7 +1441,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -1539,7 +1516,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -1590,7 +1567,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -1644,6 +1621,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + [[package]] name = "untrusted" version = "0.9.0" @@ -1652,17 +1635,16 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.7" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" +checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea" dependencies = [ "base64 0.22.1", "flate2", "log", "once_cell", - "rustls 0.22.4", + "rustls 0.23.12", "rustls-pki-types", - "rustls-webpki 0.102.4", "url", "webpki-roots 0.26.3", ] @@ -1686,9 +1668,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "uuid" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", "serde", @@ -1742,7 +1724,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn", "wasm-bindgen-shared", ] @@ -1776,7 +1758,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1983,21 +1965,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "xtask" -version = "0.4.1" -dependencies = [ - "anyhow", - "regex", - "taskchampion-lib", -] - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - [[package]] name = "zerocopy" version = "0.7.34" @@ -2015,7 +1982,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b8e486d6a..7dabb449b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,7 @@ [workspace] members = [ - "src/tc/lib", - "xtask", + "src/taskchampion-cpp", ] resolver = "2" diff --git a/config.toml b/config.toml deleted file mode 100644 index 35049cbcb..000000000 --- a/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[alias] -xtask = "run --package xtask --" diff --git a/doc/devel/contrib/rust-and-c++.md b/doc/devel/contrib/rust-and-c++.md index 5317985e3..d658e850d 100644 --- a/doc/devel/contrib/rust-and-c++.md +++ b/doc/devel/contrib/rust-and-c++.md @@ -7,15 +7,12 @@ Taskwarrior has historically been a C++ project, but as of taskwarrior-3.0.0, th TaskChampion implements storage and access to "replicas" containing a user's tasks. It defines an abstract model for this data, and also provides a simple Rust API for manipulating replicas. It also defines a method of synchronizing replicas and provides an implementation of that method in the form of a sync server. -TaskChampion provides a C interface via the `taskchampion-lib` crate, at `src/tc/lib`. Other applications, besides Taskwarrior, can use TaskChampion to manage tasks. Taskwarrior is just one application using the TaskChampion interface. ## Taskwarrior's use of TaskChampion -Taskwarrior's interface to TaskChampion has a few layers: - -* A Rust library, `takschampion-lib`, that presents `extern "C"` functions for use from C++, essentially defining a C interface to TaskChampion. -* C++ wrappers for the types from `taskchampion-lib`, defined in [`src/tc`](../../src/tc), ensuring memory safety (with `unique_ptr`) and adding methods corresponding to the Rust API's methods. - The wrapper types are in the C++ namespace, `tc`. +Taskwarrior's interface to TaskChampion is in `src/taskchampion-cpp`. +This links to `taskchampion` as a Rust dependency, and uses [cxx](https://cxx.rs) to build a C++ API for it. +That API is defined, and documented, in `src/taskchampion-cpp/src/lib.rs`, and available in the `tc` namespace in C++ code. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1c269770b..15530c776 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,8 +1,6 @@ cmake_minimum_required (VERSION 3.22) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/src/tc - ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src @@ -28,6 +26,7 @@ add_library (task STATIC CLI2.cpp CLI2.h rules.cpp sort.cpp util.cpp util.h) +target_link_libraries(task taskchampion-cpp) add_library (libshared STATIC libshared/src/Color.cpp libshared/src/Color.h libshared/src/Configuration.cpp libshared/src/Configuration.h @@ -52,10 +51,9 @@ add_executable (calc_executable calc.cpp) add_executable (lex_executable lex.cpp) # Yes, 'task' (and hence libshared) is included twice, otherwise linking fails on assorted OSes. -# Similarly for `tc`. -target_link_libraries (task_executable task tc commands tc columns libshared task libshared ${TASK_LIBRARIES}) -target_link_libraries (calc_executable task tc commands tc columns libshared task libshared ${TASK_LIBRARIES}) -target_link_libraries (lex_executable task tc commands tc columns libshared task libshared ${TASK_LIBRARIES}) +target_link_libraries (task_executable task commands columns libshared task libshared ${TASK_LIBRARIES}) +target_link_libraries (calc_executable task commands columns libshared task libshared ${TASK_LIBRARIES}) +target_link_libraries (lex_executable task commands columns libshared task libshared ${TASK_LIBRARIES}) if (DARWIN) # SystemConfiguration is required by Rust libraries like reqwest, to get proxy configuration. target_link_libraries (task_executable "-framework CoreFoundation -framework Security -framework SystemConfiguration") diff --git a/src/Context.cpp b/src/Context.cpp index 363c1d640..3ca9e6c2c 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -37,9 +37,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -681,6 +683,11 @@ int Context::initialize(int argc, const char** argv) { rc = 2; } + catch (rust::Error& err) { + error(err.what()); + rc = 2; + } + catch (int) { // Hooks can terminate processing by throwing integers. rc = 4; @@ -689,7 +696,7 @@ int Context::initialize(int argc, const char** argv) { catch (const std::regex_error& e) { std::cout << "regex_error caught: " << e.what() << '\n'; } catch (...) { - error("knknown error. Please report."); + error("Unknown error. Please report."); rc = 3; } @@ -772,6 +779,11 @@ int Context::run() { rc = 2; } + catch (rust::Error& err) { + error(err.what()); + rc = 2; + } + catch (int) { // Hooks can terminate processing by throwing integers. rc = 4; diff --git a/src/TDB2.cpp b/src/TDB2.cpp index d175a61ff..30009b42c 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -46,21 +46,12 @@ #include #include -#include "tc/Server.h" -#include "tc/util.h" - bool TDB2::debug_mode = false; static void dependency_scan(std::vector&); -//////////////////////////////////////////////////////////////////////////////// -TDB2::TDB2() - : replica{tc::Replica()} // in-memory Replica - , - _working_set{} {} - //////////////////////////////////////////////////////////////////////////////// void TDB2::open_replica(const std::string& location, bool create_if_missing) { - replica = tc::Replica(location, create_if_missing); + _replica = tc::new_replica_on_disk(location, create_if_missing); } //////////////////////////////////////////////////////////////////////////////// @@ -71,54 +62,35 @@ void TDB2::add(Task& task) { // properties not otherwise given. task.validate(true); - std::string uuid = task.get("uuid"); + rust::Vec ops; + maybe_add_undo_point(ops); + + auto uuid = task.get("uuid"); changes[uuid] = task; + tc::Uuid tcuuid = tc::uuid_from_string(uuid); // run hooks for this new task Context::getContext().hooks.onAdd(task); - auto innertask = replica.import_task_with_uuid(uuid); + auto taskdata = tc::create_task(tcuuid, ops); - { - auto guard = replica.mutate_task(innertask); - - // add the task attributes - for (auto& attr : task.all()) { - // TaskChampion does not store uuid or id in the taskmap - if (attr == "uuid" || attr == "id") { - continue; - } - - // Use `set_status` for the task status, to get expected behavior - // with respect to the working set. - else if (attr == "status") { - innertask.set_status(Task::status2tc(Task::textToStatus(task.get(attr)))); - } - - // use `set_modified` to set the modified timestamp, avoiding automatic - // updates to this field by TaskChampion. - else if (attr == "modified") { - auto mod = (time_t)std::stoi(task.get(attr)); - innertask.set_modified(mod); - } - - // otherwise, just set the k/v map value - else { - innertask.set_value(attr, std::make_optional(task.get(attr))); - } + // add the task attributes + for (auto& attr : task.all()) { + // TaskChampion does not store uuid or id in the task data + if (attr == "uuid" || attr == "id") { + continue; } - } - auto ws = replica.working_set(); + taskdata->update(attr, task.get(attr), ops); + } + replica()->commit_operations(std::move(ops)); + + invalidate_cached_info(); // get the ID that was assigned to this task - auto id = ws.by_uuid(uuid); - - // update the cached working set with the new information - _working_set = std::make_optional(std::move(ws)); - - if (id.has_value()) { - task.id = id.value(); + auto id = working_set()->by_uuid(tcuuid); + if (id > 0) { + task.id = id; } } @@ -144,6 +116,9 @@ void TDB2::modify(Task& task) { task.validate(false); auto uuid = task.get("uuid"); + rust::Vec ops; + maybe_add_undo_point(ops); + changes[uuid] = task; // invoke the hook and allow it to modify the task before updating @@ -151,14 +126,15 @@ void TDB2::modify(Task& task) { get(uuid, original); Context::getContext().hooks.onModify(original, task); - auto maybe_tctask = replica.get_task(uuid); - if (!maybe_tctask.has_value()) { + tc::Uuid tcuuid = tc::uuid_from_string(uuid); + auto maybe_tctask = replica()->get_task_data(tcuuid); + if (maybe_tctask.is_none()) { throw std::string("task no longer exists"); } - auto tctask = std::move(maybe_tctask.value()); - auto guard = replica.mutate_task(tctask); - auto tctask_map = tctask.get_taskmap(); + auto tctask = maybe_tctask.take(); + // Perform the necessary `update` operations to set all keys in `tctask` + // equal to those in `task`. std::unordered_set seen; for (auto k : task.all()) { // ignore task keys that aren't stored @@ -168,45 +144,76 @@ void TDB2::modify(Task& task) { seen.insert(k); bool update = false; auto v_new = task.get(k); - try { - auto v_tctask = tctask_map.at(k); + std::string v_tctask; + if (tctask->get(k, v_tctask)) { update = v_tctask != v_new; - } catch (const std::out_of_range& oor) { - // tctask_map does not contain k, so update it + } else { + // tctask does not contain k, so update it update = true; } if (update) { // An empty string indicates the value should be removed. if (v_new == "") { - tctask.set_value(k, {}); + tctask->update_remove(k, ops); } else { - tctask.set_value(k, make_optional(v_new)); + tctask->update(k, v_new, ops); } } } // we've now added and updated properties; but must find any deleted properties - for (auto kv : tctask_map) { - if (seen.find(kv.first) == seen.end()) { - tctask.set_value(kv.first, {}); + for (auto k : tctask->properties()) { + auto kstr = static_cast(k); + if (seen.find(kstr) == seen.end()) { + tctask->update_remove(kstr, ops); } } + + replica()->commit_operations(std::move(ops)); + + invalidate_cached_info(); } //////////////////////////////////////////////////////////////////////////////// void TDB2::purge(Task& task) { - auto uuid = task.get("uuid"); - replica.delete_task(uuid); + auto uuid = tc::uuid_from_string(task.get("uuid")); + rust::Vec ops; + auto maybe_tctask = replica()->get_task_data(uuid); + if (maybe_tctask.is_some()) { + auto tctask = maybe_tctask.take(); + tctask->delete_task(ops); + replica()->commit_operations(std::move(ops)); + } + + invalidate_cached_info(); } //////////////////////////////////////////////////////////////////////////////// -const tc::WorkingSet& TDB2::working_set() { +rust::Box& TDB2::replica() { + // Create a replica in-memory if `open_replica` has not been called. This + // occurs in tests. + if (!_replica) { + _replica = tc::new_replica_in_memory(); + } + return _replica.value(); +} + +//////////////////////////////////////////////////////////////////////////////// +const rust::Box& TDB2::working_set() { if (!_working_set.has_value()) { - _working_set = std::make_optional(replica.working_set()); + _working_set = replica()->working_set(); } return _working_set.value(); } +//////////////////////////////////////////////////////////////////////////////// +void TDB2::maybe_add_undo_point(rust::Vec& ops) { + // Only add an UndoPoint if there are not yet any changes. + if (changes.size() == 0) { + tc::add_undo_point(ops); + } +} + //////////////////////////////////////////////////////////////////////////////// void TDB2::get_changes(std::vector& changes) { std::map& changes_map = this->changes; @@ -217,47 +224,58 @@ void TDB2::get_changes(std::vector& changes) { //////////////////////////////////////////////////////////////////////////////// void TDB2::revert() { - auto undo_ops = replica.get_undo_ops(); - if (undo_ops.len == 0) { + rust::Vec undo_ops = replica()->get_undo_operations(); + if (undo_ops.size() == 0) { std::cout << "No operations to undo."; return; } if (confirm_revert(undo_ops)) { - // Has the side-effect of freeing undo_ops. - replica.commit_undo_ops(undo_ops, NULL); - } else { - replica.free_replica_ops(undo_ops); + // Note that commit_reversed_operations rebuilds the working set, so that + // need not be done here. + if (!replica()->commit_reversed_operations(std::move(undo_ops))) { + std::cout << "Could not undo: other operations have occurred."; + } } - replica.rebuild_working_set(false); } //////////////////////////////////////////////////////////////////////////////// -bool TDB2::confirm_revert(struct tc::ffi::TCReplicaOpList undo_ops) { +bool TDB2::confirm_revert(rust::Vec& undo_ops) { // TODO Use show_diff rather than this basic listing of operations, though // this might be a worthy undo.style itself. - std::cout << "The following " << undo_ops.len << " operations would be reverted:\n"; - for (size_t i = 0; i < undo_ops.len; i++) { + std::cout << "The following " << undo_ops.size() << " operations would be reverted:\n"; + for (auto& op : undo_ops) { + if (op.is_undo_point()) { + continue; + } + std::cout << "- "; - tc::ffi::TCReplicaOp op = undo_ops.items[i]; - switch (op.operation_type) { - case tc::ffi::TCReplicaOpType::Create: - std::cout << "Create " << replica.get_op_uuid(op); - break; - case tc::ffi::TCReplicaOpType::Delete: - std::cout << "Delete " << replica.get_op_old_task_description(op); - break; - case tc::ffi::TCReplicaOpType::Update: - std::cout << "Update " << replica.get_op_uuid(op) << "\n"; - std::cout << " " << replica.get_op_property(op) << ": " - << option_string(replica.get_op_old_value(op)) << " -> " - << option_string(replica.get_op_value(op)); - break; - case tc::ffi::TCReplicaOpType::UndoPoint: - throw std::string("Can't undo UndoPoint."); - break; - default: - throw std::string("Can't undo non-operation."); - break; + std::string uuid = static_cast(op.get_uuid().to_string()); + if (op.is_create()) { + std::cout << "Create " << uuid; + } else if (op.is_delete()) { + std::cout << "Delete "; + auto old_task = op.get_old_task(); + bool found_description = false; + for (auto& pv : old_task) { + if (static_cast(pv.prop) == "description") { + std::cout << static_cast(pv.value) << " (" << uuid << ")"; + found_description = true; + } + } + if (!found_description) { + std::cout << uuid; + } + } else if (op.is_update()) { + std::cout << "Update " << uuid << "\n"; + std::string property; + op.get_property(property); + std::string value; + bool have_value = op.get_value(value); + std::string old_value; + bool have_old_value = op.get_old_value(old_value); + std::cout << " " << property << ": "; + std::cout << (have_old_value ? old_value : "") << " -> "; + std::cout << (have_value ? value : ""); } std::cout << "\n"; } @@ -265,11 +283,9 @@ bool TDB2::confirm_revert(struct tc::ffi::TCReplicaOpList undo_ops) { confirm( "The undo command is not reversible. Are you sure you want to revert to the previous " "state?"); + return true; } -//////////////////////////////////////////////////////////////////////////////// -std::string TDB2::option_string(std::string input) { return input == "" ? "" : input; } - //////////////////////////////////////////////////////////////////////////////// void TDB2::show_diff(const std::string& current, const std::string& prior, const std::string& when) { @@ -305,78 +321,102 @@ void TDB2::gc() { // Allowed as an override, but not recommended. if (Context::getContext().config.getBoolean("gc")) { - replica.rebuild_working_set(true); + replica()->rebuild_working_set(true); } Context::getContext().time_gc_us += timer.total_us(); } //////////////////////////////////////////////////////////////////////////////// -void TDB2::expire_tasks() { replica.expire_tasks(); } +void TDB2::expire_tasks() { replica()->expire_tasks(); } //////////////////////////////////////////////////////////////////////////////// // Latest ID is that of the last pending task. int TDB2::latest_id() { - const tc::WorkingSet& ws = working_set(); - return (int)ws.largest_index(); + auto& ws = working_set(); + return (int)ws->largest_index(); } //////////////////////////////////////////////////////////////////////////////// const std::vector TDB2::all_tasks() { - auto all_tctasks = replica.all_tasks(); + auto all_tctasks = replica()->all_task_data(); std::vector all; - for (auto& tctask : all_tctasks) all.push_back(Task(std::move(tctask))); + for (auto& maybe_tctask : all_tctasks) { + auto tctask = maybe_tctask.take(); + all.push_back(Task(std::move(tctask))); + } + + dependency_scan(all); return all; } //////////////////////////////////////////////////////////////////////////////// const std::vector TDB2::pending_tasks() { - const tc::WorkingSet& ws = working_set(); - auto largest_index = ws.largest_index(); + if (!_pending_tasks) { + auto& ws = working_set(); + auto largest_index = ws->largest_index(); - std::vector result; - for (size_t i = 0; i <= largest_index; i++) { - auto maybe_uuid = ws.by_index(i); - if (maybe_uuid.has_value()) { - auto maybe_task = replica.get_task(maybe_uuid.value()); - if (maybe_task.has_value()) { - result.push_back(Task(std::move(maybe_task.value()))); + std::vector result; + for (size_t i = 0; i <= largest_index; i++) { + auto uuid = ws->by_index(i); + if (!uuid.is_nil()) { + auto maybe_task = replica()->get_task_data(uuid); + if (maybe_task.is_some()) { + result.push_back(Task(maybe_task.take())); + } } } + + dependency_scan(result); + + _pending_tasks = result; } - dependency_scan(result); - - return result; + return *_pending_tasks; } //////////////////////////////////////////////////////////////////////////////// const std::vector TDB2::completed_tasks() { - auto all_tctasks = replica.all_tasks(); - const tc::WorkingSet& ws = working_set(); + if (!_completed_tasks) { + auto all_tctasks = replica()->all_task_data(); + auto& ws = working_set(); - std::vector result; - for (auto& tctask : all_tctasks) { - // if this task is _not_ in the working set, return it. - if (!ws.by_uuid(tctask.get_uuid())) { - result.push_back(Task(std::move(tctask))); + std::vector result; + for (auto& maybe_tctask : all_tctasks) { + auto tctask = maybe_tctask.take(); + // if this task is _not_ in the working set, return it. + if (ws->by_uuid(tctask->get_uuid()) == 0) { + result.push_back(Task(std::move(tctask))); + } } + _completed_tasks = result; } + return *_completed_tasks; +} - return result; +//////////////////////////////////////////////////////////////////////////////// +void TDB2::invalidate_cached_info() { + _pending_tasks = std::nullopt; + _completed_tasks = std::nullopt; + _working_set = std::nullopt; } //////////////////////////////////////////////////////////////////////////////// // Locate task by ID, wherever it is. bool TDB2::get(int id, Task& task) { - const tc::WorkingSet& ws = working_set(); - const auto maybe_uuid = ws.by_index(id); - if (maybe_uuid) { - auto maybe_task = replica.get_task(*maybe_uuid); - if (maybe_task) { - task = Task{std::move(*maybe_task)}; - return true; + auto& ws = working_set(); + const auto tcuuid = ws->by_index(id); + if (!tcuuid.is_nil()) { + std::string uuid = static_cast(tcuuid.to_string()); + // Load all pending tasks in order to get dependency data, and in particular + // `task.is_blocking` and `task.is_blocked`, set correctly. + std::vector pending = pending_tasks(); + for (auto& pending_task : pending) { + if (pending_task.get("uuid") == uuid) { + task = pending_task; + return true; + } } } @@ -386,22 +426,23 @@ bool TDB2::get(int id, Task& task) { //////////////////////////////////////////////////////////////////////////////// // Locate task by UUID, including by partial ID, wherever it is. bool TDB2::get(const std::string& uuid, Task& task) { + // Load all pending tasks in order to get dependency data, and in particular + // `task.is_blocking` and `task.is_blocked`, set correctly. + std::vector pending = pending_tasks(); + // try by raw uuid, if the length is right - if (uuid.size() == 36) { - try { - auto maybe_task = replica.get_task(uuid); - if (maybe_task) { - task = Task{std::move(*maybe_task)}; - return true; - } - } catch (const std::string& err) { - return false; + for (auto& pending_task : pending) { + if (closeEnough(pending_task.get("uuid"), uuid, uuid.length())) { + task = pending_task; + return true; } } - // Nothing to do but iterate over all tasks and check whether it's closeEnough - for (auto& tctask : replica.all_tasks()) { - if (closeEnough(tctask.get_uuid(), uuid, uuid.length())) { + // Nothing to do but iterate over all tasks and check whether it's closeEnough. + for (auto& maybe_tctask : replica()->all_task_data()) { + auto tctask = maybe_tctask.take(); + auto tctask_uuid = static_cast(tctask->get_uuid().to_string()); + if (closeEnough(tctask_uuid, uuid, uuid.length())) { task = Task{std::move(tctask)}; return true; } @@ -446,63 +487,59 @@ const std::vector TDB2::children(Task& parent) { std::vector results; std::string this_uuid = parent.get("uuid"); - const tc::WorkingSet& ws = working_set(); - size_t end_idx = ws.largest_index(); + auto& ws = working_set(); + size_t end_idx = ws->largest_index(); for (size_t i = 0; i <= end_idx; i++) { - auto uuid_opt = ws.by_index(i); - if (!uuid_opt) { + auto uuid = ws->by_index(i); + if (uuid.is_nil()) { continue; } - auto uuid = uuid_opt.value(); // skip self-references - if (uuid == this_uuid) { + if (uuid.to_string() == this_uuid) { continue; } - auto task_opt = replica.get_task(uuid_opt.value()); - if (!task_opt) { + auto task_opt = replica()->get_task_data(uuid); + if (task_opt.is_none()) { continue; } - auto task = std::move(task_opt.value()); + auto task = task_opt.take(); - auto parent_uuid_opt = task.get_value("parent"); - if (!parent_uuid_opt) { + std::string parent_uuid; + if (!task->get("parent", parent_uuid)) { continue; } - auto parent_uuid = parent_uuid_opt.value(); if (parent_uuid == this_uuid) { results.push_back(Task(std::move(task))); } } - return results; } //////////////////////////////////////////////////////////////////////////////// std::string TDB2::uuid(int id) { - const tc::WorkingSet& ws = working_set(); - return ws.by_index((size_t)id).value_or(""); + auto& ws = working_set(); + auto uuid = ws->by_index(id); + if (uuid.is_nil()) { + return ""; + } + return static_cast(uuid.to_string()); } //////////////////////////////////////////////////////////////////////////////// int TDB2::id(const std::string& uuid) { - const tc::WorkingSet& ws = working_set(); - return (int)ws.by_uuid(uuid).value_or(0); + auto& ws = working_set(); + return ws->by_uuid(tc::uuid_from_string(uuid)); } //////////////////////////////////////////////////////////////////////////////// -int TDB2::num_local_changes() { return (int)replica.num_local_operations(); } +int TDB2::num_local_changes() { return (int)replica()->num_local_operations(); } //////////////////////////////////////////////////////////////////////////////// -int TDB2::num_reverts_possible() { return (int)replica.num_undo_points(); } - -//////////////////////////////////////////////////////////////////////////////// -void TDB2::sync(tc::Server server, bool avoid_snapshots) { - replica.sync(std::move(server), avoid_snapshots); -} +int TDB2::num_reverts_possible() { return (int)replica()->num_undo_points(); } //////////////////////////////////////////////////////////////////////////////// void TDB2::dump() { diff --git a/src/TDB2.h b/src/TDB2.h index 03c8dca5b..9de0d9d7e 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -30,25 +30,21 @@ #include #include #include -#include -#include +#include #include +#include #include #include #include #include -namespace tc { -class Server; -} - // TDB2 Class represents all the files in the task database. class TDB2 { public: static bool debug_mode; - TDB2(); + TDB2() = default; void open_replica(const std::string &, bool create_if_missing); void add(Task &); @@ -79,18 +75,24 @@ class TDB2 { void dump(); - void sync(tc::Server server, bool avoid_snapshots); - bool confirm_revert(struct tc::ffi::TCReplicaOpList); + bool confirm_revert(rust::Vec &); + + rust::Box &replica(); private: - tc::Replica replica; - std::optional _working_set; + std::optional> _replica; + + // Cached information from the replica + std::optional> _working_set; + std::optional> _pending_tasks; + std::optional> _completed_tasks; + void invalidate_cached_info(); // UUID -> Task containing all tasks modified in this invocation. std::map changes; - const tc::WorkingSet &working_set(); - static std::string option_string(std::string input); + const rust::Box &working_set(); + void maybe_add_undo_point(rust::Vec &); static void show_diff(const std::string &, const std::string &, const std::string &); }; diff --git a/src/Task.cpp b/src/Task.cpp index a62533cf7..6474c4384 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -138,7 +138,7 @@ Task::Task(const json::object* obj) { } //////////////////////////////////////////////////////////////////////////////// -Task::Task(tc::Task obj) { +Task::Task(rust::Box obj) { id = 0; urgency_value = 0.0; recalc_urgency = true; @@ -146,7 +146,7 @@ Task::Task(tc::Task obj) { is_blocking = false; annotation_count = 0; - parseTC(obj); + parseTC(std::move(obj)); } //////////////////////////////////////////////////////////////////////////////// @@ -717,8 +717,12 @@ void Task::parseJSON(const json::object* root_obj) { //////////////////////////////////////////////////////////////////////////////// // Note that all fields undergo encode/decode. -void Task::parseTC(const tc::Task& task) { - data = task.get_taskmap(); +void Task::parseTC(rust::Box task) { + auto items = task->items(); + data.clear(); + for (auto& item : items) { + data[static_cast(item.prop)] = static_cast(item.value); + } // count annotations annotation_count = 0; @@ -728,11 +732,8 @@ void Task::parseTC(const tc::Task& task) { } } - data["uuid"] = task.get_uuid(); + data["uuid"] = static_cast(task->get_uuid().to_string()); id = Context::getContext().tdb2.id(data["uuid"]); - - is_blocking = task.is_blocking(); - is_blocked = task.is_blocked(); } //////////////////////////////////////////////////////////////////////////////// @@ -1602,40 +1603,6 @@ const std::string Task::decode(const std::string& value) const { return str_replace(modified, "&close;", "]"); } -//////////////////////////////////////////////////////////////////////////////// -tc::Status Task::status2tc(const Task::status status) { - switch (status) { - case Task::pending: - return tc::Status::Pending; - case Task::completed: - return tc::Status::Completed; - case Task::deleted: - return tc::Status::Deleted; - case Task::waiting: - return tc::Status::Pending; // waiting is no longer a status - case Task::recurring: - return tc::Status::Recurring; - default: - return tc::Status::Unknown; - } -} - -//////////////////////////////////////////////////////////////////////////////// -Task::status Task::tc2status(const tc::Status status) { - switch (status) { - case tc::Status::Pending: - return Task::pending; - case tc::Status::Completed: - return Task::completed; - case tc::Status::Deleted: - return Task::deleted; - case tc::Status::Recurring: - return Task::recurring; - default: - return Task::pending; - } -} - //////////////////////////////////////////////////////////////////////////////// int Task::determineVersion(const std::string& line) { // Version 2 looks like: diff --git a/src/Task.h b/src/Task.h index 28acb5c08..08d508f0a 100644 --- a/src/Task.h +++ b/src/Task.h @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include @@ -66,7 +66,7 @@ class Task { bool operator!=(const Task&); Task(const std::string&); Task(const json::object*); - Task(tc::Task); + Task(rust::Box); void parse(const std::string&); std::string composeJSON(bool decorate = false) const; @@ -88,8 +88,6 @@ class Task { // Series of helper functions. static status textToStatus(const std::string&); static std::string statusToText(status); - static tc::Status status2tc(const Task::status); - static Task::status tc2status(const tc::Status); void setAsNow(const std::string&); bool has(const std::string&) const; @@ -186,7 +184,7 @@ class Task { int determineVersion(const std::string&); void parseJSON(const std::string&); void parseJSON(const json::object*); - void parseTC(const tc::Task&); + void parseTC(rust::Box); void parseLegacy(const std::string&); void validate_before(const std::string&, const std::string&); const std::string encode(const std::string&) const; diff --git a/src/columns/CMakeLists.txt b/src/columns/CMakeLists.txt index ced88cb77..575d7c660 100644 --- a/src/columns/CMakeLists.txt +++ b/src/columns/CMakeLists.txt @@ -1,8 +1,6 @@ cmake_minimum_required (VERSION 3.22) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/src/tc - ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src @@ -39,6 +37,7 @@ set (columns_SRCS Column.cpp Column.h ColWait.cpp ColWait.h) add_library (columns STATIC ${columns_SRCS}) +target_link_libraries(columns taskchampion-cpp) #SET(CMAKE_BUILD_TYPE gcov) #SET(CMAKE_CXX_FLAGS_GCOV "--coverage") diff --git a/src/commands/CMakeLists.txt b/src/commands/CMakeLists.txt index 16b8025af..e12f5cc0e 100644 --- a/src/commands/CMakeLists.txt +++ b/src/commands/CMakeLists.txt @@ -1,8 +1,6 @@ cmake_minimum_required (VERSION 3.22) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/src/tc - ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src @@ -61,6 +59,7 @@ set (commands_SRCS Command.cpp Command.h CmdVersion.cpp CmdVersion.h) add_library (commands STATIC ${commands_SRCS}) +target_link_libraries(commands taskchampion-cpp) #SET(CMAKE_BUILD_TYPE gcov) #SET(CMAKE_CXX_FLAGS_GCOV "--coverage") diff --git a/src/commands/CmdImport.h b/src/commands/CmdImport.h index e46f9a0d5..6ae736156 100644 --- a/src/commands/CmdImport.h +++ b/src/commands/CmdImport.h @@ -31,6 +31,7 @@ #include #include +#include class CmdImport : public Command { public: diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index 6245b5acc..361df7f4d 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -35,12 +35,11 @@ #include #include #include +#include #include #include -#include "tc/Server.h" - //////////////////////////////////////////////////////////////////////////////// CmdSync::CmdSync() { _keyword = "synchronize"; @@ -60,56 +59,53 @@ CmdSync::CmdSync() { int CmdSync::execute(std::string& output) { int status = 0; - tc::Server server; - std::string server_ident; + Context& context = Context::getContext(); + auto& replica = context.tdb2.replica(); + std::stringstream out; + bool avoid_snapshots = false; + bool verbose = Context::getContext().verbose("sync"); // If no server is set up, quit. std::string origin = Context::getContext().config.get("sync.server.origin"); std::string url = Context::getContext().config.get("sync.server.url"); std::string server_dir = Context::getContext().config.get("sync.local.server_dir"); + std::string client_id = Context::getContext().config.get("sync.server.client_id"); std::string gcp_credential_path = Context::getContext().config.get("sync.gcp.credential_path"); std::string gcp_bucket = Context::getContext().config.get("sync.gcp.bucket"); std::string encryption_secret = Context::getContext().config.get("sync.encryption_secret"); // sync.server.origin is a deprecated synonym for sync.server.url std::string server_url = url == "" ? origin : url; - - if (server_dir != "") { - server = tc::Server::new_local(server_dir); - server_ident = server_dir; - } else if (gcp_bucket != "") { - if (encryption_secret == "") { - throw std::string("sync.encryption_secret is required"); - } - server = tc::Server::new_gcp(gcp_bucket, gcp_credential_path, encryption_secret); - std::ostringstream os; - os << "GCP bucket " << gcp_bucket; - server_ident = os.str(); - } else if (server_url != "") { - std::string client_id = Context::getContext().config.get("sync.server.client_id"); - if (client_id == "" || encryption_secret == "") { - throw std::string("sync.server.client_id and sync.encryption_secret are required"); - } - server = tc::Server::new_sync(server_url, client_id, encryption_secret); - std::ostringstream os; - os << "Sync server at " << server_url; - server_ident = os.str(); - } else { - throw std::string("No sync.* settings are configured. See task-sync(5)."); - } - - std::stringstream out; if (origin != "") { out << "sync.server.origin is deprecated. Use sync.server.url instead.\n"; } - if (Context::getContext().verbose("sync")) { - out << format("Syncing with {1}", server_ident) << '\n'; + if (server_dir != "") { + if (verbose) { + out << format("Syncing with {1}", server_dir) << '\n'; + } + replica->sync_to_local(server_dir, avoid_snapshots); + } else if (gcp_bucket != "") { + if (encryption_secret == "") { + throw std::string("sync.encryption_secret is required"); + } + if (verbose) { + out << format("Syncing with GCP bucket {1}", gcp_bucket) << '\n'; + } + replica->sync_to_gcp(gcp_bucket, gcp_credential_path, encryption_secret, avoid_snapshots); + } else if (server_url != "") { + if (client_id == "" || encryption_secret == "") { + throw std::string("sync.server.client_id and sync.encryption_secret are required"); + } + if (verbose) { + out << format("Syncing with sync server at {1}", server_url) << '\n'; + } + replica->sync_to_remote(server_url, tc::uuid_from_string(client_id), encryption_secret, + avoid_snapshots); + } else { + throw std::string("No sync.* settings are configured. See task-sync(5)."); } - Context& context = Context::getContext(); - context.tdb2.sync(std::move(server), false); - if (context.config.getBoolean("purge.on-sync")) { context.tdb2.expire_tasks(); } diff --git a/src/main.cpp b/src/main.cpp index f64daeee7..cbdeb0994 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,7 @@ // cmake.h include header must come first #include +#include #include #include @@ -55,6 +56,11 @@ int main(int argc, const char** argv) { status = -1; } + catch (rust::Error& err) { + std::cerr << err.what() << "\n"; + status = -1; + } + catch (std::bad_alloc& error) { std::cerr << "Error: Memory allocation failed: " << error.what() << "\n"; status = -3; diff --git a/src/tc/.gitignore b/src/taskchampion-cpp/.gitignore similarity index 100% rename from src/tc/.gitignore rename to src/taskchampion-cpp/.gitignore diff --git a/src/taskchampion-cpp/CMakeLists.txt b/src/taskchampion-cpp/CMakeLists.txt new file mode 100644 index 000000000..241273d6c --- /dev/null +++ b/src/taskchampion-cpp/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required (VERSION 3.22) + +OPTION(SYSTEM_CORROSION "Use system provided corrosion instead of vendored version" OFF) +if(SYSTEM_CORROSION) + find_package(Corrosion REQUIRED) +else() + add_subdirectory(${CMAKE_SOURCE_DIR}/src/taskchampion-cpp/corrosion) +endif() + +# Import taskchampion-lib as a CMake library. This implements the Rust side of +# the cxxbridge, and depends on the `taskchampion` crate. +corrosion_import_crate( + MANIFEST_PATH "${CMAKE_SOURCE_DIR}/Cargo.toml" + LOCKED + CRATES "taskchampion-lib") + +# Set up `taskchampion-cpp`, the C++ side of the bridge. +corrosion_add_cxxbridge(taskchampion-cpp + CRATE taskchampion_lib + FILES lib.rs +) diff --git a/src/taskchampion-cpp/Cargo.toml b/src/taskchampion-cpp/Cargo.toml new file mode 100644 index 000000000..91e557587 --- /dev/null +++ b/src/taskchampion-cpp/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "taskchampion-lib" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["staticlib"] + +[dependencies] +taskchampion = "0.7.0" +cxx = "1.0.124" + +[build-dependencies] +cxx-build = "1.0" diff --git a/src/taskchampion-cpp/build.rs b/src/taskchampion-cpp/build.rs new file mode 100644 index 000000000..7ad128819 --- /dev/null +++ b/src/taskchampion-cpp/build.rs @@ -0,0 +1,6 @@ +#[allow(unused_must_use)] +fn main() { + cxx_build::bridge("src/lib.rs"); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=src/lib.rs"); +} diff --git a/src/tc/corrosion b/src/taskchampion-cpp/corrosion similarity index 100% rename from src/tc/corrosion rename to src/taskchampion-cpp/corrosion diff --git a/src/taskchampion-cpp/src/lib.rs b/src/taskchampion-cpp/src/lib.rs new file mode 100644 index 000000000..d58af7a60 --- /dev/null +++ b/src/taskchampion-cpp/src/lib.rs @@ -0,0 +1,974 @@ +use cxx::CxxString; +use std::path::PathBuf; +use std::pin::Pin; +use taskchampion as tc; + +// All Taskchampion FFI is contained in this module, due to issues with cxx and multiple modules +// such as https://github.com/dtolnay/cxx/issues/1323. + +/// FFI interface for TaskChampion. +/// +/// This loosely follows the TaskChampion API defined at +/// https://docs.rs/taskchampion/latest/taskchampion/, with adjustments made as necessary to +/// accomodate cxx's limitations. Consult that documentation for full descriptions of the types and +/// methods. +/// +/// This interface is an internal implementation detail of Taskwarrior and may change at any time. +#[cxx::bridge(namespace = "tc")] +mod ffi { + // --- Uuid + + #[derive(Debug, Eq, PartialEq, Clone, Copy)] + struct Uuid { + v: [u8; 16], + } + + extern "Rust" { + /// Generate a new, random Uuid. + fn uuid_v4() -> Uuid; + + /// Parse the given string as a Uuid, panicking if it is not valid. + fn uuid_from_string(uuid: Pin<&CxxString>) -> Uuid; + + /// Convert the given Uuid to a string. + fn to_string(self: &Uuid) -> String; + + /// Check whether this is the "nil" Uuid, used as a sentinel value. + fn is_nil(self: &Uuid) -> bool; + } + + // --- Operation and Operations + + extern "Rust" { + type Operation; + + /// Check if this is a Create operation. + fn is_create(&self) -> bool; + + /// Check if this is a Update operation. + fn is_update(&self) -> bool; + + /// Check if this is a Delete operation. + fn is_delete(&self) -> bool; + + /// Check if this is an UndoPoint operation. + fn is_undo_point(&self) -> bool; + + /// Get the operation's uuid. + /// + /// Only valid for create, update, and delete operations. + fn get_uuid(&self) -> Uuid; + + /// Get the `old_task` for this update operation. + /// + /// Only valid for delete operations. + fn get_old_task(&self) -> Vec; + + /// Get the `property` for this update operation. + /// + /// Only valid for update operations. + fn get_property(&self, property_out: Pin<&mut CxxString>); + + /// Get the `value` for this update operation, returning false if the + /// `value` field is None. + /// + /// Only valid for update operations. + fn get_value(&self, value_out: Pin<&mut CxxString>) -> bool; + + /// Get the `old_value` for this update operation, returning false if the + /// `old_value` field is None. + /// + /// Only valid for update operations. + fn get_old_value(&self, old_value_out: Pin<&mut CxxString>) -> bool; + + /// Get the `timestamp` for this update operation. + /// + /// Only valid for update operations. + fn get_timestamp(&self) -> i64; + + /// Create a new vector of operations. It's also fine to construct a + /// `rust::Vec` directly. + fn new_operations() -> Vec; + + /// Add an UndoPoint operation to the vector of operations. All other + /// operation types should be added via `TaskData`. + fn add_undo_point(ops: &mut Vec); + } + + // --- Replica + + extern "Rust" { + type Replica; + + /// Create a new in-memory replica, such as for testing. + fn new_replica_in_memory() -> Result>; + + /// Create a new replica stored on-disk. + fn new_replica_on_disk(taskdb_dir: String, create_if_missing: bool) + -> Result>; + + /// Commit the given operations to the replica. + fn commit_operations(&mut self, ops: Vec) -> Result<()>; + + /// Commit the reverse of the given operations. + fn commit_reversed_operations(&mut self, ops: Vec) -> Result; + + /// Get `TaskData` values for all tasks in the replica. + + /// This contains `OptionTaskData` to allow C++ to `take` values out of the vector and use + /// them as `rust::Box`. Cxx does not support `Vec>`. Cxx also does not + /// handle `HashMap`, so the result is not a map from uuid to task. The returned Vec is + /// fully populated, so it is safe to call `take` on each value in the returned Vec once . + fn all_task_data(&mut self) -> Result>; + + /// Get the UUIDs of all tasks. + fn all_task_uuids(&mut self) -> Result>; + + /// Expire old, deleted tasks. + fn expire_tasks(&mut self) -> Result<()>; + + /// Get an existing task by its UUID. + fn get_task_data(&mut self, uuid: Uuid) -> Result; + + /// Return the operations back to and including the last undo point, or since the last sync if + /// no undo point is found. + fn get_undo_operations(&mut self) -> Result>; + + /// Get the number of local, un-sync'd operations, excluding undo operations. + fn num_local_operations(&mut self) -> Result; + + /// Get the number of (un-synchronized) undo points in storage. + fn num_undo_points(&mut self) -> Result; + + /// Rebuild the working set. + fn rebuild_working_set(&mut self, renumber: bool) -> Result<()>; + + /// Get the working set for this replica. + fn working_set(&mut self) -> Result>; + + /// Sync with a server crated from `ServerConfig::Local`. + fn sync_to_local(&mut self, server_dir: String, avoid_snapshots: bool) -> Result<()>; + + /// Sync with a server created from `ServerConfig::Remote`. + fn sync_to_remote( + &mut self, + url: String, + client_id: Uuid, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<()>; + + /// Sync with a server created from `ServerConfig::Gcp`. + /// + /// An empty value for `credential_path` is converted to `Option::None`. + fn sync_to_gcp( + &mut self, + bucket: String, + credential_path: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<()>; + } + + // --- OptionTaskData + + /// Wrapper around `Option>`, required since cxx does not support Option. + /// + /// Note that if an OptionTaskData containing a task is dropped without calling `take`, + /// it will leak the contained task. C++ code should be careful to always take. + struct OptionTaskData { + maybe_task: *mut TaskData, + } + + extern "Rust" { + /// Check if the value contains a task. + fn is_some(self: &OptionTaskData) -> bool; + /// Check if the value does not contain a task. + fn is_none(self: &OptionTaskData) -> bool; + /// Get the contained task, or panic if there is no task. The OptionTaskData + /// will be reset to contain None. + fn take(self: &mut OptionTaskData) -> Box; + } + + // --- TaskData + + extern "Rust" { + type TaskData; + + /// Create a new task with the given Uuid. + fn create_task(uuid: Uuid, ops: &mut Vec) -> Box; + + /// Get the task's Uuid. + fn get_uuid(&self) -> Uuid; + + /// Get a value on this task. If the property exists, returns true and updates + /// the output parameter. If not, returns false. + fn get(&self, property: &CxxString, value_out: Pin<&mut CxxString>) -> bool; + + /// Check if the given property is set. + fn has(&self, property: &CxxString) -> bool; + + /// Enumerate all properties on this task, in arbitrary order. + fn properties(&self) -> Vec; + + /// Enumerate all properties and their values on this task, in arbitrary order, as a + /// vector. + fn items(&self) -> Vec; + + /// Update the given property with the given value. + fn update(&mut self, property: &CxxString, value: &CxxString, ops: &mut Vec); + + /// Like `update`, but removing the property by passing None for the value. + fn update_remove(&mut self, property: &CxxString, ops: &mut Vec); + + /// Delete the task. The name is `delete_task` because `delete` is a C++ keyword. + fn delete_task(&mut self, ops: &mut Vec); + } + + // --- PropValuePair + + #[derive(Debug, Eq, PartialEq)] + struct PropValuePair { + prop: String, + value: String, + } + + // --- WorkingSet + + extern "Rust" { + type WorkingSet; + + /// Get the "length" of the working set: the total number of uuids in the set. + fn len(&self) -> usize; + + /// Get the largest index in the working set, or zero if the set is empty. + fn largest_index(&self) -> usize; + + /// True if the length is zero + fn is_empty(&self) -> bool; + + /// Get the uuid with the given index, if any exists. Returns the nil UUID if + /// there is no task at that index. + fn by_index(&self, index: usize) -> Uuid; + + /// Get the index for the given uuid, or zero if it is not in the working set. + fn by_uuid(&self, uuid: Uuid) -> usize; + + /// Get the entire working set, as a vector indexed by each task's id. For example, the + /// UUID for task 5 will be at `all_uuids()[5]`. All elements of the vector not corresponding + /// to a task contain the nil UUID. + fn all_uuids(&self) -> Vec; + } +} + +#[derive(Debug)] +struct CppError(tc::Error); + +impl From for CppError { + fn from(err: tc::Error) -> Self { + CppError(err) + } +} + +impl std::fmt::Display for CppError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let tc::Error::Other(err) = &self.0 { + // The default `to_string` representation of `anyhow::Error` only shows the "outermost" + // context, e.g., "Could not connect to server", and omits the juicy details about what + // actually went wrong. So, join all of those contexts with `: ` for presentation to the C++ + // layer. + let entire_msg = err + .chain() + .skip(1) + .fold(err.to_string(), |a, b| format!("{}: {}", a, b)); + write!(f, "{}", entire_msg) + } else { + self.0.fmt(f) + } + } +} + +// --- Uuid + +impl From for tc::Uuid { + fn from(value: ffi::Uuid) -> Self { + tc::Uuid::from_bytes(value.v) + } +} + +impl From<&ffi::Uuid> for tc::Uuid { + fn from(value: &ffi::Uuid) -> Self { + tc::Uuid::from_bytes(value.v) + } +} + +impl From for ffi::Uuid { + fn from(uuid: tc::Uuid) -> ffi::Uuid { + ffi::Uuid { + v: *uuid.as_bytes(), + } + } +} + +impl From<&tc::Uuid> for ffi::Uuid { + fn from(uuid: &tc::Uuid) -> ffi::Uuid { + ffi::Uuid { + v: *uuid.as_bytes(), + } + } +} + +fn uuid_v4() -> ffi::Uuid { + tc::Uuid::new_v4().into() +} + +fn uuid_from_string(uuid: Pin<&CxxString>) -> ffi::Uuid { + let Ok(uuid) = tc::Uuid::parse_str(uuid.to_str().expect("invalid utf-8")) else { + panic!("{} is not a valid UUID", uuid); + }; + uuid.into() +} + +impl ffi::Uuid { + #[allow(clippy::inherent_to_string, clippy::wrong_self_convention)] + fn to_string(&self) -> String { + tc::Uuid::from(self).as_hyphenated().to_string() + } + + fn is_nil(&self) -> bool { + tc::Uuid::from(self).is_nil() + } +} + +// --- Operation and Operations + +#[repr(transparent)] // required for safety +pub struct Operation(tc::Operation); + +impl Operation { + fn is_create(&self) -> bool { + matches!(&self.0, tc::Operation::Create { .. }) + } + + fn is_update(&self) -> bool { + matches!(&self.0, tc::Operation::Update { .. }) + } + + fn is_delete(&self) -> bool { + matches!(&self.0, tc::Operation::Delete { .. }) + } + + fn is_undo_point(&self) -> bool { + matches!(&self.0, tc::Operation::UndoPoint) + } + + fn get_uuid(&self) -> ffi::Uuid { + match self.0 { + tc::Operation::Create { uuid, .. } => uuid, + tc::Operation::Update { uuid, .. } => uuid, + tc::Operation::Delete { uuid, .. } => uuid, + _ => panic!("operation has no uuid"), + } + .into() + } + + fn get_property(&self, mut property_out: Pin<&mut CxxString>) { + match &self.0 { + tc::Operation::Update { property, .. } => { + property_out.as_mut().clear(); + property_out.as_mut().push_str(property); + } + _ => panic!("operation is not an update"), + } + } + + fn get_value(&self, mut value_out: Pin<&mut CxxString>) -> bool { + match &self.0 { + tc::Operation::Update { value, .. } => { + if let Some(value) = value { + value_out.as_mut().clear(); + value_out.as_mut().push_str(value); + true + } else { + false + } + } + _ => panic!("operation is not an update"), + } + } + + fn get_old_value(&self, mut old_value_out: Pin<&mut CxxString>) -> bool { + match &self.0 { + tc::Operation::Update { old_value, .. } => { + if let Some(old_value) = old_value { + old_value_out.as_mut().clear(); + old_value_out.as_mut().push_str(old_value); + true + } else { + false + } + } + _ => panic!("operation is not an update"), + } + } + + fn get_timestamp(&self) -> i64 { + match &self.0 { + tc::Operation::Update { timestamp, .. } => timestamp.timestamp(), + _ => panic!("operation is not an update"), + } + } + + fn get_old_task(&self) -> Vec { + match &self.0 { + tc::Operation::Delete { old_task, .. } => old_task + .iter() + .map(|(p, v)| ffi::PropValuePair { + prop: p.into(), + value: v.into(), + }) + .collect(), + _ => panic!("operation is not a delete"), + } + } +} + +fn new_operations() -> Vec { + Vec::new() +} + +fn add_undo_point(ops: &mut Vec) { + ops.push(Operation(tc::Operation::UndoPoint)); +} + +// --- Replica + +struct Replica(tc::Replica); + +impl From for Replica { + fn from(inner: tc::Replica) -> Self { + Replica(inner) + } +} + +fn new_replica_on_disk( + taskdb_dir: String, + create_if_missing: bool, +) -> Result, CppError> { + let storage = tc::StorageConfig::OnDisk { + taskdb_dir: PathBuf::from(taskdb_dir), + create_if_missing, + } + .into_storage()?; + Ok(Box::new(tc::Replica::new(storage).into())) +} + +fn new_replica_in_memory() -> Result, CppError> { + let storage = tc::StorageConfig::InMemory.into_storage()?; + Ok(Box::new(tc::Replica::new(storage).into())) +} + +/// Utility function for Replica methods using Operations. +fn to_tc_operations(ops: Vec) -> Vec { + // SAFETY: Operation is a transparent newtype for tc::Operation, so a Vec of one is + // a Vec of the other. + unsafe { std::mem::transmute::, Vec>(ops) } +} + +/// Utility function for Replica methods using Operations. +fn from_tc_operations(ops: Vec) -> Vec { + // SAFETY: Operation is a transparent newtype for tc::Operation, so a Vec of one is + // a Vec of the other. + unsafe { std::mem::transmute::, Vec>(ops) } +} + +impl Replica { + fn commit_operations(&mut self, ops: Vec) -> Result<(), CppError> { + Ok(self.0.commit_operations(to_tc_operations(ops))?) + } + + fn commit_reversed_operations(&mut self, ops: Vec) -> Result { + Ok(self.0.commit_reversed_operations(to_tc_operations(ops))?) + } + + fn all_task_data(&mut self) -> Result, CppError> { + Ok(self + .0 + .all_task_data()? + .drain() + .map(|(_, t)| Some(t).into()) + .collect()) + } + + fn all_task_uuids(&mut self) -> Result, CppError> { + Ok(self + .0 + .all_task_uuids()? + .into_iter() + .map(ffi::Uuid::from) + .collect()) + } + + fn expire_tasks(&mut self) -> Result<(), CppError> { + Ok(self.0.expire_tasks()?) + } + + fn get_task_data(&mut self, uuid: ffi::Uuid) -> Result { + Ok(self.0.get_task_data(uuid.into())?.into()) + } + + fn get_undo_operations(&mut self) -> Result, CppError> { + Ok(from_tc_operations(self.0.get_undo_operations()?)) + } + + fn num_local_operations(&mut self) -> Result { + Ok(self.0.num_local_operations()?) + } + + fn num_undo_points(&mut self) -> Result { + Ok(self.0.num_undo_points()?) + } + + fn rebuild_working_set(&mut self, renumber: bool) -> Result<(), CppError> { + Ok(self.0.rebuild_working_set(renumber)?) + } + + fn working_set(&mut self) -> Result, CppError> { + Ok(Box::new(self.0.working_set()?.into())) + } + + fn sync_to_local(&mut self, server_dir: String, avoid_snapshots: bool) -> Result<(), CppError> { + let mut server = tc::server::ServerConfig::Local { + server_dir: server_dir.into(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } + + fn sync_to_remote( + &mut self, + url: String, + client_id: ffi::Uuid, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<(), CppError> { + let mut server = tc::server::ServerConfig::Remote { + url, + client_id: client_id.into(), + encryption_secret: encryption_secret.as_bytes().to_vec(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } + + fn sync_to_gcp( + &mut self, + bucket: String, + credential_path: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<(), CppError> { + let mut server = tc::server::ServerConfig::Gcp { + bucket, + credential_path: if credential_path.is_empty() { + None + } else { + Some(credential_path) + }, + encryption_secret: encryption_secret.as_bytes().to_vec(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } +} + +// --- OptionTaskData + +impl From> for ffi::OptionTaskData { + fn from(value: Option) -> Self { + let Some(task) = value else { + return ffi::OptionTaskData { + maybe_task: std::ptr::null_mut(), + }; + }; + let boxed = Box::new(task.into()); + ffi::OptionTaskData { + maybe_task: Box::into_raw(boxed), + } + } +} + +impl ffi::OptionTaskData { + fn is_some(&self) -> bool { + !self.maybe_task.is_null() + } + + fn is_none(&self) -> bool { + self.maybe_task.is_null() + } + + fn take(&mut self) -> Box { + let mut ptr = std::ptr::null_mut(); + std::mem::swap(&mut ptr, &mut self.maybe_task); + if ptr.is_null() { + panic!("Cannot take an empty OptionTaskdata"); + } + // SAFETY: this value is not NULL and was created from `Box::into_raw` in the + // `From>` implementation above. + unsafe { Box::from_raw(ptr) } + } +} + +// --- TaskData + +pub struct TaskData(tc::TaskData); + +impl From for TaskData { + fn from(task: tc::TaskData) -> Self { + TaskData(task) + } +} + +/// Utility function for TaskData methods. +fn operations_ref(ops: &mut Vec) -> &mut Vec { + // SAFETY: Operation is a transparent newtype for tc::Operation, so a Vec of one is a + // Vec of the other. + unsafe { std::mem::transmute::<&mut Vec, &mut Vec>(ops) } +} + +fn create_task(uuid: ffi::Uuid, ops: &mut Vec) -> Box { + let t = tc::TaskData::create(uuid.into(), operations_ref(ops)); + Box::new(TaskData(t)) +} + +impl TaskData { + fn get_uuid(&self) -> ffi::Uuid { + self.0.get_uuid().into() + } + + fn get(&self, property: &CxxString, mut value_out: Pin<&mut CxxString>) -> bool { + let Some(value) = self.0.get(property.to_string_lossy()) else { + return false; + }; + value_out.as_mut().clear(); + value_out.as_mut().push_str(value); + true + } + + fn has(&self, property: &CxxString) -> bool { + self.0.has(property.to_string_lossy()) + } + + fn properties(&self) -> Vec { + self.0.properties().map(|s| s.to_owned()).collect() + } + + fn items(&self) -> Vec { + self.0 + .iter() + .map(|(p, v)| ffi::PropValuePair { + prop: p.into(), + value: v.into(), + }) + .collect() + } + + fn update(&mut self, property: &CxxString, value: &CxxString, ops: &mut Vec) { + self.0.update( + property.to_string_lossy(), + Some(value.to_string_lossy().into()), + operations_ref(ops), + ) + } + + fn update_remove(&mut self, property: &CxxString, ops: &mut Vec) { + self.0 + .update(property.to_string_lossy(), None, operations_ref(ops)) + } + + fn delete_task(&mut self, ops: &mut Vec) { + self.0.delete(operations_ref(ops)) + } +} + +// --- WorkingSet + +struct WorkingSet(tc::WorkingSet); + +impl From for WorkingSet { + fn from(task: tc::WorkingSet) -> Self { + WorkingSet(task) + } +} + +impl WorkingSet { + fn len(&self) -> usize { + self.0.len() + } + + fn largest_index(&self) -> usize { + self.0.largest_index() + } + + fn is_empty(&self) -> bool { + self.0.is_empty() + } + + fn by_index(&self, index: usize) -> ffi::Uuid { + self.0.by_index(index).unwrap_or_else(tc::Uuid::nil).into() + } + + fn by_uuid(&self, uuid: ffi::Uuid) -> usize { + self.0.by_uuid(uuid.into()).unwrap_or(0) + } + + fn all_uuids(&self) -> Vec { + let mut res = vec![tc::Uuid::nil().into(); self.0.largest_index() + 1]; + for (i, uuid) in self.0.iter() { + res[i] = uuid.into(); + } + res + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn uuids() { + let uuid = uuid_v4(); + assert_eq!(uuid.to_string().len(), 36); + } + + #[test] + fn operations() { + cxx::let_cxx_string!(prop = "prop"); + cxx::let_cxx_string!(prop2 = "prop2"); + cxx::let_cxx_string!(value = "value"); + cxx::let_cxx_string!(value2 = "value2"); + + let mut operations = new_operations(); + add_undo_point(&mut operations); + let mut i = 0; + assert_eq!(operations.len(), i + 1); + assert!(!operations[i].is_create()); + assert!(!operations[i].is_update()); + assert!(!operations[i].is_delete()); + assert!(operations[i].is_undo_point()); + + let uuid = uuid_v4(); + let mut t = create_task(uuid, &mut operations); + i += 1; + assert_eq!(operations.len(), i + 1); + assert!(operations[i].is_create()); + assert!(!operations[i].is_update()); + assert!(!operations[i].is_delete()); + assert!(!operations[i].is_undo_point()); + assert_eq!(operations[i].get_uuid(), uuid); + + t.update(&prop, &value, &mut operations); + i += 1; + assert_eq!(operations.len(), i + 1); + assert!(!operations[i].is_create()); + assert!(operations[i].is_update()); + assert!(!operations[i].is_delete()); + assert!(!operations[i].is_undo_point()); + assert_eq!(operations[i].get_uuid(), uuid); + // Note that `get_value` and `get_old_value` cannot be tested from Rust, as it is not + // possible to pass a reference to a CxxString and retain ownership of it. + assert!(operations[i].get_timestamp() > 0); + + t.update(&prop2, &value, &mut operations); + i += 1; + assert_eq!(operations.len(), i + 1); + assert!(!operations[i].is_create()); + assert!(operations[i].is_update()); + assert!(!operations[i].is_delete()); + assert!(!operations[i].is_undo_point()); + assert_eq!(operations[i].get_uuid(), uuid); + assert!(operations[i].get_timestamp() > 0); + + t.update(&prop2, &value2, &mut operations); + i += 1; + assert_eq!(operations.len(), i + 1); + assert!(!operations[i].is_create()); + assert!(operations[i].is_update()); + assert!(!operations[i].is_delete()); + assert!(!operations[i].is_undo_point()); + assert_eq!(operations[i].get_uuid(), uuid); + assert!(operations[i].get_timestamp() > 0); + + t.update_remove(&prop, &mut operations); + i += 1; + assert_eq!(operations.len(), i + 1); + assert!(!operations[i].is_create()); + assert!(operations[i].is_update()); + assert!(!operations[i].is_delete()); + assert!(!operations[i].is_undo_point()); + assert_eq!(operations[i].get_uuid(), uuid); + assert!(operations[i].get_timestamp() > 0); + + t.delete_task(&mut operations); + i += 1; + assert_eq!(operations.len(), i + 1); + assert!(!operations[i].is_create()); + assert!(!operations[i].is_update()); + assert!(operations[i].is_delete()); + assert!(!operations[i].is_undo_point()); + assert_eq!(operations[i].get_uuid(), uuid); + assert_eq!( + operations[i].get_old_task(), + vec![ffi::PropValuePair { + prop: "prop2".into(), + value: "value2".into(), + },] + ); + } + + #[test] + fn operation_counts() { + let mut rep = new_replica_in_memory().unwrap(); + let mut operations = new_operations(); + add_undo_point(&mut operations); + create_task(uuid_v4(), &mut operations); + create_task(uuid_v4(), &mut operations); + create_task(uuid_v4(), &mut operations); + add_undo_point(&mut operations); + rep.commit_operations(operations).unwrap(); + // Three non-undo-point operations. + assert_eq!(rep.num_local_operations().unwrap(), 3); + // Two undo points + assert_eq!(rep.num_undo_points().unwrap(), 2); + } + + #[test] + fn undo_operations() { + let mut rep = new_replica_in_memory().unwrap(); + let mut operations = new_operations(); + let (uuid1, uuid2, uuid3) = (uuid_v4(), uuid_v4(), uuid_v4()); + add_undo_point(&mut operations); + create_task(uuid1, &mut operations); + add_undo_point(&mut operations); + create_task(uuid2, &mut operations); + create_task(uuid3, &mut operations); + rep.commit_operations(operations).unwrap(); + + let undo_ops = rep.get_undo_operations().unwrap(); + assert_eq!(undo_ops.len(), 3); + assert!(undo_ops[0].is_undo_point()); + assert!(undo_ops[1].is_create()); + assert_eq!(undo_ops[1].get_uuid(), uuid2); + assert!(undo_ops[2].is_create()); + assert_eq!(undo_ops[2].get_uuid(), uuid3); + } + + #[test] + fn task_lists() { + let mut rep = new_replica_in_memory().unwrap(); + let mut operations = new_operations(); + add_undo_point(&mut operations); + create_task(uuid_v4(), &mut operations); + create_task(uuid_v4(), &mut operations); + create_task(uuid_v4(), &mut operations); + rep.commit_operations(operations).unwrap(); + + assert_eq!(rep.all_task_data().unwrap().len(), 3); + assert_eq!(rep.all_task_uuids().unwrap().len(), 3); + } + + #[test] + fn expire_tasks() { + let mut rep = new_replica_in_memory().unwrap(); + let mut operations = new_operations(); + add_undo_point(&mut operations); + create_task(uuid_v4(), &mut operations); + create_task(uuid_v4(), &mut operations); + create_task(uuid_v4(), &mut operations); + rep.commit_operations(operations).unwrap(); + rep.expire_tasks().unwrap(); + } + + #[test] + fn get_task_data() { + let mut rep = new_replica_in_memory().unwrap(); + + let uuid = uuid_v4(); + assert!(rep.get_task_data(uuid).unwrap().is_none()); + + let mut operations = new_operations(); + create_task(uuid, &mut operations); + rep.commit_operations(operations).unwrap(); + + let mut t = rep.get_task_data(uuid).unwrap(); + assert!(t.is_some()); + assert_eq!(t.take().get_uuid(), uuid); + } + + #[test] + fn task_properties() { + cxx::let_cxx_string!(prop = "prop"); + cxx::let_cxx_string!(prop2 = "prop2"); + cxx::let_cxx_string!(value = "value"); + + let mut rep = new_replica_in_memory().unwrap(); + + let uuid = uuid_v4(); + let mut operations = new_operations(); + let mut t = create_task(uuid, &mut operations); + t.update(&prop, &value, &mut operations); + rep.commit_operations(operations).unwrap(); + + let t = rep.get_task_data(uuid).unwrap().take(); + assert!(t.has(&prop)); + assert!(!t.has(&prop2)); + // Note that `get` cannot be tested from Rust, as it is not possible to pass a reference to + // a CxxString and retain ownership of it. + + assert_eq!(t.properties(), vec!["prop".to_string()]); + assert_eq!( + t.iter(), + vec![ffi::PropValuePair { + prop: "prop".into(), + value: "value".into(), + }] + ); + } + + #[test] + fn working_set() { + cxx::let_cxx_string!(status = "status"); + cxx::let_cxx_string!(pending = "pending"); + cxx::let_cxx_string!(completed = "completed"); + let (uuid1, uuid2, uuid3) = (uuid_v4(), uuid_v4(), uuid_v4()); + + let mut rep = new_replica_in_memory().unwrap(); + + let mut operations = new_operations(); + let mut t = create_task(uuid1, &mut operations); + t.update(&status, &pending, &mut operations); + rep.commit_operations(operations).unwrap(); + + let mut operations = new_operations(); + let mut t = create_task(uuid2, &mut operations); + t.update(&status, &pending, &mut operations); + rep.commit_operations(operations).unwrap(); + + let mut operations = new_operations(); + let mut t = create_task(uuid3, &mut operations); + t.update(&status, &completed, &mut operations); + rep.commit_operations(operations).unwrap(); + + rep.rebuild_working_set(false).unwrap(); + + let ws = rep.working_set().unwrap(); + assert!(!ws.is_empty()); + assert_eq!(ws.len(), 2); + assert_eq!(ws.largest_index(), 2); + assert_eq!(ws.by_index(1), uuid1); + assert_eq!(ws.by_uuid(uuid2), 2); + assert_eq!(ws.by_index(100), tc::Uuid::nil().into()); + assert_eq!(ws.by_uuid(uuid3), 0); + assert_eq!(ws.all_uuids(), vec![tc::Uuid::nil().into(), uuid1, uuid2]); + } +} diff --git a/src/tc/CMakeLists.txt b/src/tc/CMakeLists.txt deleted file mode 100644 index 907ecbad6..000000000 --- a/src/tc/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -cmake_minimum_required (VERSION 3.22) - -OPTION(SYSTEM_CORROSION "Use system provided corrosion instead of vendored version" OFF) -if(SYSTEM_CORROSION) - find_package(Corrosion REQUIRED) -else() - add_subdirectory(${CMAKE_SOURCE_DIR}/src/tc/corrosion) -endif() - -# Import taskchampion-lib as a CMake library. -corrosion_import_crate( - MANIFEST_PATH "${CMAKE_SOURCE_DIR}/Cargo.toml" - LOCKED - CRATES "taskchampion-lib") - -# TODO(#3425): figure out how to create taskchampion.h - -include_directories (${CMAKE_SOURCE_DIR} - ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/src/tc - ${CMAKE_SOURCE_DIR}/src/tc/lib - ${CMAKE_SOURCE_DIR}/src/libshared/src - ${TASK_INCLUDE_DIRS}) - -set (tc_SRCS - ffi.h - lib/taskchampion.h - util.cpp util.h - Replica.cpp Replica.h - Server.cpp Server.h - WorkingSet.cpp WorkingSet.h - Task.cpp Task.h) - -add_library (tc STATIC ${tc_SRCS}) -target_link_libraries(tc taskchampion_lib) diff --git a/src/tc/Replica.cpp b/src/tc/Replica.cpp deleted file mode 100644 index a90ac2e0b..000000000 --- a/src/tc/Replica.cpp +++ /dev/null @@ -1,273 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#include -// cmake.h include header must come first - -#include - -#include - -#include "tc/Replica.h" -#include "tc/Server.h" -#include "tc/Task.h" -#include "tc/WorkingSet.h" -#include "tc/util.h" - -using namespace tc::ffi; - -//////////////////////////////////////////////////////////////////////////////// -tc::ReplicaGuard::ReplicaGuard(Replica &replica, Task &task) : replica(replica), task(task) { - // "steal" the reference from the Replica and store it locally, so that any - // attempt to use the Replica will fail - tcreplica = replica.inner.release(); - task.to_mut(tcreplica); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::ReplicaGuard::~ReplicaGuard() { - task.to_immut(); - // return the reference to the Replica. - replica.inner.reset(tcreplica); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Replica::Replica() { - inner = unique_tcreplica_ptr(tc_replica_new_in_memory(), - [](TCReplica *rep) { tc_replica_free(rep); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Replica::Replica(Replica &&other) noexcept { - // move inner from other - inner = unique_tcreplica_ptr(other.inner.release(), [](TCReplica *rep) { tc_replica_free(rep); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Replica &tc::Replica::operator=(Replica &&other) noexcept { - if (this != &other) { - // move inner from other - inner = - unique_tcreplica_ptr(other.inner.release(), [](TCReplica *rep) { tc_replica_free(rep); }); - } - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Replica::Replica(const std::string &dir, bool create_if_missing) { - TCString path = tc_string_borrow(dir.c_str()); - TCString error; - auto tcreplica = tc_replica_new_on_disk(path, create_if_missing, &error); - if (!tcreplica) { - auto errmsg = format("Could not create replica at {1}: {2}", dir, tc_string_content(&error)); - tc_string_free(&error); - throw errmsg; - } - inner = unique_tcreplica_ptr(tcreplica, [](TCReplica *rep) { tc_replica_free(rep); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet tc::Replica::working_set() { - TCWorkingSet *tcws = tc_replica_working_set(&*inner); - if (!tcws) { - throw replica_error(); - } - return WorkingSet{tcws}; -} - -//////////////////////////////////////////////////////////////////////////////// -std::optional tc::Replica::get_task(const std::string &uuid) { - TCTask *tctask = tc_replica_get_task(&*inner, uuid2tc(uuid)); - if (!tctask) { - auto error = tc_replica_error(&*inner); - if (error.ptr) { - throw replica_error(error); - } else { - return std::nullopt; - } - } - return std::make_optional(Task(tctask)); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Task tc::Replica::new_task(tc::Status status, const std::string &description) { - TCTask *tctask = tc_replica_new_task(&*inner, (tc::ffi::TCStatus)status, string2tc(description)); - if (!tctask) { - throw replica_error(); - } - return Task(tctask); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Task tc::Replica::import_task_with_uuid(const std::string &uuid) { - TCTask *tctask = tc_replica_import_task_with_uuid(&*inner, uuid2tc(uuid)); - if (!tctask) { - throw replica_error(); - } - return Task(tctask); -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::delete_task(const std::string &uuid) { - auto res = tc_replica_delete_task(&*inner, uuid2tc(uuid)); - if (res != TC_RESULT_OK) { - throw replica_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::expire_tasks() { - auto res = tc_replica_expire_tasks(&*inner); - if (res != TC_RESULT_OK) { - throw replica_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::sync(Server server, bool avoid_snapshots) { - // The server remains owned by this function, per tc_replica_sync docs. - auto res = tc_replica_sync(&*inner, server.inner.get(), avoid_snapshots); - if (res != TC_RESULT_OK) { - throw replica_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -TCReplicaOpList tc::Replica::get_undo_ops() { return tc_replica_get_undo_ops(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::commit_undo_ops(TCReplicaOpList tc_undo_ops, int32_t *undone_out) { - auto res = tc_replica_commit_undo_ops(&*inner, tc_undo_ops, undone_out); - if (res != TC_RESULT_OK) { - throw replica_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::free_replica_ops(TCReplicaOpList tc_undo_ops) { - tc_replica_op_list_free(&tc_undo_ops); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_uuid(TCReplicaOp &tc_replica_op) const { - TCString uuid = tc_replica_op_get_uuid(&tc_replica_op); - return tc2string(uuid); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_property(TCReplicaOp &tc_replica_op) const { - TCString property = tc_replica_op_get_property(&tc_replica_op); - return tc2string(property); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_value(TCReplicaOp &tc_replica_op) const { - TCString value = tc_replica_op_get_value(&tc_replica_op); - return tc2string(value); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_old_value(TCReplicaOp &tc_replica_op) const { - TCString old_value = tc_replica_op_get_old_value(&tc_replica_op); - return tc2string(old_value); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_timestamp(TCReplicaOp &tc_replica_op) const { - TCString timestamp = tc_replica_op_get_timestamp(&tc_replica_op); - return tc2string(timestamp); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::get_op_old_task_description(TCReplicaOp &tc_replica_op) const { - TCString description = tc_replica_op_get_old_task_description(&tc_replica_op); - return tc2string(description); -} - -//////////////////////////////////////////////////////////////////////////////// -int64_t tc::Replica::num_local_operations() { - auto num = tc_replica_num_local_operations(&*inner); - if (num < 0) { - throw replica_error(); - } - return num; -} - -//////////////////////////////////////////////////////////////////////////////// -int64_t tc::Replica::num_undo_points() { - auto num = tc_replica_num_undo_points(&*inner); - if (num < 0) { - throw replica_error(); - } - return num; -} - -//////////////////////////////////////////////////////////////////////////////// -std::vector tc::Replica::all_tasks() { - TCTaskList tasks = tc_replica_all_tasks(&*inner); - if (!tasks.items) { - throw replica_error(); - } - - std::vector all; - all.reserve(tasks.len); - for (size_t i = 0; i < tasks.len; i++) { - auto tctask = tc_task_list_take(&tasks, i); - if (tctask) { - all.push_back(Task(tctask)); - } - } - - return all; -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Replica::rebuild_working_set(bool force) { - auto res = tc_replica_rebuild_working_set(&*inner, force); - if (res != TC_RESULT_OK) { - throw replica_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -tc::ReplicaGuard tc::Replica::mutate_task(tc::Task &task) { return ReplicaGuard(*this, task); } - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::replica_error() { return replica_error(tc_replica_error(&*inner)); } - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Replica::replica_error(TCString error) { - std::string errmsg; - if (!error.ptr) { - errmsg = std::string("Unknown TaskChampion error"); - } else { - errmsg = std::string(tc_string_content(&error)); - } - tc_string_free(&error); - return errmsg; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Replica.h b/src/tc/Replica.h deleted file mode 100644 index 8dd3ef188..000000000 --- a/src/tc/Replica.h +++ /dev/null @@ -1,127 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_REPLICA -#define INCLUDED_TC_REPLICA - -#include -#include -#include -#include -#include - -#include "tc/Task.h" -#include "tc/ffi.h" - -namespace tc { -class Task; -class WorkingSet; -class Server; - -// a unique_ptr to a TCReplica which will automatically free the value when -// it goes out of scope. -using unique_tcreplica_ptr = - std::unique_ptr>; - -// ReplicaGuard uses RAII to ensure that a Replica is not accessed while it -// is mutably borrowed (specifically, to make a task mutable). -class ReplicaGuard { - protected: - friend class Replica; - explicit ReplicaGuard(Replica &, Task &); - - public: - ~ReplicaGuard(); - - // No moving or copying allowed - ReplicaGuard(const ReplicaGuard &) = delete; - ReplicaGuard &operator=(const ReplicaGuard &) = delete; - ReplicaGuard(ReplicaGuard &&) = delete; - ReplicaGuard &operator=(Replica &&) = delete; - - private: - Replica &replica; - tc::ffi::TCReplica *tcreplica; - Task &task; -}; - -// Replica wraps the TCReplica type, managing its memory, errors, and so on. -// -// Except as noted, method names match the suffix to `tc_replica_..`. -class Replica { - public: - Replica(); // tc_replica_new_in_memory - Replica(const std::string &dir, bool create_if_missing); // tc_replica_new_on_disk - - // This object "owns" inner, so copy is not allowed. - Replica(const Replica &) = delete; - Replica &operator=(const Replica &) = delete; - - // Explicit move constructor and assignment - Replica(Replica &&) noexcept; - Replica &operator=(Replica &&) noexcept; - - std::vector all_tasks(); - // TODO: struct TCUuidList tc_replica_all_task_uuids(struct TCReplica *rep); - tc::WorkingSet working_set(); - std::optional get_task(const std::string &uuid); - tc::Task new_task(Status status, const std::string &description); - tc::Task import_task_with_uuid(const std::string &uuid); - void delete_task(const std::string &uuid); - // TODO: struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid - // tcuuid); - void expire_tasks(); - void sync(Server server, bool avoid_snapshots); - tc::ffi::TCReplicaOpList get_undo_ops(); - void commit_undo_ops(tc::ffi::TCReplicaOpList tc_undo_ops, int32_t *undone_out); - void free_replica_ops(tc::ffi::TCReplicaOpList tc_undo_ops); - std::string get_op_uuid(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_property(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_value(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_old_value(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_timestamp(tc::ffi::TCReplicaOp &tc_replica_op) const; - std::string get_op_old_task_description(tc::ffi::TCReplicaOp &tc_replica_op) const; - int64_t num_local_operations(); - int64_t num_undo_points(); - // TODO: TCResult tc_replica_add_undo_point(struct TCReplica *rep, bool force); - void rebuild_working_set(bool force); - - ReplicaGuard mutate_task(tc::Task &); - void immut_task(tc::Task &); - - protected: - friend class ReplicaGuard; - unique_tcreplica_ptr inner; - - // construct an error message from tc_replica_error, or from the given - // string retrieved from tc_replica_error. - std::string replica_error(); - std::string replica_error(tc::ffi::TCString string); -}; -} // namespace tc - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Server.cpp b/src/tc/Server.cpp deleted file mode 100644 index e5adef9dd..000000000 --- a/src/tc/Server.cpp +++ /dev/null @@ -1,109 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#include -// cmake.h include header must come first - -#include - -#include "tc/Server.h" -#include "tc/util.h" - -using namespace tc::ffi; - -//////////////////////////////////////////////////////////////////////////////// -tc::Server tc::Server::new_local(const std::string &server_dir) { - TCString tc_server_dir = tc_string_borrow(server_dir.c_str()); - TCString error; - auto tcserver = tc_server_new_local(tc_server_dir, &error); - if (!tcserver) { - std::string errmsg = format("Could not configure local server at {1}: {2}", server_dir, - tc_string_content(&error)); - tc_string_free(&error); - throw errmsg; - } - return Server(unique_tcserver_ptr(tcserver, [](TCServer *rep) { tc_server_free(rep); })); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Server tc::Server::new_sync(const std::string &url, const std::string &client_id, - const std::string &encryption_secret) { - TCString tc_url = tc_string_borrow(url.c_str()); - TCString tc_client_id = tc_string_borrow(client_id.c_str()); - TCString tc_encryption_secret = tc_string_borrow(encryption_secret.c_str()); - - TCUuid tc_client_uuid; - if (tc_uuid_from_str(tc_client_id, &tc_client_uuid) != TC_RESULT_OK) { - tc_string_free(&tc_url); - tc_string_free(&tc_encryption_secret); - throw format("client_id '{1}' is not a valid UUID", client_id); - } - - TCString error; - auto tcserver = tc_server_new_sync(tc_url, tc_client_uuid, tc_encryption_secret, &error); - if (!tcserver) { - std::string errmsg = format("Could not configure connection to server at {1}: {2}", url, - tc_string_content(&error)); - tc_string_free(&error); - throw errmsg; - } - return Server(unique_tcserver_ptr(tcserver, [](TCServer *rep) { tc_server_free(rep); })); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Server tc::Server::new_gcp(const std::string &bucket, const std::string &credential_path, - const std::string &encryption_secret) { - TCString tc_bucket = tc_string_borrow(bucket.c_str()); - TCString tc_encryption_secret = tc_string_borrow(encryption_secret.c_str()); - TCString tc_credential_path = tc_string_borrow(credential_path.c_str()); - - TCString error; - auto tcserver = tc_server_new_gcp(tc_bucket, tc_credential_path, tc_encryption_secret, &error); - if (!tcserver) { - std::string errmsg = format("Could not configure connection to GCP bucket {1}: {2}", bucket, - tc_string_content(&error)); - tc_string_free(&error); - throw errmsg; - } - return Server(unique_tcserver_ptr(tcserver, [](TCServer *rep) { tc_server_free(rep); })); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Server::Server(tc::Server &&other) noexcept { - // move inner from other - inner = unique_tcserver_ptr(other.inner.release(), [](TCServer *rep) { tc_server_free(rep); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Server &tc::Server::operator=(tc::Server &&other) noexcept { - if (this != &other) { - // move inner from other - inner = unique_tcserver_ptr(other.inner.release(), [](TCServer *rep) { tc_server_free(rep); }); - } - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Server.h b/src/tc/Server.h deleted file mode 100644 index 489d73425..000000000 --- a/src/tc/Server.h +++ /dev/null @@ -1,85 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_SERVER -#define INCLUDED_TC_SERVER - -#include -#include -#include -#include -#include - -#include "tc/ffi.h" - -namespace tc { -// a unique_ptr to a TCServer which will automatically free the value when -// it goes out of scope. -using unique_tcserver_ptr = - std::unique_ptr>; - -// Server wraps the TCServer type, managing its memory, errors, and so on. -// -// Except as noted, method names match the suffix to `tc_server_..`. -class Server { - public: - // Construct a null server - Server() = default; - - // Construct a local server (tc_server_new_local). - static Server new_local(const std::string &server_dir); - - // Construct a remote server (tc_server_new_sync). - static Server new_sync(const std::string &url, const std::string &client_id, - const std::string &encryption_secret); - - // Construct a GCP server (tc_server_new_gcp). - static Server new_gcp(const std::string &bucket, const std::string &credential_path, - const std::string &encryption_secret); - - // This object "owns" inner, so copy is not allowed. - Server(const Server &) = delete; - Server &operator=(const Server &) = delete; - - // Explicit move constructor and assignment - Server(Server &&) noexcept; - Server &operator=(Server &&) noexcept; - - protected: - Server(unique_tcserver_ptr inner) : inner(std::move(inner)) {}; - - unique_tcserver_ptr inner; - - // Replica accesses the inner pointer to call tc_replica_sync - friend class Replica; - - // construct an error message from the given string. - std::string server_error(tc::ffi::TCString string); -}; -} // namespace tc - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Task.cpp b/src/tc/Task.cpp deleted file mode 100644 index 5b5e5fd9a..000000000 --- a/src/tc/Task.cpp +++ /dev/null @@ -1,162 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#include -// cmake.h include header must come first - -#include - -#include "tc/Task.h" -#include "tc/util.h" - -using namespace tc::ffi; - -//////////////////////////////////////////////////////////////////////////////// -tc::Task::Task(TCTask* tctask) { - inner = unique_tctask_ptr(tctask, [](TCTask* task) { tc_task_free(task); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Task::Task(Task&& other) noexcept { - // move inner from other - inner = unique_tctask_ptr(other.inner.release(), [](TCTask* task) { tc_task_free(task); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Task& tc::Task::operator=(Task&& other) noexcept { - if (this != &other) { - // move inner from other - inner = unique_tctask_ptr(other.inner.release(), [](TCTask* task) { tc_task_free(task); }); - } - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Task::to_mut(TCReplica* replica) { tc_task_to_mut(&*inner, replica); } - -//////////////////////////////////////////////////////////////////////////////// -void tc::Task::to_immut() { tc_task_to_immut(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Task::get_uuid() const { - auto uuid = tc_task_get_uuid(&*inner); - return tc2uuid(uuid); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::Status tc::Task::get_status() const { - auto status = tc_task_get_status(&*inner); - return tc::Status(status); -} - -//////////////////////////////////////////////////////////////////////////////// -std::map tc::Task::get_taskmap() const { - TCKVList kv = tc_task_get_taskmap(&*inner); - if (!kv.items) { - throw task_error(); - } - - std::map taskmap; - for (size_t i = 0; i < kv.len; i++) { - auto k = tc2string_clone(kv.items[i].key); - auto v = tc2string_clone(kv.items[i].value); - taskmap[k] = v; - } - - return taskmap; -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Task::get_description() const { - auto desc = tc_task_get_description(&*inner); - return tc2string(desc); -} - -//////////////////////////////////////////////////////////////////////////////// -std::optional tc::Task::get_value(std::string property) const { - auto maybe_desc = tc_task_get_value(&*inner, string2tc(property)); - if (maybe_desc.ptr == NULL) { - return std::nullopt; - } - return std::make_optional(tc2string(maybe_desc)); -} - -bool tc::Task::is_waiting() const { return tc_task_is_waiting(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -bool tc::Task::is_active() const { return tc_task_is_active(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -bool tc::Task::is_blocked() const { return tc_task_is_blocked(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -bool tc::Task::is_blocking() const { return tc_task_is_blocking(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -void tc::Task::set_status(tc::Status status) { - TCResult res = tc_task_set_status(&*inner, (TCStatus)status); - if (res != TC_RESULT_OK) { - throw task_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Task::set_value(std::string property, std::optional value) { - TCResult res; - if (value.has_value()) { - res = tc_task_set_value(&*inner, string2tc(property), string2tc(value.value())); - } else { - TCString nullstr; - nullstr.ptr = NULL; - res = tc_task_set_value(&*inner, string2tc(property), nullstr); - } - if (res != TC_RESULT_OK) { - throw task_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -void tc::Task::set_modified(time_t modified) { - TCResult res = tc_task_set_modified(&*inner, modified); - if (res != TC_RESULT_OK) { - throw task_error(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc::Task::task_error() const { - TCString error = tc_task_error(&*inner); - std::string errmsg; - if (!error.ptr) { - errmsg = std::string("Unknown TaskChampion error"); - } else { - errmsg = std::string(tc_string_content(&error)); - } - tc_string_free(&error); - return errmsg; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/Task.h b/src/tc/Task.h deleted file mode 100644 index d087dc810..000000000 --- a/src/tc/Task.h +++ /dev/null @@ -1,130 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell, Tomas Babej, Paul Beckingham, Federico Hernandez. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_TASK -#define INCLUDED_TC_TASK - -#include -#include -#include -#include -#include - -#include "tc/ffi.h" - -namespace tc { -class Replica; -class ReplicaGuard; - -enum Status { - Pending = tc::ffi::TC_STATUS_PENDING, - Completed = tc::ffi::TC_STATUS_COMPLETED, - Deleted = tc::ffi::TC_STATUS_DELETED, - Recurring = tc::ffi::TC_STATUS_RECURRING, - Unknown = tc::ffi::TC_STATUS_UNKNOWN, -}; - -// a unique_ptr to a TCReplica which will automatically free the value when -// it goes out of scope. -using unique_tctask_ptr = std::unique_ptr>; - -// Task wraps the TCTask type, managing its memory, errors, and so on. -// -// Except as noted, method names match the suffix to `tc_task_..`. -class Task { - protected: - // Tasks may only be created and made mutable/immutable - // by tc::Replica - friend class tc::Replica; - explicit Task(tc::ffi::TCTask *); - - // RplicaGuard handles mut/immut - friend class tc::ReplicaGuard; - void to_mut(tc::ffi::TCReplica *); - void to_immut(); - - public: - // This object "owns" inner, so copy is not allowed. - Task(const Task &) = delete; - Task &operator=(const Task &) = delete; - - // Explicit move constructor and assignment - Task(Task &&) noexcept; - Task &operator=(Task &&) noexcept; - - std::string get_uuid() const; - Status get_status() const; - std::map get_taskmap() const; - std::string get_description() const; - std::optional get_value(std::string property) const; - // TODO: time_t tc_task_get_entry(struct TCTask *task); - // TODO: time_t tc_task_get_wait(struct TCTask *task); - // TODO: time_t tc_task_get_modified(struct TCTask *task); - bool is_waiting() const; - bool is_active() const; - bool is_blocked() const; - bool is_blocking() const; - // TODO: bool tc_task_has_tag(struct TCTask *task, struct TCString tag); - // TODO: struct TCStringList tc_task_get_tags(struct TCTask *task); - // TODO: struct TCAnnotationList tc_task_get_annotations(struct TCTask *task); - // TODO: struct TCString tc_task_get_uda(struct TCTask *task, struct TCString ns, struct TCString - // key); - // TODO: struct TCString tc_task_get_legacy_uda(struct TCTask *task, struct TCString key); - // TODO: struct TCUdaList tc_task_get_udas(struct TCTask *task); - // TODO: struct TCUdaList tc_task_get_legacy_udas(struct TCTask *task); - void set_status(Status status); - // TODO: TCResult tc_task_set_description(struct TCTask *task, struct TCString description); - void set_value(std::string property, std::optional value); - // TODO: TCResult tc_task_set_entry(struct TCTask *task, time_t entry); - // TODO: TCResult tc_task_set_wait(struct TCTask *task, time_t wait); - void set_modified(time_t modified); - // TODO: TCResult tc_task_start(struct TCTask *task); - // TODO: TCResult tc_task_stop(struct TCTask *task); - // TODO: TCResult tc_task_done(struct TCTask *task); - // TODO: TCResult tc_task_delete(struct TCTask *task); - // TODO: TCResult tc_task_add_tag(struct TCTask *task, struct TCString tag); - // TODO: TCResult tc_task_remove_tag(struct TCTask *task, struct TCString tag); - // TODO: TCResult tc_task_add_annotation(struct TCTask *task, struct TCAnnotation *annotation); - // TODO: TCResult tc_task_remove_annotation(struct TCTask *task, int64_t entry); - // TODO: TCResult tc_task_set_uda(struct TCTask *task, - // TODO: TCResult tc_task_remove_uda(struct TCTask *task, struct TCString ns, struct TCString - // key); - // TODO: TCResult tc_task_set_legacy_uda(struct TCTask *task, struct TCString key, struct TCString - // value); - // TODO: TCResult tc_task_remove_legacy_uda(struct TCTask *task, struct TCString key); - - private: - unique_tctask_ptr inner; - - std::string task_error() const; // tc_task_error -}; -} // namespace tc - -// TODO: struct TCTask *tc_task_list_take(struct TCTaskList *tasks, size_t index); -// TODO: void tc_task_list_free(struct TCTaskList *tasks); - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/WorkingSet.cpp b/src/tc/WorkingSet.cpp deleted file mode 100644 index 1adeb8f9d..000000000 --- a/src/tc/WorkingSet.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#include -// cmake.h include header must come first - -#include - -#include "tc/Task.h" -#include "tc/WorkingSet.h" -#include "tc/util.h" - -using namespace tc::ffi; - -//////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet::WorkingSet(WorkingSet&& other) noexcept { - // move inner from other - inner = unique_tcws_ptr(other.inner.release(), [](TCWorkingSet* ws) { tc_working_set_free(ws); }); -} - -//////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet& tc::WorkingSet::operator=(WorkingSet&& other) noexcept { - if (this != &other) { - // move inner from other - inner = - unique_tcws_ptr(other.inner.release(), [](TCWorkingSet* ws) { tc_working_set_free(ws); }); - } - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// -tc::WorkingSet::WorkingSet(tc::ffi::TCWorkingSet* tcws) { - inner = unique_tcws_ptr(tcws, [](TCWorkingSet* ws) { tc_working_set_free(ws); }); -} - -//////////////////////////////////////////////////////////////////////////////// -size_t tc::WorkingSet::len() const noexcept { return tc_working_set_len(&*inner); } - -//////////////////////////////////////////////////////////////////////////////// -size_t tc::WorkingSet::largest_index() const noexcept { - return tc_working_set_largest_index(&*inner); -} - -//////////////////////////////////////////////////////////////////////////////// -std::optional tc::WorkingSet::by_index(size_t index) const noexcept { - TCUuid uuid; - if (tc_working_set_by_index(&*inner, index, &uuid)) { - return std::make_optional(tc2uuid(uuid)); - } else { - return std::nullopt; - } -} - -//////////////////////////////////////////////////////////////////////////////// -std::optional tc::WorkingSet::by_uuid(const std::string& uuid) const noexcept { - auto index = tc_working_set_by_uuid(&*inner, uuid2tc(uuid)); - if (index > 0) { - return std::make_optional(index); - } else { - return std::nullopt; - } -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/WorkingSet.h b/src/tc/WorkingSet.h deleted file mode 100644 index e9a3a5d5c..000000000 --- a/src/tc/WorkingSet.h +++ /dev/null @@ -1,74 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_WORKINGSET -#define INCLUDED_TC_WORKINGSET - -#include -#include -#include -#include - -#include "tc/Task.h" -#include "tc/ffi.h" - -namespace tc { -class Task; - -// a unique_ptr to a TCWorkingSet which will automatically free the value when -// it goes out of scope. -using unique_tcws_ptr = - std::unique_ptr>; - -// WorkingSet wraps the TCWorkingSet type, managing its memory, errors, and so on. -// -// Except as noted, method names match the suffix to `tc_working_set_..`. -class WorkingSet { - protected: - friend class tc::Replica; - WorkingSet(tc::ffi::TCWorkingSet *); // via tc_replica_working_set - - public: - // This object "owns" inner, so copy is not allowed. - WorkingSet(const WorkingSet &) = delete; - WorkingSet &operator=(const WorkingSet &) = delete; - - // Explicit move constructor and assignment - WorkingSet(WorkingSet &&) noexcept; - WorkingSet &operator=(WorkingSet &&) noexcept; - - size_t len() const noexcept; // tc_working_set_len - size_t largest_index() const noexcept; // tc_working_set_largest_index - std::optional by_index(size_t index) const noexcept; // tc_working_set_by_index - std::optional by_uuid(const std::string &index) const noexcept; // tc_working_set_by_uuid - - private: - unique_tcws_ptr inner; -}; -} // namespace tc - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/ffi.h b/src/tc/ffi.h deleted file mode 100644 index 213fc4074..000000000 --- a/src/tc/ffi.h +++ /dev/null @@ -1,36 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell, Tomas Babej, Paul Beckingham, Federico Hernandez. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_FFI -#define INCLUDED_TC_FFI - -// The entire FFI API is embedded in the `tc::ffi` namespace -namespace tc::ffi { -#include -} - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/tc/lib/Cargo.toml b/src/tc/lib/Cargo.toml deleted file mode 100644 index 2a48d5293..000000000 --- a/src/tc/lib/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "taskchampion-lib" -version = "0.1.0" -edition = "2021" -publish = false - -[lib] -name = "taskchampion_lib" -crate-type = ["staticlib", "rlib"] - -[dependencies] -libc.workspace = true -anyhow.workspace = true -ffizz-header.workspace = true -taskchampion.workspace = true - -[dev-dependencies] -pretty_assertions.workspace = true diff --git a/src/tc/lib/src/annotation.rs b/src/tc/lib/src/annotation.rs deleted file mode 100644 index 5edfda4c8..000000000 --- a/src/tc/lib/src/annotation.rs +++ /dev/null @@ -1,186 +0,0 @@ -use crate::traits::*; -use crate::types::*; -use taskchampion::chrono::prelude::*; - -#[ffizz_header::item] -#[ffizz(order = 400)] -/// ***** TCAnnotation ***** -/// -/// TCAnnotation contains the details of an annotation. -/// -/// # Safety -/// -/// An annotation must be initialized from a tc_.. function, and later freed -/// with `tc_annotation_free` or `tc_annotation_list_free`. -/// -/// Any function taking a `*TCAnnotation` requires: -/// - the pointer must not be NUL; -/// - the pointer must be one previously returned from a tc_… function; -/// - the memory referenced by the pointer must never be modified by C code; and -/// - ownership transfers to the called function, and the value must not be used -/// after the call returns. In fact, the value will be zeroed out to ensure this. -/// -/// TCAnnotations are not threadsafe. -/// -/// ```c -/// typedef struct TCAnnotation { -/// // Time the annotation was made. Must be nonzero. -/// time_t entry; -/// // Content of the annotation. Must not be NULL. -/// TCString description; -/// } TCAnnotation; -/// ``` -#[repr(C)] -pub struct TCAnnotation { - pub entry: libc::time_t, - pub description: TCString, -} - -impl PassByValue for TCAnnotation { - // NOTE: we cannot use `RustType = Annotation` here because conversion of the - // Rust to a String can fail. - type RustType = (DateTime, RustString<'static>); - - unsafe fn from_ctype(mut self) -> Self::RustType { - // SAFETY: - // - any time_t value is valid - // - time_t is copy, so ownership is not important - let entry = unsafe { libc::time_t::val_from_arg(self.entry) }.unwrap(); - // SAFETY: - // - self.description is valid (came from return_val in as_ctype) - // - self is owned, so we can take ownership of this TCString - let description = - unsafe { TCString::take_val_from_arg(&mut self.description, TCString::default()) }; - (entry, description) - } - - fn as_ctype((entry, description): Self::RustType) -> Self { - TCAnnotation { - entry: libc::time_t::as_ctype(Some(entry)), - // SAFETY: - // - ownership of the TCString tied to ownership of Self - description: unsafe { TCString::return_val(description) }, - } - } -} - -impl Default for TCAnnotation { - fn default() -> Self { - TCAnnotation { - entry: 0 as libc::time_t, - description: TCString::default(), - } - } -} - -#[ffizz_header::item] -#[ffizz(order = 410)] -/// ***** TCAnnotationList ***** -/// -/// TCAnnotationList represents a list of annotations. -/// -/// The content of this struct must be treated as read-only. -/// -/// ```c -/// typedef struct TCAnnotationList { -/// // number of annotations in items -/// size_t len; -/// // reserved -/// size_t _u1; -/// // Array of annotations. These remain owned by the TCAnnotationList instance and will be freed by -/// // tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList. -/// struct TCAnnotation *items; -/// } TCAnnotationList; -/// ``` -#[repr(C)] -pub struct TCAnnotationList { - len: libc::size_t, - /// total size of items (internal use only) - capacity: libc::size_t, - items: *mut TCAnnotation, -} - -impl CList for TCAnnotationList { - type Element = TCAnnotation; - - unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { - TCAnnotationList { - len, - capacity: cap, - items, - } - } - - fn slice(&mut self) -> &mut [Self::Element] { - // SAFETY: - // - because we have &mut self, we have read/write access to items[0..len] - // - all items are properly initialized Element's - // - return value lifetime is equal to &mmut self's, so access is exclusive - // - items and len came from Vec, so total size is < isize::MAX - unsafe { std::slice::from_raw_parts_mut(self.items, self.len) } - } - - fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { - (self.items, self.len, self.capacity) - } -} - -#[ffizz_header::item] -#[ffizz(order = 401)] -/// Free a TCAnnotation instance. The instance, and the TCString it contains, must not be used -/// after this call. -/// -/// ```c -/// EXTERN_C void tc_annotation_free(struct TCAnnotation *tcann); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_annotation_free(tcann: *mut TCAnnotation) { - debug_assert!(!tcann.is_null()); - // SAFETY: - // - tcann is not NULL - // - *tcann is a valid TCAnnotation (caller promised to treat it as read-only) - let annotation = unsafe { TCAnnotation::take_val_from_arg(tcann, TCAnnotation::default()) }; - drop(annotation); -} - -#[ffizz_header::item] -#[ffizz(order = 411)] -/// Free a TCAnnotationList instance. The instance, and all TCAnnotations it contains, must not be used after -/// this call. -/// -/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCAnnotationList. -/// -/// ```c -/// EXTERN_C void tc_annotation_list_free(struct TCAnnotationList *tcanns); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_annotation_list_free(tcanns: *mut TCAnnotationList) { - // SAFETY: - // - tcanns is not NULL and points to a valid TCAnnotationList (caller is not allowed to - // modify the list) - // - caller promises not to use the value after return - unsafe { drop_value_list(tcanns) } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn empty_list_has_non_null_pointer() { - let tcanns = unsafe { TCAnnotationList::return_val(Vec::new()) }; - assert!(!tcanns.items.is_null()); - assert_eq!(tcanns.len, 0); - assert_eq!(tcanns.capacity, 0); - } - - #[test] - fn free_sets_null_pointer() { - let mut tcanns = unsafe { TCAnnotationList::return_val(Vec::new()) }; - // SAFETY: testing expected behavior - unsafe { tc_annotation_list_free(&mut tcanns) }; - assert!(tcanns.items.is_null()); - assert_eq!(tcanns.len, 0); - assert_eq!(tcanns.capacity, 0); - } -} diff --git a/src/tc/lib/src/atomic.rs b/src/tc/lib/src/atomic.rs deleted file mode 100644 index 01c72059e..000000000 --- a/src/tc/lib/src/atomic.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! Trait implementations for a few atomic types - -use crate::traits::*; -use taskchampion::chrono::{DateTime, Utc}; -use taskchampion::utc_timestamp; - -impl PassByValue for usize { - type RustType = usize; - - unsafe fn from_ctype(self) -> usize { - self - } - - fn as_ctype(arg: usize) -> usize { - arg - } -} - -/// Convert an Option> to a libc::time_t, or zero if not set. -impl PassByValue for libc::time_t { - type RustType = Option>; - - unsafe fn from_ctype(self) -> Option> { - if self != 0 { - return Some(utc_timestamp(self)); - } - None - } - - fn as_ctype(arg: Option>) -> libc::time_t { - arg.map(|ts| ts.timestamp() as libc::time_t) - .unwrap_or(0 as libc::time_t) - } -} diff --git a/src/tc/lib/src/kv.rs b/src/tc/lib/src/kv.rs deleted file mode 100644 index 3831c7016..000000000 --- a/src/tc/lib/src/kv.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::traits::*; -use crate::types::*; - -#[ffizz_header::item] -#[ffizz(order = 600)] -/// ***** TCKV ***** -/// -/// TCKV contains a key/value pair that is part of a task. -/// -/// Neither key nor value are ever NULL. They remain owned by the TCKV and -/// will be freed when it is freed with tc_kv_list_free. -/// -/// ```c -/// typedef struct TCKV { -/// struct TCString key; -/// struct TCString value; -/// } TCKV; -/// ``` -#[repr(C)] -#[derive(Debug)] -pub struct TCKV { - pub key: TCString, - pub value: TCString, -} - -impl PassByValue for TCKV { - type RustType = (RustString<'static>, RustString<'static>); - - unsafe fn from_ctype(self) -> Self::RustType { - // SAFETY: - // - self.key is not NULL (field docstring) - // - self.key came from return_ptr in as_ctype - // - self is owned, so we can take ownership of this TCString - let key = unsafe { TCString::val_from_arg(self.key) }; - // SAFETY: (same) - let value = unsafe { TCString::val_from_arg(self.value) }; - (key, value) - } - - fn as_ctype((key, value): Self::RustType) -> Self { - TCKV { - // SAFETY: - // - ownership of the TCString tied to ownership of Self - key: unsafe { TCString::return_val(key) }, - // SAFETY: - // - ownership of the TCString tied to ownership of Self - value: unsafe { TCString::return_val(value) }, - } - } -} - -#[ffizz_header::item] -#[ffizz(order = 610)] -/// ***** TCKVList ***** -/// -/// TCKVList represents a list of key/value pairs. -/// -/// The content of this struct must be treated as read-only. -/// -/// ```c -/// typedef struct TCKVList { -/// // number of key/value pairs in items -/// size_t len; -/// // reserved -/// size_t _u1; -/// // Array of TCKV's. These remain owned by the TCKVList instance and will be freed by -/// // tc_kv_list_free. This pointer is never NULL for a valid TCKVList. -/// struct TCKV *items; -/// } TCKVList; -/// ``` -#[repr(C)] -#[derive(Debug)] -pub struct TCKVList { - pub len: libc::size_t, - /// total size of items (internal use only) - pub _capacity: libc::size_t, - pub items: *mut TCKV, -} - -impl CList for TCKVList { - type Element = TCKV; - - unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { - TCKVList { - len, - _capacity: cap, - items, - } - } - - fn slice(&mut self) -> &mut [Self::Element] { - // SAFETY: - // - because we have &mut self, we have read/write access to items[0..len] - // - all items are properly initialized Element's - // - return value lifetime is equal to &mmut self's, so access is exclusive - // - items and len came from Vec, so total size is < isize::MAX - unsafe { std::slice::from_raw_parts_mut(self.items, self.len) } - } - - fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { - (self.items, self.len, self._capacity) - } -} - -impl Default for TCKVList { - fn default() -> Self { - // SAFETY: - // - caller will free this list - unsafe { TCKVList::return_val(Vec::new()) } - } -} - -// NOTE: callers never have a TCKV that is not in a list, so there is no tc_kv_free. - -#[ffizz_header::item] -#[ffizz(order = 611)] -/// Free a TCKVList instance. The instance, and all TCKVs it contains, must not be used after -/// this call. -/// -/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCKVList. -/// -/// ```c -/// EXTERN_C void tc_kv_list_free(struct TCKVList *tckvs); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_kv_list_free(tckvs: *mut TCKVList) { - // SAFETY: - // - tckvs is not NULL and points to a valid TCKVList (caller is not allowed to - // modify the list) - // - caller promises not to use the value after return - unsafe { drop_value_list(tckvs) } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn empty_list_has_non_null_pointer() { - let tckvs = unsafe { TCKVList::return_val(Vec::new()) }; - assert!(!tckvs.items.is_null()); - assert_eq!(tckvs.len, 0); - assert_eq!(tckvs._capacity, 0); - } - - #[test] - fn free_sets_null_pointer() { - let mut tckvs = unsafe { TCKVList::return_val(Vec::new()) }; - // SAFETY: testing expected behavior - unsafe { tc_kv_list_free(&mut tckvs) }; - assert!(tckvs.items.is_null()); - assert_eq!(tckvs.len, 0); - assert_eq!(tckvs._capacity, 0); - } -} diff --git a/src/tc/lib/src/lib.rs b/src/tc/lib/src/lib.rs deleted file mode 100644 index ad1fc31f7..000000000 --- a/src/tc/lib/src/lib.rs +++ /dev/null @@ -1,172 +0,0 @@ -#![warn(unsafe_op_in_unsafe_fn)] -#![allow(unused_unsafe)] -// Not working yet in stable - https://github.com/rust-lang/rust-clippy/issues/8020 -// #![warn(clippy::undocumented_unsafe_blocks)] - -// docstrings for extern "C" functions are reflected into C, and do not benefit -// from safety docs. -#![allow(clippy::missing_safety_doc)] -// deny some things that are typically warnings -#![deny(clippy::derivable_impls)] -#![deny(clippy::wrong_self_convention)] -#![deny(clippy::extra_unused_lifetimes)] -#![deny(clippy::unnecessary_to_owned)] - -// ffizz_header orders: -// -// 000-099: header matter -// 100-199: TCResult -// 200-299: TCString / List -// 300-399: TCUuid / List -// 400-499: TCAnnotation / List -// 500-599: TCUda / List -// 600-699: TCKV / List -// 700-799: TCStatus -// 800-899: TCServer -// 900-999: TCReplica -// 1000-1099: TCTask / List -// 1100-1199: TCWorkingSet -// 10000-10099: footer - -ffizz_header::snippet! { -#[ffizz(name="intro", order=0)] -/// TaskChampion -/// -/// This file defines the C interface to libtaskchampion. This is a thin wrapper around the Rust -/// `taskchampion` crate. Refer to the documentation for that crate at -/// https://docs.rs/taskchampion/latest/taskchampion/ for API details. The comments in this file -/// focus mostly on the low-level details of passing values to and from TaskChampion. -/// -/// # Overview -/// -/// This library defines four major types used to interact with the API, that map directly to Rust -/// types. -/// -/// * TCReplica - see https://docs.rs/taskchampion/latest/taskchampion/struct.Replica.html -/// * TCTask - see https://docs.rs/taskchampion/latest/taskchampion/struct.Task.html -/// * TCServer - see https://docs.rs/taskchampion/latest/taskchampion/trait.Server.html -/// * TCWorkingSet - see https://docs.rs/taskchampion/latest/taskchampion/struct.WorkingSet.html -/// -/// It also defines a few utility types: -/// -/// * TCString - a wrapper around both C (NUL-terminated) and Rust (always utf-8) strings. -/// * TC…List - a list of objects represented as a C array -/// * see below for the remainder -/// -/// # Safety -/// -/// Each type contains specific instructions to ensure memory safety. The general rules are as -/// follows. -/// -/// No types in this library are threadsafe. All values should be used in only one thread for their -/// entire lifetime. It is safe to use unrelated values in different threads (for example, -/// different threads may use different TCReplica values concurrently). -/// -/// ## Pass by Pointer -/// -/// Several types such as TCReplica and TCString are "opaque" types and always handled as pointers -/// in C. The bytes these pointers address are private to the Rust implementation and must not be -/// accessed from C. -/// -/// Pass-by-pointer values have exactly one owner, and that owner is responsible for freeing the -/// value (using a `tc_…_free` function), or transferring ownership elsewhere. Except where -/// documented otherwise, when a value is passed to C, ownership passes to C as well. When a value -/// is passed to Rust, ownership stays with the C code. The exception is TCString, ownership of -/// which passes to Rust when it is used as a function argument. -/// -/// The limited circumstances where one value must not outlive another, due to pointer references -/// between them, are documented below. -/// -/// ## Pass by Value -/// -/// Types such as TCUuid and TC…List are passed by value, and contain fields that are accessible -/// from C. C code is free to access the content of these types in a _read_only_ fashion. -/// -/// Pass-by-value values that contain pointers also have exactly one owner, responsible for freeing -/// the value or transferring ownership. The tc_…_free functions for these types will replace the -/// pointers with NULL to guard against use-after-free errors. The interior pointers in such values -/// should never be freed directly (for example, `tc_string_free(tcuda.value)` is an error). -/// -/// TCUuid is a special case, because it does not contain pointers. It can be freely copied and -/// need not be freed. -/// -/// ## Lists -/// -/// Lists are a special kind of pass-by-value type. Each contains `len` and `items`, where `items` -/// is an array of length `len`. Lists, and the values in the `items` array, must be treated as -/// read-only. On return from an API function, a list's ownership is with the C caller, which must -/// eventually free the list. List data must be freed with the `tc_…_list_free` function. It is an -/// error to free any value in the `items` array of a list. -} - -ffizz_header::snippet! { -#[ffizz(name="topmatter", order=1)] -/// ```c -/// #ifndef TASKCHAMPION_H -/// #define TASKCHAMPION_H -/// -/// #include -/// #include -/// #include -/// -/// #ifdef __cplusplus -/// #define EXTERN_C extern "C" -/// #else -/// #define EXTERN_C -/// #endif // __cplusplus -/// ``` -} - -ffizz_header::snippet! { -#[ffizz(name="bottomatter", order=10000)] -/// ```c -/// #endif /* TASKCHAMPION_H */ -/// ``` -} - -mod traits; -mod util; - -pub mod annotation; -pub use annotation::*; -pub mod atomic; -pub mod kv; -pub use kv::*; -pub mod replica; -pub use replica::*; -pub mod result; -pub use result::*; -pub mod server; -pub use server::*; -pub mod status; -pub use status::*; -pub mod string; -pub use string::*; -pub mod task; -pub use task::*; -pub mod uda; -pub use uda::*; -pub mod uuid; -pub use uuid::*; -pub mod workingset; -pub use workingset::*; - -pub(crate) mod types { - pub(crate) use crate::annotation::{TCAnnotation, TCAnnotationList}; - pub(crate) use crate::kv::TCKVList; - pub(crate) use crate::replica::TCReplica; - pub(crate) use crate::result::TCResult; - pub(crate) use crate::server::TCServer; - pub(crate) use crate::status::TCStatus; - pub(crate) use crate::string::{RustString, TCString, TCStringList}; - pub(crate) use crate::task::{TCTask, TCTaskList}; - pub(crate) use crate::uda::{TCUda, TCUdaList, Uda}; - pub(crate) use crate::uuid::{TCUuid, TCUuidList}; - pub(crate) use crate::workingset::TCWorkingSet; -} - -#[cfg(debug_assertions)] -/// Generate the taskchapion.h header -pub fn generate_header() -> String { - ffizz_header::generate() -} diff --git a/src/tc/lib/src/replica.rs b/src/tc/lib/src/replica.rs deleted file mode 100644 index 340ef878b..000000000 --- a/src/tc/lib/src/replica.rs +++ /dev/null @@ -1,958 +0,0 @@ -use crate::traits::*; -use crate::types::*; -use crate::util::err_to_ruststring; -use std::ptr::NonNull; -use taskchampion::storage::ReplicaOp; -use taskchampion::{Replica, StorageConfig}; - -#[ffizz_header::item] -#[ffizz(order = 900)] -/// ***** TCReplica ***** -/// -/// A replica represents an instance of a user's task data, providing an easy interface -/// for querying and modifying that data. -/// -/// # Error Handling -/// -/// When a `tc_replica_..` function that returns a TCResult returns TC_RESULT_ERROR, then -/// `tc_replica_error` will return the error message. -/// -/// # Safety -/// -/// The `*TCReplica` returned from `tc_replica_new…` functions is owned by the caller and -/// must later be freed to avoid a memory leak. -/// -/// Any function taking a `*TCReplica` requires: -/// - the pointer must not be NUL; -/// - the pointer must be one previously returned from a tc_… function; -/// - the memory referenced by the pointer must never be modified by C code; and -/// - except for `tc_replica_free`, ownership of a `*TCReplica` remains with the caller. -/// -/// Once passed to `tc_replica_free`, a `*TCReplica` becomes invalid and must not be used again. -/// -/// TCReplicas are not threadsafe. -/// -/// ```c -/// typedef struct TCReplica TCReplica; -/// ``` -pub struct TCReplica { - /// The wrapped Replica - inner: Replica, - - /// If true, this replica has an outstanding &mut (for a TaskMut) - mut_borrowed: bool, - - /// The error from the most recent operation, if any - error: Option>, -} - -impl PassByPointer for TCReplica {} - -impl TCReplica { - /// Mutably borrow the inner Replica - pub(crate) fn borrow_mut(&mut self) -> &mut Replica { - if self.mut_borrowed { - panic!("replica is already borrowed"); - } - self.mut_borrowed = true; - &mut self.inner - } - - /// Release the borrow made by [`borrow_mut`] - pub(crate) fn release_borrow(&mut self) { - if !self.mut_borrowed { - panic!("replica is not borrowed"); - } - self.mut_borrowed = false; - } -} - -impl From for TCReplica { - fn from(rep: Replica) -> TCReplica { - TCReplica { - inner: rep, - mut_borrowed: false, - error: None, - } - } -} - -/// Utility function to allow using `?` notation to return an error value. This makes -/// a mutable borrow, because most Replica methods require a `&mut`. -fn wrap(rep: *mut TCReplica, f: F, err_value: T) -> T -where - F: FnOnce(&mut Replica) -> anyhow::Result, -{ - debug_assert!(!rep.is_null()); - // SAFETY: - // - rep is not NULL (promised by caller) - // - *rep is a valid TCReplica (promised by caller) - // - rep is valid for the duration of this function - // - rep is not modified by anything else (not threadsafe) - let rep: &mut TCReplica = unsafe { TCReplica::from_ptr_arg_ref_mut(rep) }; - if rep.mut_borrowed { - panic!("replica is borrowed and cannot be used"); - } - rep.error = None; - match f(&mut rep.inner) { - Ok(v) => v, - Err(e) => { - rep.error = Some(err_to_ruststring(e)); - err_value - } - } -} - -/// Utility function to allow using `?` notation to return an error value in the constructor. -fn wrap_constructor(f: F, error_out: *mut TCString, err_value: T) -> T -where - F: FnOnce() -> anyhow::Result, -{ - if !error_out.is_null() { - // SAFETY: - // - error_out is not NULL (just checked) - // - properly aligned and valid (promised by caller) - unsafe { *error_out = TCString::default() }; - } - - match f() { - Ok(v) => v, - Err(e) => { - if !error_out.is_null() { - // SAFETY: - // - error_out is not NULL (just checked) - // - properly aligned and valid (promised by caller) - unsafe { - TCString::val_to_arg_out(err_to_ruststring(e), error_out); - } - } - err_value - } - } -} - -#[ffizz_header::item] -#[ffizz(order = 900)] -/// ***** TCReplicaOpType ***** -/// -/// ```c -/// enum TCReplicaOpType -/// #ifdef __cplusplus -/// : uint32_t -/// #endif // __cplusplus -/// { -/// Create = 0, -/// Delete = 1, -/// Update = 2, -/// UndoPoint = 3, -/// }; -/// #ifndef __cplusplus -/// typedef uint32_t TCReplicaOpType; -/// #endif // __cplusplus -/// ``` -#[derive(Debug, Default)] -#[repr(u32)] -pub enum TCReplicaOpType { - Create = 0, - Delete = 1, - Update = 2, - UndoPoint = 3, - #[default] - Error = 4, -} - -#[ffizz_header::item] -#[ffizz(order = 901)] -/// Create a new TCReplica with an in-memory database. The contents of the database will be -/// lost when it is freed with tc_replica_free. -/// -/// ```c -/// EXTERN_C struct TCReplica *tc_replica_new_in_memory(void); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_new_in_memory() -> *mut TCReplica { - let storage = StorageConfig::InMemory - .into_storage() - .expect("in-memory always succeeds"); - // SAFETY: - // - caller promises to free this value - unsafe { TCReplica::from(Replica::new(storage)).return_ptr() } -} - -#[ffizz_header::item] -#[ffizz(order = 901)] -/// Create a new TCReplica with an on-disk database having the given filename. On error, a string -/// is written to the error_out parameter (if it is not NULL) and NULL is returned. The caller -/// must free this string. -/// -/// ```c -/// EXTERN_C struct TCReplica *tc_replica_new_on_disk(struct TCString path, -/// bool create_if_missing, -/// struct TCString *error_out); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_new_on_disk( - path: TCString, - create_if_missing: bool, - error_out: *mut TCString, -) -> *mut TCReplica { - wrap_constructor( - || { - // SAFETY: - // - path is valid (promised by caller) - // - caller will not use path after this call (convention) - let mut path = unsafe { TCString::val_from_arg(path) }; - let storage = StorageConfig::OnDisk { - taskdb_dir: path.to_path_buf_mut()?, - create_if_missing, - } - .into_storage()?; - - // SAFETY: - // - caller promises to free this value - Ok(unsafe { TCReplica::from(Replica::new(storage)).return_ptr() }) - }, - error_out, - std::ptr::null_mut(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 901)] -/// ***** TCReplicaOp ***** -/// -/// ```c -/// struct TCReplicaOp { -/// TCReplicaOpType operation_type; -/// void* inner; -/// }; -/// -/// typedef struct TCReplicaOp TCReplicaOp; -/// ``` -#[derive(Debug)] -#[repr(C)] -pub struct TCReplicaOp { - operation_type: TCReplicaOpType, - inner: Box, -} - -impl From for TCReplicaOp { - fn from(replica_op: ReplicaOp) -> TCReplicaOp { - match replica_op { - ReplicaOp::Create { .. } => TCReplicaOp { - operation_type: TCReplicaOpType::Create, - inner: Box::new(replica_op), - }, - ReplicaOp::Delete { .. } => TCReplicaOp { - operation_type: TCReplicaOpType::Delete, - inner: Box::new(replica_op), - }, - ReplicaOp::Update { .. } => TCReplicaOp { - operation_type: TCReplicaOpType::Update, - inner: Box::new(replica_op), - }, - ReplicaOp::UndoPoint => TCReplicaOp { - operation_type: TCReplicaOpType::UndoPoint, - inner: Box::new(replica_op), - }, - } - } -} - -#[ffizz_header::item] -#[ffizz(order = 901)] -/// ***** TCReplicaOpList ***** -/// -/// ```c -/// struct TCReplicaOpList { -/// struct TCReplicaOp *items; -/// size_t len; -/// size_t capacity; -/// }; -/// -/// typedef struct TCReplicaOpList TCReplicaOpList; -/// ``` -#[repr(C)] -#[derive(Debug)] -pub struct TCReplicaOpList { - items: *mut TCReplicaOp, - len: usize, - capacity: usize, -} - -impl Default for TCReplicaOpList { - fn default() -> Self { - // SAFETY: - // - caller will free this value - unsafe { TCReplicaOpList::return_val(Vec::new()) } - } -} - -impl CList for TCReplicaOpList { - type Element = TCReplicaOp; - - unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { - TCReplicaOpList { - len, - capacity: cap, - items, - } - } - - fn slice(&mut self) -> &mut [Self::Element] { - // SAFETY: - // - because we have &mut self, we have read/write access to items[0..len] - // - all items are properly initialized Element's - // - return value lifetime is equal to &mmut self's, so access is exclusive - // - items and len came from Vec, so total size is < isize::MAX - unsafe { std::slice::from_raw_parts_mut(self.items, self.len) } - } - - fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { - (self.items, self.len, self.capacity) - } -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Get a list of all tasks in the replica. -/// -/// Returns a TCTaskList with a NULL items field on error. -/// -/// ```c -/// EXTERN_C struct TCTaskList tc_replica_all_tasks(struct TCReplica *rep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_all_tasks(rep: *mut TCReplica) -> TCTaskList { - wrap( - rep, - |rep| { - // note that the Replica API returns a hashmap here, but we discard - // the keys and return a simple list. The task UUIDs are available - // from task.get_uuid(), so information is not lost. - let tasks: Vec<_> = rep - .all_tasks()? - .drain() - .map(|(_uuid, t)| { - Some( - NonNull::new( - // SAFETY: - // - caller promises to free this value (via freeing the list) - unsafe { TCTask::from(t).return_ptr() }, - ) - .expect("TCTask::return_ptr returned NULL"), - ) - }) - .collect(); - // SAFETY: - // - value is not allocated and need not be freed - Ok(unsafe { TCTaskList::return_val(tasks) }) - }, - TCTaskList::null_value(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Get a list of all uuids for tasks in the replica. -/// -/// Returns a TCUuidList with a NULL items field on error. -/// -/// The caller must free the UUID list with `tc_uuid_list_free`. -/// -/// ```c -/// EXTERN_C struct TCUuidList tc_replica_all_task_uuids(struct TCReplica *rep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_all_task_uuids(rep: *mut TCReplica) -> TCUuidList { - wrap( - rep, - |rep| { - let uuids: Vec<_> = rep - .all_task_uuids()? - .drain(..) - // SAFETY: - // - value is not allocated and need not be freed - .map(|uuid| unsafe { TCUuid::return_val(uuid) }) - .collect(); - // SAFETY: - // - value will be freed (promised by caller) - Ok(unsafe { TCUuidList::return_val(uuids) }) - }, - TCUuidList::null_value(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Get the current working set for this replica. The resulting value must be freed -/// with tc_working_set_free. -/// -/// Returns NULL on error. -/// -/// ```c -/// EXTERN_C struct TCWorkingSet *tc_replica_working_set(struct TCReplica *rep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_working_set(rep: *mut TCReplica) -> *mut TCWorkingSet { - wrap( - rep, - |rep| { - let ws = rep.working_set()?; - // SAFETY: - // - caller promises to free this value - Ok(unsafe { TCWorkingSet::return_ptr(ws.into()) }) - }, - std::ptr::null_mut(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Get an existing task by its UUID. -/// -/// Returns NULL when the task does not exist, and on error. Consult tc_replica_error -/// to distinguish the two conditions. -/// -/// ```c -/// EXTERN_C struct TCTask *tc_replica_get_task(struct TCReplica *rep, struct TCUuid tcuuid); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_get_task(rep: *mut TCReplica, tcuuid: TCUuid) -> *mut TCTask { - wrap( - rep, - |rep| { - // SAFETY: - // - tcuuid is a valid TCUuid (all bytes are valid) - // - tcuuid is Copy so ownership doesn't matter - let uuid = unsafe { TCUuid::val_from_arg(tcuuid) }; - if let Some(task) = rep.get_task(uuid)? { - // SAFETY: - // - caller promises to free this task - Ok(unsafe { TCTask::from(task).return_ptr() }) - } else { - Ok(std::ptr::null_mut()) - } - }, - std::ptr::null_mut(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Create a new task. The task must not already exist. -/// -/// Returns the task, or NULL on error. -/// -/// ```c -/// EXTERN_C struct TCTask *tc_replica_new_task(struct TCReplica *rep, -/// enum TCStatus status, -/// struct TCString description); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_new_task( - rep: *mut TCReplica, - status: TCStatus, - description: TCString, -) -> *mut TCTask { - // SAFETY: - // - description is valid (promised by caller) - // - caller will not use description after this call (convention) - let mut description = unsafe { TCString::val_from_arg(description) }; - wrap( - rep, - |rep| { - let task = rep.new_task(status.into(), description.as_str()?.to_string())?; - // SAFETY: - // - caller promises to free this task - Ok(unsafe { TCTask::from(task).return_ptr() }) - }, - std::ptr::null_mut(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Create a new task. The task must not already exist. -/// -/// Returns the task, or NULL on error. -/// -/// ```c -/// EXTERN_C struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_import_task_with_uuid( - rep: *mut TCReplica, - tcuuid: TCUuid, -) -> *mut TCTask { - wrap( - rep, - |rep| { - // SAFETY: - // - tcuuid is a valid TCUuid (all bytes are valid) - // - tcuuid is Copy so ownership doesn't matter - let uuid = unsafe { TCUuid::val_from_arg(tcuuid) }; - let task = rep.import_task_with_uuid(uuid)?; - // SAFETY: - // - caller promises to free this task - Ok(unsafe { TCTask::from(task).return_ptr() }) - }, - std::ptr::null_mut(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Delete a task. The task must exist. Note that this is different from setting status to -/// Deleted; this is the final purge of the task. -/// -/// Deletion may interact poorly with modifications to the same task on other replicas. For -/// example, if a task is deleted on replica 1 and its description modified on replica 2, then -/// after both replicas have fully synced, the resulting task will only have a `description` -/// property. -/// -/// ```c -/// EXTERN_C TCResult tc_replica_delete_task(struct TCReplica *rep, struct TCUuid tcuuid); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_delete_task(rep: *mut TCReplica, tcuuid: TCUuid) -> TCResult { - wrap( - rep, - |rep| { - // SAFETY: - // - tcuuid is a valid TCUuid (all bytes are valid) - // - tcuuid is Copy so ownership doesn't matter - let uuid = unsafe { TCUuid::val_from_arg(tcuuid) }; - rep.delete_task(uuid)?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Synchronize this replica with a server. -/// -/// The `server` argument remains owned by the caller, and must be freed explicitly. -/// -/// ```c -/// EXTERN_C TCResult tc_replica_sync(struct TCReplica *rep, struct TCServer *server, bool avoid_snapshots); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_sync( - rep: *mut TCReplica, - server: *mut TCServer, - avoid_snapshots: bool, -) -> TCResult { - wrap( - rep, - |rep| { - debug_assert!(!server.is_null()); - // SAFETY: - // - server is not NULL - // - *server is a valid TCServer (promised by caller) - // - server is valid for the lifetime of tc_replica_sync (not threadsafe) - // - server will not be accessed simultaneously (not threadsafe) - let server = unsafe { TCServer::from_ptr_arg_ref_mut(server) }; - rep.sync(server.as_mut(), avoid_snapshots)?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Expire old, deleted tasks. -/// -/// Expiration entails removal of tasks from the replica. Any modifications that occur after -/// the deletion (such as operations synchronized from other replicas) will do nothing. -/// -/// Tasks are eligible for expiration when they have status Deleted and have not been modified -/// for 180 days (about six months). Note that completed tasks are not eligible. -/// -/// ```c -/// EXTERN_C TCResult tc_replica_expire_tasks(struct TCReplica *rep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_expire_tasks(rep: *mut TCReplica) -> TCResult { - wrap( - rep, - |rep| { - rep.expire_tasks()?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Return undo local operations until the most recent UndoPoint. -/// -/// ```c -/// EXTERN_C TCReplicaOpList tc_replica_get_undo_ops(struct TCReplica *rep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_get_undo_ops(rep: *mut TCReplica) -> TCReplicaOpList { - wrap( - rep, - |rep| { - // SAFETY: - // - caller will free this value, either with tc_replica_commit_undo_ops or - // tc_replica_op_list_free. - Ok(unsafe { - TCReplicaOpList::return_val( - rep.get_undo_ops()? - .into_iter() - .map(TCReplicaOp::from) - .collect(), - ) - }) - }, - TCReplicaOpList::default(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Undo local operations in storage. -/// -/// If undone_out is not NULL, then on success it is set to 1 if operations were undone, or 0 if -/// there are no operations that can be done. -/// -/// ```c -/// EXTERN_C TCResult tc_replica_commit_undo_ops(struct TCReplica *rep, TCReplicaOpList tc_undo_ops, int32_t *undone_out); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_commit_undo_ops( - rep: *mut TCReplica, - tc_undo_ops: TCReplicaOpList, - undone_out: *mut i32, -) -> TCResult { - wrap( - rep, - |rep| { - // SAFETY: - // - `tc_undo_ops` is a valid value, as it was acquired from `tc_replica_get_undo_ops`. - let undo_ops: Vec = unsafe { TCReplicaOpList::val_from_arg(tc_undo_ops) } - .into_iter() - .map(|op| *op.inner) - .collect(); - let undone = i32::from(rep.commit_undo_ops(undo_ops)?); - if !undone_out.is_null() { - // SAFETY: - // - undone_out is not NULL (just checked) - // - undone_out is properly aligned (implicitly promised by caller) - unsafe { *undone_out = undone }; - } - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Get the number of local, un-synchronized operations (not including undo points), or -1 on -/// error. -/// -/// ```c -/// EXTERN_C int64_t tc_replica_num_local_operations(struct TCReplica *rep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_num_local_operations(rep: *mut TCReplica) -> i64 { - wrap( - rep, - |rep| { - let count = rep.num_local_operations()? as i64; - Ok(count) - }, - -1, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Get the number of undo points (number of undo calls possible), or -1 on error. -/// -/// ```c -/// EXTERN_C int64_t tc_replica_num_undo_points(struct TCReplica *rep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_num_undo_points(rep: *mut TCReplica) -> i64 { - wrap( - rep, - |rep| { - let count = rep.num_undo_points()? as i64; - Ok(count) - }, - -1, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Add an UndoPoint, if one has not already been added by this Replica. This occurs automatically -/// when a change is made. The `force` flag allows forcing a new UndoPoint even if one has already -/// been created by this Replica, and may be useful when a Replica instance is held for a long time -/// and used to apply more than one user-visible change. -/// -/// ```c -/// EXTERN_C TCResult tc_replica_add_undo_point(struct TCReplica *rep, bool force); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_add_undo_point(rep: *mut TCReplica, force: bool) -> TCResult { - wrap( - rep, - |rep| { - rep.add_undo_point(force)?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Rebuild this replica's working set, based on whether tasks are pending or not. If `renumber` -/// is true, then existing tasks may be moved to new working-set indices; in any case, on -/// completion all pending tasks are in the working set and all non- pending tasks are not. -/// -/// ```c -/// EXTERN_C TCResult tc_replica_rebuild_working_set(struct TCReplica *rep, bool renumber); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_rebuild_working_set( - rep: *mut TCReplica, - renumber: bool, -) -> TCResult { - wrap( - rep, - |rep| { - rep.rebuild_working_set(renumber)?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 902)] -/// Get the latest error for a replica, or a string with NULL ptr if no error exists. Subsequent -/// calls to this function will return NULL. The rep pointer must not be NULL. The caller must -/// free the returned string. -/// -/// ```c -/// EXTERN_C struct TCString tc_replica_error(struct TCReplica *rep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_error(rep: *mut TCReplica) -> TCString { - // SAFETY: - // - rep is not NULL (promised by caller) - // - *rep is a valid TCReplica (promised by caller) - // - rep is valid for the duration of this function - // - rep is not modified by anything else (not threadsafe) - let rep: &mut TCReplica = unsafe { TCReplica::from_ptr_arg_ref_mut(rep) }; - if let Some(rstring) = rep.error.take() { - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(rstring) } - } else { - TCString::default() - } -} - -#[ffizz_header::item] -#[ffizz(order = 903)] -/// Free a replica. The replica may not be used after this function returns and must not be freed -/// more than once. -/// -/// ```c -/// EXTERN_C void tc_replica_free(struct TCReplica *rep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_free(rep: *mut TCReplica) { - // SAFETY: - // - replica is not NULL (promised by caller) - // - replica is valid (promised by caller) - // - caller will not use description after this call (promised by caller) - let replica = unsafe { TCReplica::take_from_ptr_arg(rep) }; - if replica.mut_borrowed { - panic!("replica is borrowed and cannot be freed"); - } - drop(replica); -} - -#[ffizz_header::item] -#[ffizz(order = 903)] -/// Free a vector of ReplicaOp. The vector may not be used after this function returns and must not be freed -/// more than once. -/// -/// ```c -/// EXTERN_C void tc_replica_op_list_free(struct TCReplicaOpList *oplist); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_op_list_free(oplist: *mut TCReplicaOpList) { - debug_assert!(!oplist.is_null()); - // SAFETY: - // - arg is not NULL (just checked) - // - `*oplist` is valid (guaranteed by caller not double-freeing this value) - unsafe { - TCReplicaOpList::take_val_from_arg( - oplist, - // SAFETY: - // - value is empty, so the caller need not free it. - TCReplicaOpList::return_val(Vec::new()), - ) - }; -} - -#[ffizz_header::item] -#[ffizz(order = 903)] -/// Return uuid field of ReplicaOp. -/// -/// ```c -/// EXTERN_C struct TCString tc_replica_op_get_uuid(struct TCReplicaOp *op); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_op_get_uuid(op: *const TCReplicaOp) -> TCString { - // SAFETY: - // - inner is not null - // - inner is a living object - let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() }; - - if let ReplicaOp::Create { uuid } - | ReplicaOp::Delete { uuid, .. } - | ReplicaOp::Update { uuid, .. } = rop - { - let uuid_rstr: RustString = uuid.to_string().into(); - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(uuid_rstr) } - } else { - panic!("Operation has no uuid: {:#?}", rop); - } -} - -#[ffizz_header::item] -#[ffizz(order = 903)] -/// Return property field of ReplicaOp. -/// -/// ```c -/// EXTERN_C struct TCString tc_replica_op_get_property(struct TCReplicaOp *op); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_op_get_property(op: *const TCReplicaOp) -> TCString { - // SAFETY: - // - inner is not null - // - inner is a living object - let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() }; - - if let ReplicaOp::Update { property, .. } = rop { - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(property.clone().into()) } - } else { - panic!("Operation has no property: {:#?}", rop); - } -} - -#[ffizz_header::item] -#[ffizz(order = 903)] -/// Return value field of ReplicaOp. -/// -/// ```c -/// EXTERN_C struct TCString tc_replica_op_get_value(struct TCReplicaOp *op); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_op_get_value(op: *const TCReplicaOp) -> TCString { - // SAFETY: - // - inner is not null - // - inner is a living object - let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() }; - - if let ReplicaOp::Update { value, .. } = rop { - let value_rstr: RustString = value.clone().unwrap_or(String::new()).into(); - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(value_rstr) } - } else { - panic!("Operation has no value: {:#?}", rop); - } -} - -#[ffizz_header::item] -#[ffizz(order = 903)] -/// Return old value field of ReplicaOp. -/// -/// ```c -/// EXTERN_C struct TCString tc_replica_op_get_old_value(struct TCReplicaOp *op); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_op_get_old_value(op: *const TCReplicaOp) -> TCString { - // SAFETY: - // - inner is not null - // - inner is a living object - let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() }; - - if let ReplicaOp::Update { old_value, .. } = rop { - let old_value_rstr: RustString = old_value.clone().unwrap_or(String::new()).into(); - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(old_value_rstr) } - } else { - panic!("Operation has no old value: {:#?}", rop); - } -} - -#[ffizz_header::item] -#[ffizz(order = 903)] -/// Return timestamp field of ReplicaOp. -/// -/// ```c -/// EXTERN_C struct TCString tc_replica_op_get_timestamp(struct TCReplicaOp *op); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_op_get_timestamp(op: *const TCReplicaOp) -> TCString { - // SAFETY: - // - inner is not null - // - inner is a living object - let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() }; - - if let ReplicaOp::Update { timestamp, .. } = rop { - let timestamp_rstr: RustString = timestamp.to_string().into(); - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(timestamp_rstr) } - } else { - panic!("Operation has no timestamp: {:#?}", rop); - } -} - -#[ffizz_header::item] -#[ffizz(order = 903)] -/// Return description field of old task field of ReplicaOp. -/// -/// ```c -/// EXTERN_C struct TCString tc_replica_op_get_old_task_description(struct TCReplicaOp *op); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_replica_op_get_old_task_description( - op: *const TCReplicaOp, -) -> TCString { - // SAFETY: - // - inner is not null - // - inner is a living object - let rop: &ReplicaOp = unsafe { (*op).inner.as_ref() }; - - if let ReplicaOp::Delete { old_task, .. } = rop { - let description_rstr: RustString = old_task["description"].clone().into(); - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(description_rstr) } - } else { - panic!("Operation has no timestamp: {:#?}", rop); - } -} diff --git a/src/tc/lib/src/result.rs b/src/tc/lib/src/result.rs deleted file mode 100644 index bb72efb1d..000000000 --- a/src/tc/lib/src/result.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[ffizz_header::item] -#[ffizz(order = 100)] -/// ***** TCResult ***** -/// -/// A result from a TC operation. Typically if this value is TC_RESULT_ERROR, -/// the associated object's `tc_.._error` method will return an error message. -/// -/// ```c -/// enum TCResult -/// #ifdef __cplusplus -/// : int32_t -/// #endif // __cplusplus -/// { -/// TC_RESULT_ERROR = -1, -/// TC_RESULT_OK = 0, -/// }; -/// #ifndef __cplusplus -/// typedef int32_t TCResult; -/// #endif // __cplusplus -/// ``` -#[repr(i32)] -pub enum TCResult { - Error = -1, - Ok = 0, -} diff --git a/src/tc/lib/src/server.rs b/src/tc/lib/src/server.rs deleted file mode 100644 index 4b3ffc3b9..000000000 --- a/src/tc/lib/src/server.rs +++ /dev/null @@ -1,234 +0,0 @@ -use crate::traits::*; -use crate::types::*; -use crate::util::err_to_ruststring; -use taskchampion::{Server, ServerConfig}; - -#[ffizz_header::item] -#[ffizz(order = 800)] -/// ***** TCServer ***** -/// -/// TCServer represents an interface to a sync server. Aside from new and free, a server -/// has no C-accessible API, but is designed to be passed to `tc_replica_sync`. -/// -/// ## Safety -/// -/// TCServer are not threadsafe, and must not be used with multiple replicas simultaneously. -/// -/// ```c -/// typedef struct TCServer TCServer; -/// ``` -pub struct TCServer(Box); - -impl PassByPointer for TCServer {} - -impl From> for TCServer { - fn from(server: Box) -> TCServer { - TCServer(server) - } -} - -impl AsMut> for TCServer { - fn as_mut(&mut self) -> &mut Box { - &mut self.0 - } -} - -/// Utility function to allow using `?` notation to return an error value. -fn wrap(f: F, error_out: *mut TCString, err_value: T) -> T -where - F: FnOnce() -> anyhow::Result, -{ - if !error_out.is_null() { - // SAFETY: - // - error_out is not NULL (just checked) - // - properly aligned and valid (promised by caller) - unsafe { *error_out = TCString::default() }; - } - - match f() { - Ok(v) => v, - Err(e) => { - if !error_out.is_null() { - // SAFETY: - // - error_out is not NULL (just checked) - // - properly aligned and valid (promised by caller) - unsafe { - TCString::val_to_arg_out(err_to_ruststring(e), error_out); - } - } - err_value - } - } -} - -#[ffizz_header::item] -#[ffizz(order = 801)] -/// Create a new TCServer that operates locally (on-disk). See the TaskChampion docs for the -/// description of the arguments. -/// -/// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is -/// returned. The caller must free this string. -/// -/// The server must be freed after it is used - tc_replica_sync does not automatically free it. -/// -/// ```c -/// EXTERN_C struct TCServer *tc_server_new_local(struct TCString server_dir, struct TCString *error_out); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_server_new_local( - server_dir: TCString, - error_out: *mut TCString, -) -> *mut TCServer { - wrap( - || { - // SAFETY: - // - server_dir is valid (promised by caller) - // - caller will not use server_dir after this call (convention) - let mut server_dir = unsafe { TCString::val_from_arg(server_dir) }; - let server_config = ServerConfig::Local { - server_dir: server_dir.to_path_buf_mut()?, - }; - let server = server_config.into_server()?; - // SAFETY: caller promises to free this server. - Ok(unsafe { TCServer::return_ptr(server.into()) }) - }, - error_out, - std::ptr::null_mut(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 801)] -/// Create a new TCServer that connects to a remote server. See the TaskChampion docs for the -/// description of the arguments. -/// -/// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is -/// returned. The caller must free this string. -/// -/// The server must be freed after it is used - tc_replica_sync does not automatically free it. -/// -/// ```c -/// EXTERN_C struct TCServer *tc_server_new_sync(struct TCString url, -/// struct TCUuid client_id, -/// struct TCString encryption_secret, -/// struct TCString *error_out); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_server_new_sync( - url: TCString, - client_id: TCUuid, - encryption_secret: TCString, - error_out: *mut TCString, -) -> *mut TCServer { - wrap( - || { - // SAFETY: - // - url is valid (promised by caller) - // - url ownership is transferred to this function - let url = unsafe { TCString::val_from_arg(url) }.into_string()?; - - // SAFETY: - // - client_id is a valid Uuid (any 8-byte sequence counts) - let client_id = unsafe { TCUuid::val_from_arg(client_id) }; - - // SAFETY: - // - encryption_secret is valid (promised by caller) - // - encryption_secret ownership is transferred to this function - let encryption_secret = unsafe { TCString::val_from_arg(encryption_secret) } - .as_bytes() - .to_vec(); - - let server_config = ServerConfig::Remote { - url, - client_id, - encryption_secret, - }; - let server = server_config.into_server()?; - // SAFETY: caller promises to free this server. - Ok(unsafe { TCServer::return_ptr(server.into()) }) - }, - error_out, - std::ptr::null_mut(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 802)] -/// Create a new TCServer that connects to the Google Cloud Platform. See the TaskChampion docs -/// for the description of the arguments. -/// -/// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is -/// returned. The caller must free this string. -/// -/// The server must be freed after it is used - tc_replica_sync does not automatically free it. -/// -/// ```c -/// EXTERN_C struct TCServer *tc_server_new_gcp(struct TCString bucket, -/// struct TCString credential_path, -/// struct TCString encryption_secret, -/// struct TCString *error_out); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_server_new_gcp( - bucket: TCString, - credential_path_argument: TCString, - encryption_secret: TCString, - error_out: *mut TCString, -) -> *mut TCServer { - wrap( - || { - // SAFETY: - // - bucket is valid (promised by caller) - // - bucket ownership is transferred to this function - let bucket = unsafe { TCString::val_from_arg(bucket) }.into_string()?; - - // SAFETY: - // - credential_path is valid (promised by caller) - // - credential_path ownership is transferred to this function - - let credential_path = - unsafe { TCString::val_from_arg(credential_path_argument) }.into_string()?; - let credential_path = if credential_path.is_empty() { - None - } else { - Some(credential_path) - }; - - // SAFETY: - // - encryption_secret is valid (promised by caller) - // - encryption_secret ownership is transferred to this function - let encryption_secret = unsafe { TCString::val_from_arg(encryption_secret) } - .as_bytes() - .to_vec(); - let server_config = ServerConfig::Gcp { - bucket, - credential_path, - encryption_secret, - }; - let server = server_config.into_server()?; - // SAFETY: caller promises to free this server. - Ok(unsafe { TCServer::return_ptr(server.into()) }) - }, - error_out, - std::ptr::null_mut(), - ) -} - -#[ffizz_header::item] -#[ffizz(order = 899)] -/// Free a server. The server may not be used after this function returns and must not be freed -/// more than once. -/// -/// ```c -/// EXTERN_C void tc_server_free(struct TCServer *server); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_server_free(server: *mut TCServer) { - debug_assert!(!server.is_null()); - // SAFETY: - // - server is not NULL - // - server came from tc_server_new_.., which used return_ptr - // - server will not be used after (promised by caller) - let server = unsafe { TCServer::take_from_ptr_arg(server) }; - drop(server); -} diff --git a/src/tc/lib/src/status.rs b/src/tc/lib/src/status.rs deleted file mode 100644 index df5332401..000000000 --- a/src/tc/lib/src/status.rs +++ /dev/null @@ -1,73 +0,0 @@ -pub use taskchampion::Status; - -#[ffizz_header::item] -#[ffizz(order = 700)] -/// ***** TCStatus ***** -/// -/// The status of a task, as defined by the task data model. -/// -/// ```c -/// #ifdef __cplusplus -/// typedef enum TCStatus : int32_t { -/// #else // __cplusplus -/// typedef int32_t TCStatus; -/// enum TCStatus { -/// #endif // __cplusplus -/// TC_STATUS_PENDING = 0, -/// TC_STATUS_COMPLETED = 1, -/// TC_STATUS_DELETED = 2, -/// TC_STATUS_RECURRING = 3, -/// // Unknown signifies a status in the task DB that was not -/// // recognized. -/// TC_STATUS_UNKNOWN = -1, -/// #ifdef __cplusplus -/// } TCStatus; -/// #else // __cplusplus -/// }; -/// #endif // __cplusplus -/// ``` -#[repr(i32)] -pub enum TCStatus { - Pending = 0, - Completed = 1, - Deleted = 2, - Recurring = 3, - Unknown = -1, -} - -impl From for Status { - fn from(status: TCStatus) -> Status { - match status { - TCStatus::Pending => Status::Pending, - TCStatus::Completed => Status::Completed, - TCStatus::Deleted => Status::Deleted, - TCStatus::Recurring => Status::Recurring, - _ => Status::Unknown(format!("unknown TCStatus {}", status as i32)), - } - } -} - -impl From for TCStatus { - fn from(status: Status) -> TCStatus { - match status { - Status::Pending => TCStatus::Pending, - Status::Completed => TCStatus::Completed, - Status::Deleted => TCStatus::Deleted, - Status::Recurring => TCStatus::Recurring, - Status::Unknown(_) => TCStatus::Unknown, - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn conversion_from_unknown_tc_status_provides_discriminant_in_message() { - let tc_status = TCStatus::Unknown; - let status = Status::from(tc_status); - - assert!(matches!(status, Status::Unknown(msg) if msg == "unknown TCStatus -1")); - } -} diff --git a/src/tc/lib/src/string.rs b/src/tc/lib/src/string.rs deleted file mode 100644 index 01036c999..000000000 --- a/src/tc/lib/src/string.rs +++ /dev/null @@ -1,773 +0,0 @@ -use crate::traits::*; -use crate::util::{string_into_raw_parts, vec_into_raw_parts}; -use std::ffi::{CStr, CString, OsString}; -use std::os::raw::c_char; -use std::path::PathBuf; - -#[ffizz_header::item] -#[ffizz(order = 200)] -/// ***** TCString ***** -/// -/// TCString supports passing strings into and out of the TaskChampion API. -/// -/// # Rust Strings and C Strings -/// -/// A Rust string can contain embedded NUL characters, while C considers such a character to mark -/// the end of a string. Strings containing embedded NULs cannot be represented as a "C string" -/// and must be accessed using `tc_string_content_and_len` and `tc_string_clone_with_len`. In -/// general, these two functions should be used for handling arbitrary data, while more convenient -/// forms may be used where embedded NUL characters are impossible, such as in static strings. -/// -/// # UTF-8 -/// -/// TaskChampion expects all strings to be valid UTF-8. `tc_string_…` functions will fail if given -/// a `*TCString` containing invalid UTF-8. -/// -/// # Safety -/// -/// The `ptr` field may be checked for NULL, where documentation indicates this is possible. All -/// other fields in a TCString are private and must not be used from C. They exist in the struct -/// to ensure proper allocation and alignment. -/// -/// When a `TCString` appears as a return value or output argument, ownership is passed to the -/// caller. The caller must pass that ownership back to another function or free the string. -/// -/// Any function taking a `TCString` requires: -/// - the pointer must not be NUL; -/// - the pointer must be one previously returned from a tc_… function; and -/// - the memory referenced by the pointer must never be modified by C code. -/// -/// Unless specified otherwise, TaskChampion functions take ownership of a `TCString` when it is -/// given as a function argument, and the caller must not use or free TCStrings after passing them -/// to such API functions. -/// -/// A TCString with a NULL `ptr` field need not be freed, although tc_free_string will not fail -/// for such a value. -/// -/// TCString is not threadsafe. -/// -/// ```c -/// typedef struct TCString { -/// void *ptr; // opaque, but may be checked for NULL -/// size_t _u1; // reserved -/// size_t _u2; // reserved -/// uint8_t _u3; // reserved -/// } TCString; -/// ``` -#[repr(C)] -#[derive(Debug)] -pub struct TCString { - // defined based on the type - ptr: *mut libc::c_void, - len: usize, - cap: usize, - - // type of TCString this represents - ty: u8, -} - -// TODO: figure out how to ignore this but still use it in TCString -/// A discriminator for TCString -#[repr(u8)] -enum TCStringType { - /// Null. Nothing is contained in this string. - /// - /// * `ptr` is NULL. - /// * `len` and `cap` are zero. - Null = 0, - - /// A CString. - /// - /// * `ptr` is the result of CString::into_raw, containing a terminating NUL. It may not be - /// valid UTF-8. - /// * `len` and `cap` are zero. - CString, - - /// A CStr, referencing memory borrowed from C - /// - /// * `ptr` points to the string, containing a terminating NUL. It may not be valid UTF-8. - /// * `len` and `cap` are zero. - CStr, - - /// A String. - /// - /// * `ptr`, `len`, and `cap` are as would be returned from String::into_raw_parts. - String, - - /// A byte sequence. - /// - /// * `ptr`, `len`, and `cap` are as would be returned from Vec::into_raw_parts. - Bytes, -} - -impl Default for TCString { - fn default() -> Self { - TCString { - ptr: std::ptr::null_mut(), - len: 0, - cap: 0, - ty: TCStringType::Null as u8, - } - } -} - -impl TCString { - pub(crate) fn is_null(&self) -> bool { - self.ptr.is_null() - } -} - -#[derive(PartialEq, Eq, Debug, Default)] -pub enum RustString<'a> { - #[default] - Null, - CString(CString), - CStr(&'a CStr), - String(String), - Bytes(Vec), -} - -impl PassByValue for TCString { - type RustType = RustString<'static>; - - unsafe fn from_ctype(self) -> Self::RustType { - match self.ty { - ty if ty == TCStringType::CString as u8 => { - // SAFETY: - // - ptr was derived from CString::into_raw - // - data was not modified since that time (caller promises) - RustString::CString(unsafe { CString::from_raw(self.ptr as *mut c_char) }) - } - ty if ty == TCStringType::CStr as u8 => { - // SAFETY: - // - ptr was created by CStr::as_ptr - // - data was not modified since that time (caller promises) - RustString::CStr(unsafe { CStr::from_ptr(self.ptr as *mut c_char) }) - } - ty if ty == TCStringType::String as u8 => { - // SAFETY: - // - ptr was created by string_into_raw_parts - // - data was not modified since that time (caller promises) - RustString::String(unsafe { - String::from_raw_parts(self.ptr as *mut u8, self.len, self.cap) - }) - } - ty if ty == TCStringType::Bytes as u8 => { - // SAFETY: - // - ptr was created by vec_into_raw_parts - // - data was not modified since that time (caller promises) - RustString::Bytes(unsafe { - Vec::from_raw_parts(self.ptr as *mut u8, self.len, self.cap) - }) - } - _ => RustString::Null, - } - } - - fn as_ctype(arg: Self::RustType) -> Self { - match arg { - RustString::Null => Self { - ty: TCStringType::Null as u8, - ..Default::default() - }, - RustString::CString(cstring) => Self { - ty: TCStringType::CString as u8, - ptr: cstring.into_raw() as *mut libc::c_void, - ..Default::default() - }, - RustString::CStr(cstr) => Self { - ty: TCStringType::CStr as u8, - ptr: cstr.as_ptr() as *mut libc::c_void, - ..Default::default() - }, - RustString::String(string) => { - let (ptr, len, cap) = string_into_raw_parts(string); - Self { - ty: TCStringType::String as u8, - ptr: ptr as *mut libc::c_void, - len, - cap, - } - } - RustString::Bytes(bytes) => { - let (ptr, len, cap) = vec_into_raw_parts(bytes); - Self { - ty: TCStringType::Bytes as u8, - ptr: ptr as *mut libc::c_void, - len, - cap, - } - } - } - } -} - -impl<'a> RustString<'a> { - /// Get a regular Rust &str for this value. - pub(crate) fn as_str(&mut self) -> Result<&str, std::str::Utf8Error> { - match self { - RustString::CString(cstring) => cstring.as_c_str().to_str(), - RustString::CStr(cstr) => cstr.to_str(), - RustString::String(ref string) => Ok(string.as_ref()), - RustString::Bytes(_) => { - self.bytes_to_string()?; - self.as_str() // now the String variant, so won't recurse - } - RustString::Null => unreachable!(), - } - } - - /// Consume this RustString and return an equivalent String, or an error if not - /// valid UTF-8. In the error condition, the original data is lost. - pub(crate) fn into_string(mut self) -> Result { - match self { - RustString::CString(cstring) => cstring.into_string().map_err(|e| e.utf8_error()), - RustString::CStr(cstr) => cstr.to_str().map(|s| s.to_string()), - RustString::String(string) => Ok(string), - RustString::Bytes(_) => { - self.bytes_to_string()?; - self.into_string() // now the String variant, so won't recurse - } - RustString::Null => unreachable!(), - } - } - - pub(crate) fn as_bytes(&self) -> &[u8] { - match self { - RustString::CString(cstring) => cstring.as_bytes(), - RustString::CStr(cstr) => cstr.to_bytes(), - RustString::String(string) => string.as_bytes(), - RustString::Bytes(bytes) => bytes.as_ref(), - RustString::Null => unreachable!(), - } - } - - /// Convert the RustString, in place, from the Bytes to String variant. On successful return, - /// the RustString has variant RustString::String. - fn bytes_to_string(&mut self) -> Result<(), std::str::Utf8Error> { - let mut owned = RustString::Null; - // temporarily swap a Null value into self; we'll swap that back - // shortly. - std::mem::swap(self, &mut owned); - match owned { - RustString::Bytes(bytes) => match String::from_utf8(bytes) { - Ok(string) => { - *self = RustString::String(string); - Ok(()) - } - Err(e) => { - let (e, bytes) = (e.utf8_error(), e.into_bytes()); - // put self back as we found it - *self = RustString::Bytes(bytes); - Err(e) - } - }, - _ => { - // not bytes, so just swap back - std::mem::swap(self, &mut owned); - Ok(()) - } - } - } - - /// Convert the RustString, in place, into one of the C variants. If this is not - /// possible, such as if the string contains an embedded NUL, then the string - /// remains unchanged. - fn string_to_cstring(&mut self) { - let mut owned = RustString::Null; - // temporarily swap a Null value into self; we'll swap that back shortly - std::mem::swap(self, &mut owned); - match owned { - RustString::String(string) => { - match CString::new(string) { - Ok(cstring) => { - *self = RustString::CString(cstring); - } - Err(nul_err) => { - // recover the underlying String from the NulError and restore - // the RustString - let original_bytes = nul_err.into_vec(); - // SAFETY: original_bytes came from a String moments ago, so still valid utf8 - let string = unsafe { String::from_utf8_unchecked(original_bytes) }; - *self = RustString::String(string); - } - } - } - _ => { - // not a CString, so just swap back - std::mem::swap(self, &mut owned); - } - } - } - - pub(crate) fn to_path_buf_mut(&mut self) -> Result { - #[cfg(unix)] - let path: OsString = { - // on UNIX, we can use the bytes directly, without requiring that they - // be valid UTF-8. - use std::ffi::OsStr; - use std::os::unix::ffi::OsStrExt; - OsStr::from_bytes(self.as_bytes()).to_os_string() - }; - #[cfg(windows)] - let path: OsString = { - // on Windows, we assume the filename is valid Unicode, so it can be - // represented as UTF-8. - OsString::from(self.as_str()?.to_string()) - }; - Ok(path.into()) - } -} - -impl<'a> From for RustString<'a> { - fn from(string: String) -> RustString<'a> { - RustString::String(string) - } -} - -impl From<&str> for RustString<'static> { - fn from(string: &str) -> RustString<'static> { - RustString::String(string.to_string()) - } -} - -/// Utility function to borrow a TCString from a pointer arg, modify it, -/// and restore it. -/// -/// This implements a kind of "interior mutability", relying on the -/// single-threaded use of all TC* types. -/// -/// # SAFETY -/// -/// - tcstring must not be NULL -/// - *tcstring must be a valid TCString -/// - *tcstring must not be accessed by anything else, despite the *const -unsafe fn wrap(tcstring: *const TCString, f: F) -> T -where - F: FnOnce(&mut RustString) -> T, -{ - debug_assert!(!tcstring.is_null()); - - // SAFETY: - // - we have exclusive to *tcstring (promised by caller) - let tcstring = tcstring as *mut TCString; - - // SAFETY: - // - tcstring is not NULL - // - *tcstring is a valid string (promised by caller) - let mut rstring = unsafe { TCString::take_val_from_arg(tcstring, TCString::default()) }; - - let rv = f(&mut rstring); - - // update the caller's TCString with the updated RustString - // SAFETY: - // - tcstring is not NULL (we just took from it) - // - tcstring points to valid memory (we just took from it) - unsafe { TCString::val_to_arg_out(rstring, tcstring) }; - - rv -} - -#[ffizz_header::item] -#[ffizz(order = 210)] -/// ***** TCStringList ***** -/// -/// TCStringList represents a list of strings. -/// -/// The content of this struct must be treated as read-only. -/// -/// ```c -/// typedef struct TCStringList { -/// // number of strings in items -/// size_t len; -/// // reserved -/// size_t _u1; -/// // TCStringList representing each string. These remain owned by the TCStringList instance and will -/// // be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the -/// // *TCStringList at indexes 0..len-1 are not NULL. -/// struct TCString *items; -/// } TCStringList; -/// ``` -#[repr(C)] -pub struct TCStringList { - len: libc::size_t, - /// total size of items (internal use only) - capacity: libc::size_t, - items: *mut TCString, -} - -impl CList for TCStringList { - type Element = TCString; - - unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { - TCStringList { - len, - capacity: cap, - items, - } - } - - fn slice(&mut self) -> &mut [Self::Element] { - // SAFETY: - // - because we have &mut self, we have read/write access to items[0..len] - // - all items are properly initialized Element's - // - return value lifetime is equal to &mmut self's, so access is exclusive - // - items and len came from Vec, so total size is < isize::MAX - unsafe { std::slice::from_raw_parts_mut(self.items, self.len) } - } - - fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { - (self.items, self.len, self.capacity) - } -} - -#[ffizz_header::item] -#[ffizz(order = 201)] -/// Create a new TCString referencing the given C string. The C string must remain valid and -/// unchanged until after the TCString is freed. It's typically easiest to ensure this by using a -/// static string. -/// -/// NOTE: this function does _not_ take responsibility for freeing the given C string. The -/// given string can be freed once the TCString referencing it has been freed. -/// -/// For example: -/// -/// ```text -/// char *url = get_item_url(..); // dynamically allocate C string -/// tc_task_annotate(task, tc_string_borrow(url)); // TCString created, passed, and freed -/// free(url); // string is no longer referenced and can be freed -/// ``` -/// -/// ```c -/// EXTERN_C struct TCString tc_string_borrow(const char *cstr); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_string_borrow(cstr: *const libc::c_char) -> TCString { - debug_assert!(!cstr.is_null()); - // SAFETY: - // - cstr is not NULL (promised by caller, verified by assertion) - // - cstr's lifetime exceeds that of the TCString (promised by caller) - // - cstr contains a valid NUL terminator (promised by caller) - // - cstr's content will not change before it is destroyed (promised by caller) - let cstr: &CStr = unsafe { CStr::from_ptr(cstr) }; - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(RustString::CStr(cstr)) } -} - -#[ffizz_header::item] -#[ffizz(order = 201)] -/// Create a new TCString by cloning the content of the given C string. The resulting TCString -/// is independent of the given string, which can be freed or overwritten immediately. -/// -/// ```c -/// EXTERN_C struct TCString tc_string_clone(const char *cstr); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_string_clone(cstr: *const libc::c_char) -> TCString { - debug_assert!(!cstr.is_null()); - // SAFETY: - // - cstr is not NULL (promised by caller, verified by assertion) - // - cstr's lifetime exceeds that of this function (by C convention) - // - cstr contains a valid NUL terminator (promised by caller) - // - cstr's content will not change before it is destroyed (by C convention) - let cstr: &CStr = unsafe { CStr::from_ptr(cstr) }; - let cstring: CString = cstr.into(); - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(RustString::CString(cstring)) } -} - -#[ffizz_header::item] -#[ffizz(order = 201)] -/// Create a new TCString containing the given string with the given length. This allows creation -/// of strings containing embedded NUL characters. As with `tc_string_clone`, the resulting -/// TCString is independent of the passed buffer, which may be reused or freed immediately. -/// -/// The length should _not_ include any trailing NUL. -/// -/// The given length must be less than half the maximum value of usize. -/// -/// ```c -/// EXTERN_C struct TCString tc_string_clone_with_len(const char *buf, size_t len); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_string_clone_with_len( - buf: *const libc::c_char, - len: usize, -) -> TCString { - debug_assert!(!buf.is_null()); - debug_assert!(len < isize::MAX as usize); - // SAFETY: - // - buf is valid for len bytes (by C convention) - // - (no alignment requirements for a byte slice) - // - content of buf will not be mutated during the lifetime of this slice (lifetime - // does not outlive this function call) - // - the length of the buffer is less than isize::MAX (promised by caller) - let slice = unsafe { std::slice::from_raw_parts(buf as *const u8, len) }; - - // allocate and copy into Rust-controlled memory - let vec = slice.to_vec(); - - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(RustString::Bytes(vec)) } -} - -#[ffizz_header::item] -#[ffizz(order = 201)] -/// Get the content of the string as a regular C string. The given string must be valid. The -/// returned value is NULL if the string contains NUL bytes or (in some cases) invalid UTF-8. The -/// returned C string is valid until the TCString is freed or passed to another TC API function. -/// -/// In general, prefer [`tc_string_content_with_len`] except when it's certain that the string is -/// valid and NUL-free. -/// -/// This function takes the TCString by pointer because it may be modified in-place to add a NUL -/// terminator. The pointer must not be NULL. -/// -/// This function does _not_ take ownership of the TCString. -/// -/// ```c -/// EXTERN_C const char *tc_string_content(const struct TCString *tcstring); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_string_content(tcstring: *const TCString) -> *const libc::c_char { - // SAFETY; - // - tcstring is not NULL (promised by caller) - // - *tcstring is valid (promised by caller) - // - *tcstring is not accessed concurrently (single-threaded) - unsafe { - wrap(tcstring, |rstring| { - // try to eliminate the Bytes variant. If this fails, we'll return NULL - // below, so the error is ignorable. - let _ = rstring.bytes_to_string(); - - // and eliminate the String variant - rstring.string_to_cstring(); - - match &rstring { - RustString::CString(cstring) => cstring.as_ptr(), - RustString::String(_) => std::ptr::null(), // string_to_cstring failed - RustString::CStr(cstr) => cstr.as_ptr(), - RustString::Bytes(_) => std::ptr::null(), // already returned above - RustString::Null => unreachable!(), - } - }) - } -} - -#[ffizz_header::item] -#[ffizz(order = 201)] -/// Get the content of the string as a pointer and length. The given string must not be NULL. -/// This function can return any string, even one including NUL bytes or invalid UTF-8. The -/// returned buffer is valid until the TCString is freed or passed to another TaskChampio -/// function. -/// -/// This function takes the TCString by pointer because it may be modified in-place to add a NUL -/// terminator. The pointer must not be NULL. -/// -/// This function does _not_ take ownership of the TCString. -/// -/// ```c -/// EXTERN_C const char *tc_string_content_with_len(const struct TCString *tcstring, size_t *len_out); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_string_content_with_len( - tcstring: *const TCString, - len_out: *mut usize, -) -> *const libc::c_char { - // SAFETY; - // - tcstring is not NULL (promised by caller) - // - *tcstring is valid (promised by caller) - // - *tcstring is not accessed concurrently (single-threaded) - unsafe { - wrap(tcstring, |rstring| { - let bytes = rstring.as_bytes(); - - // SAFETY: - // - len_out is not NULL (promised by caller) - // - len_out points to valid memory (promised by caller) - // - len_out is properly aligned (C convention) - usize::val_to_arg_out(bytes.len(), len_out); - bytes.as_ptr() as *const libc::c_char - }) - } -} - -#[ffizz_header::item] -#[ffizz(order = 201)] -/// Free a TCString. The given string must not be NULL. The string must not be used -/// after this function returns, and must not be freed more than once. -/// -/// ```c -/// EXTERN_C void tc_string_free(struct TCString *tcstring); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_string_free(tcstring: *mut TCString) { - // SAFETY: - // - tcstring is not NULL (promised by caller) - // - caller is exclusive owner of tcstring (promised by caller) - drop(unsafe { TCString::take_val_from_arg(tcstring, TCString::default()) }); -} - -#[ffizz_header::item] -#[ffizz(order = 211)] -/// Free a TCStringList instance. The instance, and all TCStringList it contains, must not be used after -/// this call. -/// -/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCStringList. -/// -/// ```c -/// EXTERN_C void tc_string_list_free(struct TCStringList *tcstrings); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_string_list_free(tcstrings: *mut TCStringList) { - // SAFETY: - // - tcstrings is not NULL and points to a valid TCStringList (caller is not allowed to - // modify the list) - // - caller promises not to use the value after return - unsafe { drop_value_list(tcstrings) }; -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn empty_list_has_non_null_pointer() { - let tcstrings = unsafe { TCStringList::return_val(Vec::new()) }; - assert!(!tcstrings.items.is_null()); - assert_eq!(tcstrings.len, 0); - assert_eq!(tcstrings.capacity, 0); - } - - #[test] - fn free_sets_null_pointer() { - let mut tcstrings = unsafe { TCStringList::return_val(Vec::new()) }; - // SAFETY: testing expected behavior - unsafe { tc_string_list_free(&mut tcstrings) }; - assert!(tcstrings.items.is_null()); - assert_eq!(tcstrings.len, 0); - assert_eq!(tcstrings.capacity, 0); - } - - const INVALID_UTF8: &[u8] = b"abc\xf0\x28\x8c\x28"; - - fn make_cstring() -> RustString<'static> { - RustString::CString(CString::new("a string").unwrap()) - } - - fn make_cstr() -> RustString<'static> { - let cstr = CStr::from_bytes_with_nul(b"a string\0").unwrap(); - RustString::CStr(cstr) - } - - fn make_string() -> RustString<'static> { - RustString::String("a string".into()) - } - - fn make_string_with_nul() -> RustString<'static> { - RustString::String("a \0 nul!".into()) - } - - fn make_invalid_bytes() -> RustString<'static> { - RustString::Bytes(INVALID_UTF8.to_vec()) - } - - fn make_bytes() -> RustString<'static> { - RustString::Bytes(b"bytes".to_vec()) - } - - #[test] - fn cstring_as_str() { - assert_eq!(make_cstring().as_str().unwrap(), "a string"); - } - - #[test] - fn cstr_as_str() { - assert_eq!(make_cstr().as_str().unwrap(), "a string"); - } - - #[test] - fn string_as_str() { - assert_eq!(make_string().as_str().unwrap(), "a string"); - } - - #[test] - fn string_with_nul_as_str() { - assert_eq!(make_string_with_nul().as_str().unwrap(), "a \0 nul!"); - } - - #[test] - fn invalid_bytes_as_str() { - let as_str_err = make_invalid_bytes().as_str().unwrap_err(); - assert_eq!(as_str_err.valid_up_to(), 3); // "abc" is valid - } - - #[test] - fn valid_bytes_as_str() { - assert_eq!(make_bytes().as_str().unwrap(), "bytes"); - } - - #[test] - fn cstring_as_bytes() { - assert_eq!(make_cstring().as_bytes(), b"a string"); - } - - #[test] - fn cstr_as_bytes() { - assert_eq!(make_cstr().as_bytes(), b"a string"); - } - - #[test] - fn string_as_bytes() { - assert_eq!(make_string().as_bytes(), b"a string"); - } - - #[test] - fn string_with_nul_as_bytes() { - assert_eq!(make_string_with_nul().as_bytes(), b"a \0 nul!"); - } - - #[test] - fn invalid_bytes_as_bytes() { - assert_eq!(make_invalid_bytes().as_bytes(), INVALID_UTF8); - } - - #[test] - fn cstring_string_to_cstring() { - let mut tcstring = make_cstring(); - tcstring.string_to_cstring(); - assert_eq!(tcstring, make_cstring()); // unchanged - } - - #[test] - fn cstr_string_to_cstring() { - let mut tcstring = make_cstr(); - tcstring.string_to_cstring(); - assert_eq!(tcstring, make_cstr()); // unchanged - } - - #[test] - fn string_string_to_cstring() { - let mut tcstring = make_string(); - tcstring.string_to_cstring(); - assert_eq!(tcstring, make_cstring()); // converted to CString, same content - } - - #[test] - fn string_with_nul_string_to_cstring() { - let mut tcstring = make_string_with_nul(); - tcstring.string_to_cstring(); - assert_eq!(tcstring, make_string_with_nul()); // unchanged - } - - #[test] - fn bytes_string_to_cstring() { - let mut tcstring = make_bytes(); - tcstring.string_to_cstring(); - assert_eq!(tcstring, make_bytes()); // unchanged - } -} diff --git a/src/tc/lib/src/task.rs b/src/tc/lib/src/task.rs deleted file mode 100644 index a5d2e80de..000000000 --- a/src/tc/lib/src/task.rs +++ /dev/null @@ -1,1304 +0,0 @@ -use crate::traits::*; -use crate::types::*; -use crate::util::err_to_ruststring; -use crate::TCKV; -use std::convert::TryFrom; -use std::ops::Deref; -use std::ptr::NonNull; -use std::str::FromStr; -use taskchampion::{utc_timestamp, Annotation, Tag, Task, TaskMut, Uuid}; - -#[ffizz_header::item] -#[ffizz(order = 1000)] -/// ***** TCTask ***** -/// -/// A task, as publicly exposed by this library. -/// -/// A task begins in "immutable" mode. It must be converted to "mutable" mode -/// to make any changes, and doing so requires exclusive access to the replica -/// until the task is freed or converted back to immutable mode. -/// -/// An immutable task carries no reference to the replica that created it, and can be used until it -/// is freed or converted to a TaskMut. A mutable task carries a reference to the replica and -/// must be freed or made immutable before the replica is freed. -/// -/// All `tc_task_..` functions taking a task as an argument require that it not be NULL. -/// -/// When a `tc_task_..` function that returns a TCResult returns TC_RESULT_ERROR, then -/// `tc_task_error` will return the error message. -/// -/// # Safety -/// -/// A task is an owned object, and must be freed with tc_task_free (or, if part of a list, -/// with tc_task_list_free). -/// -/// Any function taking a `*TCTask` requires: -/// - the pointer must not be NUL; -/// - the pointer must be one previously returned from a tc_… function; -/// - the memory referenced by the pointer must never be modified by C code; and -/// - except for `tc_{task,task_list}_free`, ownership of a `*TCTask` remains with the caller. -/// -/// Once passed to tc_task_free, a `*TCTask` becomes invalid and must not be used again. -/// -/// TCTasks are not threadsafe. -/// -/// ```c -/// typedef struct TCTask TCTask; -/// ``` -pub struct TCTask { - /// The wrapped Task or TaskMut - inner: Inner, - - /// The error from the most recent operation, if any - error: Option>, -} - -enum Inner { - /// A regular, immutable task - Immutable(Task), - - /// A mutable task, together with the replica to which it holds an exclusive - /// reference. - Mutable(TaskMut<'static>, *mut TCReplica), - - /// A transitional state for a TCTask as it goes from mutable to immutable and back. A task - /// can only be in this state outside of [`to_mut`] and [`to_immut`] if a panic occurs during - /// one of those methods. - Invalid, -} - -impl PassByPointer for TCTask {} - -impl TCTask { - /// Make an immutable TCTask into a mutable TCTask. Does nothing if the task - /// is already mutable. - /// - /// # Safety - /// - /// The tcreplica pointer must not be NULL, and the replica it points to must not - /// be freed before TCTask.to_immut completes. - unsafe fn to_mut(&mut self, tcreplica: *mut TCReplica) { - self.inner = match std::mem::replace(&mut self.inner, Inner::Invalid) { - Inner::Immutable(task) => { - // SAFETY: - // - tcreplica is not null (promised by caller) - // - tcreplica outlives the pointer in this variant (promised by caller) - let tcreplica_ref: &mut TCReplica = - unsafe { TCReplica::from_ptr_arg_ref_mut(tcreplica) }; - let rep_ref = tcreplica_ref.borrow_mut(); - Inner::Mutable(task.into_mut(rep_ref), tcreplica) - } - Inner::Mutable(task, tcreplica) => Inner::Mutable(task, tcreplica), - Inner::Invalid => unreachable!(), - } - } - - /// Make an mutable TCTask into a immutable TCTask. Does nothing if the task - /// is already immutable. - #[allow(clippy::wrong_self_convention)] // to_immut_mut is not better! - fn to_immut(&mut self) { - self.inner = match std::mem::replace(&mut self.inner, Inner::Invalid) { - Inner::Immutable(task) => Inner::Immutable(task), - Inner::Mutable(task, tcreplica) => { - // SAFETY: - // - tcreplica is not null (promised by caller of to_mut, which created this - // variant) - // - tcreplica is still alive (promised by caller of to_mut) - let tcreplica_ref: &mut TCReplica = - unsafe { TCReplica::from_ptr_arg_ref_mut(tcreplica) }; - tcreplica_ref.release_borrow(); - Inner::Immutable(task.into_immut()) - } - Inner::Invalid => unreachable!(), - } - } -} - -impl From for TCTask { - fn from(task: Task) -> TCTask { - TCTask { - inner: Inner::Immutable(task), - error: None, - } - } -} - -/// Utility function to get a shared reference to the underlying Task. All Task getters -/// are error-free, so this does not handle errors. -fn wrap(task: *mut TCTask, f: F) -> T -where - F: FnOnce(&Task) -> T, -{ - // SAFETY: - // - task is not null (promised by caller) - // - task outlives this function (promised by caller) - let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) }; - let task: &Task = match &tctask.inner { - Inner::Immutable(t) => t, - Inner::Mutable(t, _) => t.deref(), - Inner::Invalid => unreachable!(), - }; - tctask.error = None; - f(task) -} - -/// Utility function to get a mutable reference to the underlying Task. The -/// TCTask must be mutable. The inner function may use `?` syntax to return an -/// error, which will be represented with the `err_value` returned to C. -fn wrap_mut(task: *mut TCTask, f: F, err_value: T) -> T -where - F: FnOnce(&mut TaskMut) -> anyhow::Result, -{ - // SAFETY: - // - task is not null (promised by caller) - // - task outlives this function (promised by caller) - let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) }; - let task: &mut TaskMut = match tctask.inner { - Inner::Immutable(_) => panic!("Task is immutable"), - Inner::Mutable(ref mut t, _) => t, - Inner::Invalid => unreachable!(), - }; - tctask.error = None; - match f(task) { - Ok(rv) => rv, - Err(e) => { - tctask.error = Some(err_to_ruststring(e)); - err_value - } - } -} - -impl TryFrom> for Tag { - type Error = anyhow::Error; - - fn try_from(mut rstring: RustString) -> Result { - let tagstr = rstring.as_str()?; - Tag::from_str(tagstr) - } -} - -#[ffizz_header::item] -#[ffizz(order = 1010)] -/// ***** TCTaskList ***** -/// -/// TCTaskList represents a list of tasks. -/// -/// The content of this struct must be treated as read-only: no fields or anything they reference -/// should be modified directly by C code. -/// -/// When an item is taken from this list, its pointer in `items` is set to NULL. -/// -/// ```c -/// typedef struct TCTaskList { -/// // number of tasks in items -/// size_t len; -/// // reserved -/// size_t _u1; -/// // Array of pointers representing each task. These remain owned by the TCTaskList instance and -/// // will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList. -/// // Pointers in the array may be NULL after `tc_task_list_take`. -/// struct TCTask **items; -/// } TCTaskList; -/// ``` -#[repr(C)] -pub struct TCTaskList { - /// number of tasks in items - len: libc::size_t, - - /// total size of items (internal use only) - capacity: libc::size_t, - - /// Array of pointers representing each task. These remain owned by the TCTaskList instance and - /// will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList. - /// Pointers in the array may be NULL after `tc_task_list_take`. - items: *mut Option>, -} - -impl CList for TCTaskList { - type Element = Option>; - - unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { - TCTaskList { - len, - capacity: cap, - items, - } - } - - fn slice(&mut self) -> &mut [Self::Element] { - // SAFETY: - // - because we have &mut self, we have read/write access to items[0..len] - // - all items are properly initialized Element's - // - return value lifetime is equal to &mmut self's, so access is exclusive - // - items and len came from Vec, so total size is < isize::MAX - unsafe { std::slice::from_raw_parts_mut(self.items, self.len) } - } - - fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { - (self.items, self.len, self.capacity) - } -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get a task's UUID. -/// -/// ```c -/// EXTERN_C struct TCUuid tc_task_get_uuid(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_uuid(task: *mut TCTask) -> TCUuid { - wrap(task, |task| { - // SAFETY: - // - value is not allocated and need not be freed - unsafe { TCUuid::return_val(task.get_uuid()) } - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get a task's status. -/// -/// ```c -/// EXTERN_C enum TCStatus tc_task_get_status(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_status(task: *mut TCTask) -> TCStatus { - wrap(task, |task| task.get_status().into()) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get the underlying key/value pairs for this task. The returned TCKVList is -/// a "snapshot" of the task and will not be updated if the task is subsequently -/// modified. It is the caller's responsibility to free the TCKVList. -/// -/// ```c -/// EXTERN_C struct TCKVList tc_task_get_taskmap(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_taskmap(task: *mut TCTask) -> TCKVList { - wrap(task, |task| { - let vec: Vec = task - .get_taskmap() - .iter() - .map(|(k, v)| { - let key = RustString::from(k.as_ref()); - let value = RustString::from(v.as_ref()); - TCKV::as_ctype((key, value)) - }) - .collect(); - // SAFETY: - // - caller will free this list - unsafe { TCKVList::return_val(vec) } - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get a task's description. -/// -/// ```c -/// EXTERN_C struct TCString tc_task_get_description(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_description(task: *mut TCTask) -> TCString { - wrap(task, |task| { - let descr = task.get_description(); - // SAFETY: - // - caller promises to free this string - unsafe { TCString::return_val(descr.into()) } - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get a task property's value, or NULL if the task has no such property, (including if the -/// property name is not valid utf-8). -/// -/// ```c -/// EXTERN_C struct TCString tc_task_get_value(struct TCTask *task, struct TCString property); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_value(task: *mut TCTask, property: TCString) -> TCString { - // SAFETY: - // - property is valid (promised by caller) - // - caller will not use property after this call (convention) - let mut property = unsafe { TCString::val_from_arg(property) }; - wrap(task, |task| { - if let Ok(property) = property.as_str() { - let value = task.get_value(property); - if let Some(value) = value { - // SAFETY: - // - caller promises to free this string - return unsafe { TCString::return_val(value.into()) }; - } - } - TCString::default() // null value - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get the entry timestamp for a task (when it was created), or 0 if not set. -/// -/// ```c -/// EXTERN_C time_t tc_task_get_entry(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_entry(task: *mut TCTask) -> libc::time_t { - wrap(task, |task| libc::time_t::as_ctype(task.get_entry())) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get the wait timestamp for a task, or 0 if not set. -/// -/// ```c -/// EXTERN_C time_t tc_task_get_wait(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_wait(task: *mut TCTask) -> libc::time_t { - wrap(task, |task| libc::time_t::as_ctype(task.get_wait())) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get the modified timestamp for a task, or 0 if not set. -/// -/// ```c -/// EXTERN_C time_t tc_task_get_modified(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_modified(task: *mut TCTask) -> libc::time_t { - wrap(task, |task| libc::time_t::as_ctype(task.get_modified())) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Check if a task is waiting. -/// -/// ```c -/// EXTERN_C bool tc_task_is_waiting(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_is_waiting(task: *mut TCTask) -> bool { - wrap(task, |task| task.is_waiting()) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Check if a task is active (started and not stopped). -/// -/// ```c -/// EXTERN_C bool tc_task_is_active(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_is_active(task: *mut TCTask) -> bool { - wrap(task, |task| task.is_active()) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Check if a task is blocked (depends on at least one other task). -/// -/// ```c -/// EXTERN_C bool tc_task_is_blocked(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_is_blocked(task: *mut TCTask) -> bool { - wrap(task, |task| task.is_blocked()) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Check if a task is blocking (at least one other task depends on it). -/// -/// ```c -/// EXTERN_C bool tc_task_is_blocking(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_is_blocking(task: *mut TCTask) -> bool { - wrap(task, |task| task.is_blocking()) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Check if a task has the given tag. If the tag is invalid, this function will return false, as -/// that (invalid) tag is not present. No error will be reported via `tc_task_error`. -/// -/// ```c -/// EXTERN_C bool tc_task_has_tag(struct TCTask *task, struct TCString tag); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_has_tag(task: *mut TCTask, tag: TCString) -> bool { - // SAFETY: - // - tag is valid (promised by caller) - // - caller will not use tag after this call (convention) - let tcstring = unsafe { TCString::val_from_arg(tag) }; - wrap(task, |task| { - if let Ok(tag) = Tag::try_from(tcstring) { - task.has_tag(&tag) - } else { - false - } - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get the tags for the task. -/// -/// The caller must free the returned TCStringList instance. The TCStringList instance does not -/// reference the task and the two may be freed in any order. -/// -/// ```c -/// EXTERN_C struct TCStringList tc_task_get_tags(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_tags(task: *mut TCTask) -> TCStringList { - wrap(task, |task| { - let vec: Vec = task - .get_tags() - .map(|t| { - // SAFETY: - // - this TCString will be freed via tc_string_list_free. - unsafe { TCString::return_val(t.as_ref().into()) } - }) - .collect(); - // SAFETY: - // - caller will free the list - unsafe { TCStringList::return_val(vec) } - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get the annotations for the task. -/// -/// The caller must free the returned TCAnnotationList instance. The TCStringList instance does not -/// reference the task and the two may be freed in any order. -/// -/// ```c -/// EXTERN_C struct TCAnnotationList tc_task_get_annotations(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_annotations(task: *mut TCTask) -> TCAnnotationList { - wrap(task, |task| { - let vec: Vec = task - .get_annotations() - .map(|a| { - let description = RustString::from(a.description); - TCAnnotation::as_ctype((a.entry, description)) - }) - .collect(); - // SAFETY: - // - caller will free the list - unsafe { TCAnnotationList::return_val(vec) } - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get the named UDA from the task. -/// -/// Returns a TCString with NULL ptr field if the UDA does not exist. -/// -/// ```c -/// EXTERN_C struct TCString tc_task_get_uda(struct TCTask *task, struct TCString ns, struct TCString key); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_uda( - task: *mut TCTask, - ns: TCString, - key: TCString, -) -> TCString { - wrap(task, |task| { - // SAFETY: - // - ns is valid (promised by caller) - // - caller will not use ns after this call (convention) - if let Ok(ns) = unsafe { TCString::val_from_arg(ns) }.as_str() { - // SAFETY: same - if let Ok(key) = unsafe { TCString::val_from_arg(key) }.as_str() { - if let Some(value) = task.get_uda(ns, key) { - // SAFETY: - // - caller will free this string (caller promises) - return unsafe { TCString::return_val(value.into()) }; - } - } - } - TCString::default() - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get the named legacy UDA from the task. -/// -/// Returns NULL if the UDA does not exist. -/// -/// ```c -/// EXTERN_C struct TCString tc_task_get_legacy_uda(struct TCTask *task, struct TCString key); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_legacy_uda(task: *mut TCTask, key: TCString) -> TCString { - wrap(task, |task| { - // SAFETY: - // - key is valid (promised by caller) - // - caller will not use key after this call (convention) - if let Ok(key) = unsafe { TCString::val_from_arg(key) }.as_str() { - if let Some(value) = task.get_legacy_uda(key) { - // SAFETY: - // - caller will free this string (caller promises) - return unsafe { TCString::return_val(value.into()) }; - } - } - TCString::default() - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get all UDAs for this task. -/// -/// Legacy UDAs are represented with an empty string in the ns field. -/// -/// ```c -/// EXTERN_C struct TCUdaList tc_task_get_udas(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_udas(task: *mut TCTask) -> TCUdaList { - wrap(task, |task| { - let vec: Vec = task - .get_udas() - .map(|((ns, key), value)| { - // SAFETY: - // - will be freed by tc_uda_list_free - unsafe { - TCUda::return_val(Uda { - ns: Some(ns.into()), - key: key.into(), - value: value.into(), - }) - } - }) - .collect(); - // SAFETY: - // - caller will free this list - unsafe { TCUdaList::return_val(vec) } - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get all UDAs for this task. -/// -/// All TCUdas in this list have a NULL ns field. The entire UDA key is -/// included in the key field. The caller must free the returned list. -/// -/// ```c -/// EXTERN_C struct TCUdaList tc_task_get_legacy_udas(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_legacy_udas(task: *mut TCTask) -> TCUdaList { - wrap(task, |task| { - let vec: Vec = task - .get_legacy_udas() - .map(|(key, value)| { - // SAFETY: - // - will be freed by tc_uda_list_free - unsafe { - TCUda::return_val(Uda { - ns: None, - key: key.into(), - value: value.into(), - }) - } - }) - .collect(); - // SAFETY: - // - caller will free this list - unsafe { TCUdaList::return_val(vec) } - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1001)] -/// Get all dependencies for a task. -/// -/// ```c -/// EXTERN_C struct TCUuidList tc_task_get_dependencies(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_get_dependencies(task: *mut TCTask) -> TCUuidList { - wrap(task, |task| { - let vec: Vec = task - .get_dependencies() - .map(|u| { - // SAFETY: - // - value is not allocated - unsafe { TCUuid::return_val(u) } - }) - .collect(); - // SAFETY: - // - caller will free this list - unsafe { TCUuidList::return_val(vec) } - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1002)] -/// Convert an immutable task into a mutable task. -/// -/// The task must not be NULL. It is modified in-place, and becomes mutable. -/// -/// The replica must not be NULL. After this function returns, the replica _cannot be used at all_ -/// until this task is made immutable again. This implies that it is not allowed for more than one -/// task associated with a replica to be mutable at any time. -/// -/// Typically mutation of tasks is bracketed with `tc_task_to_mut` and `tc_task_to_immut`: -/// -/// ```text -/// tc_task_to_mut(task, rep); -/// success = tc_task_done(task); -/// tc_task_to_immut(task, rep); -/// if (!success) { ... } -/// ``` -/// -/// ```c -/// EXTERN_C void tc_task_to_mut(struct TCTask *task, struct TCReplica *tcreplica); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_to_mut(task: *mut TCTask, tcreplica: *mut TCReplica) { - // SAFETY: - // - task is not null (promised by caller) - // - task outlives 'a (promised by caller) - let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) }; - // SAFETY: - // - tcreplica is not NULL (promised by caller) - // - tcreplica lives until later call to to_immut via tc_task_to_immut (promised by caller, - // who cannot call tc_replica_free during this time) - unsafe { tctask.to_mut(tcreplica) }; -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Set a mutable task's status. -/// -/// ```c -/// EXTERN_C TCResult tc_task_set_status(struct TCTask *task, enum TCStatus status); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_set_status(task: *mut TCTask, status: TCStatus) -> TCResult { - wrap_mut( - task, - |task| { - task.set_status(status.into())?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Set a mutable task's property value by name. If value.ptr is NULL, the property is removed. -/// -/// ```c -/// EXTERN_C TCResult tc_task_set_value(struct TCTask *task, struct TCString property, struct TCString value); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_set_value( - task: *mut TCTask, - property: TCString, - value: TCString, -) -> TCResult { - // SAFETY: - // - property is valid (promised by caller) - // - caller will not use property after this call (convention) - let mut property = unsafe { TCString::val_from_arg(property) }; - let value = if value.is_null() { - None - } else { - // SAFETY: - // - value is valid (promised by caller, after NULL check) - // - caller will not use value after this call (convention) - Some(unsafe { TCString::val_from_arg(value) }) - }; - wrap_mut( - task, - |task| { - let value_str = if let Some(mut v) = value { - Some(v.as_str()?.to_string()) - } else { - None - }; - task.set_value(property.as_str()?.to_string(), value_str)?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Set a mutable task's description. -/// -/// ```c -/// EXTERN_C TCResult tc_task_set_description(struct TCTask *task, struct TCString description); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_set_description( - task: *mut TCTask, - description: TCString, -) -> TCResult { - // SAFETY: - // - description is valid (promised by caller) - // - caller will not use description after this call (convention) - let mut description = unsafe { TCString::val_from_arg(description) }; - wrap_mut( - task, - |task| { - task.set_description(description.as_str()?.to_string())?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Set a mutable task's entry (creation time). Pass entry=0 to unset -/// the entry field. -/// -/// ```c -/// EXTERN_C TCResult tc_task_set_entry(struct TCTask *task, time_t entry); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_set_entry(task: *mut TCTask, entry: libc::time_t) -> TCResult { - wrap_mut( - task, - |task| { - // SAFETY: any time_t value is a valid timestamp - task.set_entry(unsafe { entry.from_ctype() })?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Set a mutable task's wait timestamp. Pass wait=0 to unset the wait field. -/// -/// ```c -/// EXTERN_C TCResult tc_task_set_wait(struct TCTask *task, time_t wait); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_set_wait(task: *mut TCTask, wait: libc::time_t) -> TCResult { - wrap_mut( - task, - |task| { - // SAFETY: any time_t value is a valid timestamp - task.set_wait(unsafe { wait.from_ctype() })?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Set a mutable task's modified timestamp. The value cannot be zero. -/// -/// ```c -/// EXTERN_C TCResult tc_task_set_modified(struct TCTask *task, time_t modified); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_set_modified( - task: *mut TCTask, - modified: libc::time_t, -) -> TCResult { - wrap_mut( - task, - |task| { - task.set_modified( - // SAFETY: any time_t value is a valid timestamp - unsafe { modified.from_ctype() } - .ok_or_else(|| anyhow::anyhow!("modified cannot be zero"))?, - )?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Start a task. -/// -/// ```c -/// EXTERN_C TCResult tc_task_start(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_start(task: *mut TCTask) -> TCResult { - wrap_mut( - task, - |task| { - task.start()?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Stop a task. -/// -/// ```c -/// EXTERN_C TCResult tc_task_stop(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_stop(task: *mut TCTask) -> TCResult { - wrap_mut( - task, - |task| { - task.stop()?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Mark a task as done. -/// -/// ```c -/// EXTERN_C TCResult tc_task_done(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_done(task: *mut TCTask) -> TCResult { - wrap_mut( - task, - |task| { - task.done()?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Mark a task as deleted. -/// -/// ```c -/// EXTERN_C TCResult tc_task_delete(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_delete(task: *mut TCTask) -> TCResult { - wrap_mut( - task, - |task| { - task.delete()?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Add a tag to a mutable task. -/// -/// ```c -/// EXTERN_C TCResult tc_task_add_tag(struct TCTask *task, struct TCString tag); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_add_tag(task: *mut TCTask, tag: TCString) -> TCResult { - // SAFETY: - // - tag is valid (promised by caller) - // - caller will not use tag after this call (convention) - let tcstring = unsafe { TCString::val_from_arg(tag) }; - wrap_mut( - task, - |task| { - let tag = Tag::try_from(tcstring)?; - task.add_tag(&tag)?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Remove a tag from a mutable task. -/// -/// ```c -/// EXTERN_C TCResult tc_task_remove_tag(struct TCTask *task, struct TCString tag); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_remove_tag(task: *mut TCTask, tag: TCString) -> TCResult { - // SAFETY: - // - tag is valid (promised by caller) - // - caller will not use tag after this call (convention) - let tcstring = unsafe { TCString::val_from_arg(tag) }; - wrap_mut( - task, - |task| { - let tag = Tag::try_from(tcstring)?; - task.remove_tag(&tag)?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Add an annotation to a mutable task. This call takes ownership of the -/// passed annotation, which must not be used after the call returns. -/// -/// ```c -/// EXTERN_C TCResult tc_task_add_annotation(struct TCTask *task, struct TCAnnotation *annotation); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_add_annotation( - task: *mut TCTask, - annotation: *mut TCAnnotation, -) -> TCResult { - // SAFETY: - // - annotation is not NULL (promised by caller) - // - annotation is return from a tc_string_.. so is valid - // - caller will not use annotation after this call - let (entry, description) = - unsafe { TCAnnotation::take_val_from_arg(annotation, TCAnnotation::default()) }; - wrap_mut( - task, - |task| { - let description = description.into_string()?; - task.add_annotation(Annotation { entry, description })?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Remove an annotation from a mutable task. -/// -/// ```c -/// EXTERN_C TCResult tc_task_remove_annotation(struct TCTask *task, int64_t entry); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_remove_annotation(task: *mut TCTask, entry: i64) -> TCResult { - wrap_mut( - task, - |task| { - task.remove_annotation(utc_timestamp(entry))?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Set a UDA on a mutable task. -/// -/// ```c -/// EXTERN_C TCResult tc_task_set_uda(struct TCTask *task, -/// struct TCString ns, -/// struct TCString key, -/// struct TCString value); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_set_uda( - task: *mut TCTask, - ns: TCString, - key: TCString, - value: TCString, -) -> TCResult { - // safety: - // - ns is valid (promised by caller) - // - caller will not use ns after this call (convention) - let mut ns = unsafe { TCString::val_from_arg(ns) }; - // SAFETY: same - let mut key = unsafe { TCString::val_from_arg(key) }; - // SAFETY: same - let mut value = unsafe { TCString::val_from_arg(value) }; - wrap_mut( - task, - |task| { - task.set_uda(ns.as_str()?, key.as_str()?, value.as_str()?.to_string())?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Remove a UDA fraom a mutable task. -/// -/// ```c -/// EXTERN_C TCResult tc_task_remove_uda(struct TCTask *task, struct TCString ns, struct TCString key); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_remove_uda( - task: *mut TCTask, - ns: TCString, - key: TCString, -) -> TCResult { - // safety: - // - ns is valid (promised by caller) - // - caller will not use ns after this call (convention) - let mut ns = unsafe { TCString::val_from_arg(ns) }; - // SAFETY: same - let mut key = unsafe { TCString::val_from_arg(key) }; - wrap_mut( - task, - |task| { - task.remove_uda(ns.as_str()?, key.as_str()?)?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Set a legacy UDA on a mutable task. -/// -/// ```c -/// EXTERN_C TCResult tc_task_set_legacy_uda(struct TCTask *task, struct TCString key, struct TCString value); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_set_legacy_uda( - task: *mut TCTask, - key: TCString, - value: TCString, -) -> TCResult { - // safety: - // - key is valid (promised by caller) - // - caller will not use key after this call (convention) - let mut key = unsafe { TCString::val_from_arg(key) }; - // SAFETY: same - let mut value = unsafe { TCString::val_from_arg(value) }; - wrap_mut( - task, - |task| { - task.set_legacy_uda(key.as_str()?.to_string(), value.as_str()?.to_string())?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Remove a UDA fraom a mutable task. -/// -/// ```c -/// EXTERN_C TCResult tc_task_remove_legacy_uda(struct TCTask *task, struct TCString key); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_remove_legacy_uda(task: *mut TCTask, key: TCString) -> TCResult { - // safety: - // - key is valid (promised by caller) - // - caller will not use key after this call (convention) - let mut key = unsafe { TCString::val_from_arg(key) }; - wrap_mut( - task, - |task| { - task.remove_legacy_uda(key.as_str()?.to_string())?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Add a dependency. -/// -/// ```c -/// EXTERN_C TCResult tc_task_add_dependency(struct TCTask *task, struct TCUuid dep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_add_dependency(task: *mut TCTask, dep: TCUuid) -> TCResult { - // SAFETY: - // - tcuuid is a valid TCUuid (all byte patterns are valid) - let dep: Uuid = unsafe { TCUuid::val_from_arg(dep) }; - wrap_mut( - task, - |task| { - task.add_dependency(dep)?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1003)] -/// Remove a dependency. -/// -/// ```c -/// EXTERN_C TCResult tc_task_remove_dependency(struct TCTask *task, struct TCUuid dep); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_remove_dependency(task: *mut TCTask, dep: TCUuid) -> TCResult { - // SAFETY: - // - tcuuid is a valid TCUuid (all byte patterns are valid) - let dep: Uuid = unsafe { TCUuid::val_from_arg(dep) }; - wrap_mut( - task, - |task| { - task.remove_dependency(dep)?; - Ok(TCResult::Ok) - }, - TCResult::Error, - ) -} - -#[ffizz_header::item] -#[ffizz(order = 1004)] -/// Convert a mutable task into an immutable task. -/// -/// The task must not be NULL. It is modified in-place, and becomes immutable. -/// -/// The replica passed to `tc_task_to_mut` may be used freely after this call. -/// -/// ```c -/// EXTERN_C void tc_task_to_immut(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_to_immut(task: *mut TCTask) { - // SAFETY: - // - task is not null (promised by caller) - // - task outlives 'a (promised by caller) - let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) }; - tctask.to_immut(); -} - -#[ffizz_header::item] -#[ffizz(order = 1005)] -/// Get the latest error for a task, or a string NULL ptr field if the last operation succeeded. -/// Subsequent calls to this function will return NULL. The task pointer must not be NULL. The -/// caller must free the returned string. -/// -/// ```c -/// EXTERN_C struct TCString tc_task_error(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_error(task: *mut TCTask) -> TCString { - // SAFETY: - // - task is not null (promised by caller) - // - task outlives 'a (promised by caller) - let task: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) }; - if let Some(rstring) = task.error.take() { - // SAFETY: - // - caller promises to free this value - unsafe { TCString::return_val(rstring) } - } else { - TCString::default() - } -} - -#[ffizz_header::item] -#[ffizz(order = 1006)] -/// Free a task. The given task must not be NULL. The task must not be used after this function -/// returns, and must not be freed more than once. -/// -/// If the task is currently mutable, it will first be made immutable. -/// -/// ```c -/// EXTERN_C void tc_task_free(struct TCTask *task); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_free(task: *mut TCTask) { - // SAFETY: - // - task is not NULL (promised by caller) - // - caller will not use the TCTask after this (promised by caller) - let mut tctask = unsafe { TCTask::take_from_ptr_arg(task) }; - - // convert to immut if it was mutable - tctask.to_immut(); - - drop(tctask); -} - -#[ffizz_header::item] -#[ffizz(order = 1011)] -/// Take an item from a TCTaskList. After this call, the indexed item is no longer associated -/// with the list and becomes the caller's responsibility, just as if it had been returned from -/// `tc_replica_get_task`. -/// -/// The corresponding element in the `items` array will be set to NULL. If that field is already -/// NULL (that is, if the item has already been taken), this function will return NULL. If the -/// index is out of bounds, this function will also return NULL. -/// -/// The passed TCTaskList remains owned by the caller. -/// -/// ```c -/// EXTERN_C struct TCTask *tc_task_list_take(struct TCTaskList *tasks, size_t index); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_list_take(tasks: *mut TCTaskList, index: usize) -> *mut TCTask { - // SAFETY: - // - tasks is not NULL and points to a valid TCTaskList (caller is not allowed to - // modify the list directly, and tc_task_list_take leaves the list valid) - let p = unsafe { take_optional_pointer_list_item(tasks, index) }; - if let Some(p) = p { - p.as_ptr() - } else { - std::ptr::null_mut() - } -} - -#[ffizz_header::item] -#[ffizz(order = 1011)] -/// Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after -/// this call. -/// -/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList. -/// -/// ```c -/// EXTERN_C void tc_task_list_free(struct TCTaskList *tasks); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_task_list_free(tasks: *mut TCTaskList) { - // SAFETY: - // - tasks is not NULL and points to a valid TCTaskList (caller is not allowed to - // modify the list directly, and tc_task_list_take leaves the list valid) - // - caller promises not to use the value after return - unsafe { drop_optional_pointer_list(tasks) }; -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn empty_list_has_non_null_pointer() { - let tasks = unsafe { TCTaskList::return_val(Vec::new()) }; - assert!(!tasks.items.is_null()); - assert_eq!(tasks.len, 0); - assert_eq!(tasks.capacity, 0); - } - - #[test] - fn free_sets_null_pointer() { - let mut tasks = unsafe { TCTaskList::return_val(Vec::new()) }; - // SAFETY: testing expected behavior - unsafe { tc_task_list_free(&mut tasks) }; - assert!(tasks.items.is_null()); - assert_eq!(tasks.len, 0); - assert_eq!(tasks.capacity, 0); - } -} diff --git a/src/tc/lib/src/traits.rs b/src/tc/lib/src/traits.rs deleted file mode 100644 index 0b2eac970..000000000 --- a/src/tc/lib/src/traits.rs +++ /dev/null @@ -1,338 +0,0 @@ -use crate::util::vec_into_raw_parts; -use std::ptr::NonNull; - -/// Support for values passed to Rust by value. These are represented as full structs in C. Such -/// values are implicitly copyable, via C's struct assignment. -/// -/// The Rust and C types may differ, with from_ctype and as_ctype converting between them. -/// Implement this trait for the C type. -/// -/// The RustType must be droppable (not containing raw pointers). -pub(crate) trait PassByValue: Sized { - type RustType; - - /// Convert a C value to a Rust value. - /// - /// # Safety - /// - /// `self` must be a valid CType. - #[allow(clippy::wrong_self_convention)] - unsafe fn from_ctype(self) -> Self::RustType; - - /// Convert a Rust value to a C value. - fn as_ctype(arg: Self::RustType) -> Self; - - /// Take a value from C as an argument. - /// - /// # Safety - /// - /// - `self` must be a valid instance of the C type. This is typically ensured either by - /// requiring that C code not modify it, or by defining the valid values in C comments. - unsafe fn val_from_arg(arg: Self) -> Self::RustType { - // SAFETY: - // - arg is a valid CType (promised by caller) - unsafe { arg.from_ctype() } - } - - /// Take a value from C as a pointer argument, replacing it with the given value. This is used - /// to invalidate the C value as an additional assurance against subsequent use of the value. - /// - /// # Safety - /// - /// - arg must not be NULL - /// - *arg must be a valid, properly aligned instance of the C type - unsafe fn take_val_from_arg(arg: *mut Self, mut replacement: Self) -> Self::RustType { - // SAFETY: - // - arg is valid (promised by caller) - // - replacement is valid and aligned (guaranteed by Rust) - unsafe { std::ptr::swap(arg, &mut replacement) }; - // SAFETY: - // - replacement (formerly *arg) is a valid CType (promised by caller) - unsafe { PassByValue::val_from_arg(replacement) } - } - - /// Return a value to C - /// - /// # Safety - /// - /// - if the value is allocated, the caller must ensure that the value is eventually freed - unsafe fn return_val(arg: Self::RustType) -> Self { - Self::as_ctype(arg) - } - - /// Return a value to C, via an "output parameter" - /// - /// # Safety - /// - /// - `arg_out` must not be NULL and must be properly aligned and pointing to valid memory - /// of the size of CType. - unsafe fn val_to_arg_out(val: Self::RustType, arg_out: *mut Self) { - debug_assert!(!arg_out.is_null()); - // SAFETY: - // - arg_out is not NULL (promised by caller, asserted) - // - arg_out is properly aligned and points to valid memory (promised by caller) - unsafe { *arg_out = Self::as_ctype(val) }; - } -} - -/// Support for values passed to Rust by pointer. These are represented as opaque structs in C, -/// and always handled as pointers. -pub(crate) trait PassByPointer: Sized { - /// Take a value from C as an argument. - /// - /// # Safety - /// - /// - arg must not be NULL - /// - arg must be a value returned from Box::into_raw (via return_ptr) - /// - arg becomes invalid and must not be used after this call - unsafe fn take_from_ptr_arg(arg: *mut Self) -> Self { - debug_assert!(!arg.is_null()); - // SAFETY: see docstring - unsafe { *(Box::from_raw(arg)) } - } - - /// Borrow a value from C as an argument. - /// - /// # Safety - /// - /// - arg must not be NULL - /// - *arg must be a valid instance of Self - /// - arg must be valid for the lifetime assigned by the caller - /// - arg must not be modified by anything else during that lifetime - unsafe fn from_ptr_arg_ref<'a>(arg: *const Self) -> &'a Self { - debug_assert!(!arg.is_null()); - // SAFETY: see docstring - unsafe { &*arg } - } - - /// Mutably borrow a value from C as an argument. - /// - /// # Safety - /// - /// - arg must not be NULL - /// - *arg must be a valid instance of Self - /// - arg must be valid for the lifetime assigned by the caller - /// - arg must not be accessed by anything else during that lifetime - unsafe fn from_ptr_arg_ref_mut<'a>(arg: *mut Self) -> &'a mut Self { - debug_assert!(!arg.is_null()); - // SAFETY: see docstring - unsafe { &mut *arg } - } - - /// Return a value to C, transferring ownership - /// - /// # Safety - /// - /// - the caller must ensure that the value is eventually freed - unsafe fn return_ptr(self) -> *mut Self { - Box::into_raw(Box::new(self)) - } -} - -/// Support for C lists of objects referenced by value. -/// -/// The underlying C type should have three fields, containing items, length, and capacity. The -/// required trait functions just fetch and set these fields. -/// -/// The PassByValue trait will be implemented automatically, converting between the C type and -/// `Vec`. -/// -/// The element type can be PassByValue or PassByPointer. If the latter, it should use either -/// `NonNull` or `Option>` to represent the element. The latter is an "optional -/// pointer list", where elements can be omitted. -/// -/// For most cases, it is only necessary to implement `tc_.._free` that calls one of the -/// drop_..._list functions. -/// -/// # Safety -/// -/// The C type must be documented as read-only. None of the fields may be modified, nor anything -/// accessible via the `items` array. The exception is modification via "taking" elements. -/// -/// This class guarantees that the items pointer is non-NULL for any valid list (even when len=0). -pub(crate) trait CList: Sized { - type Element; - - /// Create a new CList from the given items, len, and capacity. - /// - /// # Safety - /// - /// The arguments must either: - /// - be NULL, 0, and 0, respectively; or - /// - be valid for Vec::from_raw_parts - unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self; - - /// Return a mutable slice representing the elements in this list. - fn slice(&mut self) -> &mut [Self::Element]; - - /// Get the items, len, and capacity (in that order) for this instance. These must be - /// precisely the same values passed tearlier to `from_raw_parts`. - fn into_raw_parts(self) -> (*mut Self::Element, usize, usize); - - /// Generate a NULL value. By default this is a NULL items pointer with zero length and - /// capacity. - fn null_value() -> Self { - // SAFETY: - // - satisfies the first case in from_raw_parts' safety documentation - unsafe { Self::from_raw_parts(std::ptr::null_mut(), 0, 0) } - } -} - -/// Given a CList containing pass-by-value values, drop all of the values and -/// the list. -/// -/// This is a convenience function for `tc_.._list_free` functions. -/// -/// # Safety -/// -/// - List must be non-NULL and point to a valid CL instance -/// - The caller must not use the value array points to after this function, as -/// it has been freed. It will be replaced with the null value. -pub(crate) unsafe fn drop_value_list(list: *mut CL) -where - CL: CList, - T: PassByValue, -{ - debug_assert!(!list.is_null()); - - // SAFETY: - // - *list is a valid CL (promised by caller) - let mut vec = unsafe { CL::take_val_from_arg(list, CL::null_value()) }; - - // first, drop each of the elements in turn - for e in vec.drain(..) { - // SAFETY: - // - e is a valid Element (promised by caller) - // - e is owned - drop(unsafe { PassByValue::val_from_arg(e) }); - } - // then drop the vector - drop(vec); -} - -/// Given a CList containing NonNull pointers, drop all of the pointed-to values and the list. -/// -/// This is a convenience function for `tc_.._list_free` functions. -/// -/// # Safety -/// -/// - List must be non-NULL and point to a valid CL instance -/// - The caller must not use the value array points to after this function, as -/// it has been freed. It will be replaced with the null value. -#[allow(dead_code)] // this was useful once, and might be again? -pub(crate) unsafe fn drop_pointer_list(list: *mut CL) -where - CL: CList>, - T: PassByPointer, -{ - debug_assert!(!list.is_null()); - // SAFETY: - // - *list is a valid CL (promised by caller) - let mut vec = unsafe { CL::take_val_from_arg(list, CL::null_value()) }; - - // first, drop each of the elements in turn - for e in vec.drain(..) { - // SAFETY: - // - e is a valid Element (promised by caller) - // - e is owned - drop(unsafe { PassByPointer::take_from_ptr_arg(e.as_ptr()) }); - } - // then drop the vector - drop(vec); -} - -/// Given a CList containing optional pointers, drop all of the non-null pointed-to values and the -/// list. -/// -/// This is a convenience function for `tc_.._list_free` functions, for lists from which items -/// can be taken. -/// -/// # Safety -/// -/// - List must be non-NULL and point to a valid CL instance -/// - The caller must not use the value array points to after this function, as -/// it has been freed. It will be replaced with the null value. -pub(crate) unsafe fn drop_optional_pointer_list(list: *mut CL) -where - CL: CList>>, - T: PassByPointer, -{ - debug_assert!(!list.is_null()); - // SAFETY: - // - *list is a valid CL (promised by caller) - let mut vec = unsafe { CL::take_val_from_arg(list, CL::null_value()) }; - - // first, drop each of the elements in turn - for e in vec.drain(..).flatten() { - // SAFETY: - // - e is a valid Element (promised by caller) - // - e is owned - drop(unsafe { PassByPointer::take_from_ptr_arg(e.as_ptr()) }); - } - // then drop the vector - drop(vec); -} - -/// Take a value from an optional pointer list, returning the value and replacing its array -/// element with NULL. -/// -/// This is a convenience function for `tc_.._list_take` functions, for lists from which items -/// can be taken. -/// -/// The returned value will be None if the element has already been taken, or if the index is -/// out of bounds. -/// -/// # Safety -/// -/// - List must be non-NULL and point to a valid CL instance -pub(crate) unsafe fn take_optional_pointer_list_item( - list: *mut CL, - index: usize, -) -> Option> -where - CL: CList>>, - T: PassByPointer, -{ - debug_assert!(!list.is_null()); - - // SAFETy: - // - list is properly aligned, dereferencable, and points to an initialized CL, since it is valid - // - the lifetime of the resulting reference is limited to this function, during which time - // nothing else refers to this memory. - let slice = unsafe { list.as_mut() }.unwrap().slice(); - if let Some(elt_ref) = slice.get_mut(index) { - let mut rv = None; - if let Some(elt) = elt_ref.as_mut() { - rv = Some(*elt); - *elt_ref = None; // clear out the array element - } - rv - } else { - None // index out of bounds - } -} - -impl
    PassByValue for A -where - A: CList, -{ - type RustType = Vec; - - unsafe fn from_ctype(self) -> Self::RustType { - let (items, len, cap) = self.into_raw_parts(); - debug_assert!(!items.is_null()); - // SAFETY: - // - CList::from_raw_parts requires that items, len, and cap be valid for - // Vec::from_raw_parts if not NULL, and they are not NULL (as promised by caller) - // - CList::into_raw_parts returns precisely the values passed to from_raw_parts. - // - those parts are passed to Vec::from_raw_parts here. - unsafe { Vec::from_raw_parts(items as *mut _, len, cap) } - } - - fn as_ctype(arg: Self::RustType) -> Self { - let (items, len, cap) = vec_into_raw_parts(arg); - // SAFETY: - // - satisfies the second case in from_raw_parts' safety documentation - unsafe { Self::from_raw_parts(items, len, cap) } - } -} diff --git a/src/tc/lib/src/uda.rs b/src/tc/lib/src/uda.rs deleted file mode 100644 index 3994bfc82..000000000 --- a/src/tc/lib/src/uda.rs +++ /dev/null @@ -1,188 +0,0 @@ -use crate::traits::*; -use crate::types::*; - -#[ffizz_header::item] -#[ffizz(order = 500)] -/// ***** TCUda ***** -/// -/// TCUda contains the details of a UDA. -/// -/// ```c -/// typedef struct TCUda { -/// // Namespace of the UDA. For legacy UDAs, this may have a NULL ptr field. -/// struct TCString ns; -/// // UDA key. Must not be NULL. -/// struct TCString key; -/// // Content of the UDA. Must not be NULL. -/// struct TCString value; -/// } TCUda; -/// ``` -#[repr(C)] -#[derive(Default)] -pub struct TCUda { - pub ns: TCString, - pub key: TCString, - pub value: TCString, -} - -pub(crate) struct Uda { - pub ns: Option>, - pub key: RustString<'static>, - pub value: RustString<'static>, -} - -impl PassByValue for TCUda { - type RustType = Uda; - - unsafe fn from_ctype(self) -> Self::RustType { - Uda { - ns: if self.ns.is_null() { - None - } else { - // SAFETY: - // - self is owned, so we can take ownership of this TCString - // - self.ns is a valid, non-null TCString (NULL just checked) - Some(unsafe { TCString::val_from_arg(self.ns) }) - }, - // SAFETY: - // - self is owned, so we can take ownership of this TCString - // - self.key is a valid, non-null TCString (see type docstring) - key: unsafe { TCString::val_from_arg(self.key) }, - // SAFETY: - // - self is owned, so we can take ownership of this TCString - // - self.value is a valid, non-null TCString (see type docstring) - value: unsafe { TCString::val_from_arg(self.value) }, - } - } - - fn as_ctype(uda: Uda) -> Self { - TCUda { - // SAFETY: caller assumes ownership of this value - ns: if let Some(ns) = uda.ns { - unsafe { TCString::return_val(ns) } - } else { - TCString::default() - }, - // SAFETY: caller assumes ownership of this value - key: unsafe { TCString::return_val(uda.key) }, - // SAFETY: caller assumes ownership of this value - value: unsafe { TCString::return_val(uda.value) }, - } - } -} - -#[ffizz_header::item] -#[ffizz(order = 510)] -/// ***** TCUdaList ***** -/// -/// TCUdaList represents a list of UDAs. -/// -/// The content of this struct must be treated as read-only. -/// -/// ```c -/// typedef struct TCUdaList { -/// // number of UDAs in items -/// size_t len; -/// // reserved -/// size_t _u1; -/// // Array of UDAs. These remain owned by the TCUdaList instance and will be freed by -/// // tc_uda_list_free. This pointer is never NULL for a valid TCUdaList. -/// struct TCUda *items; -/// } TCUdaList; -/// ``` -#[repr(C)] -pub struct TCUdaList { - /// number of UDAs in items - len: libc::size_t, - - /// total size of items (internal use only) - _capacity: libc::size_t, - - /// Array of UDAs. These remain owned by the TCUdaList instance and will be freed by - /// tc_uda_list_free. This pointer is never NULL for a valid TCUdaList. - items: *mut TCUda, -} - -impl CList for TCUdaList { - type Element = TCUda; - - unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { - TCUdaList { - len, - _capacity: cap, - items, - } - } - - fn slice(&mut self) -> &mut [Self::Element] { - // SAFETY: - // - because we have &mut self, we have read/write access to items[0..len] - // - all items are properly initialized Element's - // - return value lifetime is equal to &mmut self's, so access is exclusive - // - items and len came from Vec, so total size is < isize::MAX - unsafe { std::slice::from_raw_parts_mut(self.items, self.len) } - } - - fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { - (self.items, self.len, self._capacity) - } -} - -#[ffizz_header::item] -#[ffizz(order = 501)] -/// Free a TCUda instance. The instance, and the TCStrings it contains, must not be used -/// after this call. -/// -/// ```c -/// EXTERN_C void tc_uda_free(struct TCUda *tcuda); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_uda_free(tcuda: *mut TCUda) { - debug_assert!(!tcuda.is_null()); - // SAFETY: - // - *tcuda is a valid TCUda (caller promises to treat it as read-only) - let uda = unsafe { TCUda::take_val_from_arg(tcuda, TCUda::default()) }; - drop(uda); -} - -#[ffizz_header::item] -#[ffizz(order = 511)] -/// Free a TCUdaList instance. The instance, and all TCUdas it contains, must not be used after -/// this call. -/// -/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUdaList. -/// -/// ```c -/// EXTERN_C void tc_uda_list_free(struct TCUdaList *tcudas); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_uda_list_free(tcudas: *mut TCUdaList) { - // SAFETY: - // - tcudas is not NULL and points to a valid TCUdaList (caller is not allowed to - // modify the list) - // - caller promises not to use the value after return - unsafe { drop_value_list(tcudas) } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn empty_list_has_non_null_pointer() { - let tcudas = unsafe { TCUdaList::return_val(Vec::new()) }; - assert!(!tcudas.items.is_null()); - assert_eq!(tcudas.len, 0); - assert_eq!(tcudas._capacity, 0); - } - - #[test] - fn free_sets_null_pointer() { - let mut tcudas = unsafe { TCUdaList::return_val(Vec::new()) }; - // SAFETY: testing expected behavior - unsafe { tc_uda_list_free(&mut tcudas) }; - assert!(tcudas.items.is_null()); - assert_eq!(tcudas.len, 0); - assert_eq!(tcudas._capacity, 0); - } -} diff --git a/src/tc/lib/src/util.rs b/src/tc/lib/src/util.rs deleted file mode 100644 index 61223e6bf..000000000 --- a/src/tc/lib/src/util.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::string::RustString; - -pub(crate) fn err_to_ruststring(e: anyhow::Error) -> RustString<'static> { - // The default `to_string` representation of `anyhow::Error` only shows the "outermost" - // context, e.g., "Could not connect to server", and omits the juicy details about what - // actually went wrong. So, join all of those contexts with `: ` for presentation to the C++ - // layer. - let entire_msg = e - .chain() - .skip(1) - .fold(e.to_string(), |a, b| format!("{}: {}", a, b)); - RustString::from(entire_msg) -} - -/// An implementation of Vec::into_raw_parts, which is still unstable. Returns ptr, len, cap. -pub(crate) fn vec_into_raw_parts(vec: Vec) -> (*mut T, usize, usize) { - // emulate Vec::into_raw_parts(): - // - disable dropping the Vec with ManuallyDrop - // - extract ptr, len, and capacity using those methods - let mut vec = std::mem::ManuallyDrop::new(vec); - (vec.as_mut_ptr(), vec.len(), vec.capacity()) -} - -/// An implementation of String::into_raw_parts, which is still unstable. Returns ptr, len, cap. -pub(crate) fn string_into_raw_parts(string: String) -> (*mut u8, usize, usize) { - // emulate String::into_raw_parts(): - // - disable dropping the String with ManuallyDrop - // - extract ptr, len, and capacity using those methods - let mut string = std::mem::ManuallyDrop::new(string); - (string.as_mut_ptr(), string.len(), string.capacity()) -} diff --git a/src/tc/lib/src/uuid.rs b/src/tc/lib/src/uuid.rs deleted file mode 100644 index c979074ce..000000000 --- a/src/tc/lib/src/uuid.rs +++ /dev/null @@ -1,239 +0,0 @@ -use crate::traits::*; -use crate::types::*; -use libc; -use taskchampion::Uuid; - -#[ffizz_header::item] -#[ffizz(order = 300)] -/// ***** TCUuid ***** -/// -/// TCUuid is used as a task identifier. Uuids do not contain any pointers and need not be freed. -/// Uuids are typically treated as opaque, but the bytes are available in big-endian format. -/// -/// ```c -/// typedef struct TCUuid { -/// uint8_t bytes[16]; -/// } TCUuid; -/// ``` -#[repr(C)] -#[derive(Debug, Default)] -pub struct TCUuid([u8; 16]); - -impl PassByValue for TCUuid { - type RustType = Uuid; - - unsafe fn from_ctype(self) -> Self::RustType { - // SAFETY: - // - any 16-byte value is a valid Uuid - Uuid::from_bytes(self.0) - } - - fn as_ctype(arg: Uuid) -> Self { - TCUuid(*arg.as_bytes()) - } -} - -#[ffizz_header::item] -#[ffizz(order = 301)] -/// Length, in bytes, of the string representation of a UUID (without NUL terminator) -/// -/// ```c -/// #define TC_UUID_STRING_BYTES 36 -/// ``` -// TODO: debug_assert or static_assert this somewhere? -pub const TC_UUID_STRING_BYTES: usize = 36; - -#[ffizz_header::item] -#[ffizz(order = 310)] -/// TCUuidList represents a list of uuids. -/// -/// The content of this struct must be treated as read-only. -/// -/// ```c -/// typedef struct TCUuidList { -/// // number of uuids in items -/// size_t len; -/// // reserved -/// size_t _u1; -/// // Array of uuids. This pointer is never NULL for a valid TCUuidList. -/// struct TCUuid *items; -/// } TCUuidList; -/// ``` -#[repr(C)] -pub struct TCUuidList { - /// number of uuids in items - len: libc::size_t, - - /// total size of items (internal use only) - capacity: libc::size_t, - - /// Array of uuids. This pointer is never NULL for a valid TCUuidList. - items: *mut TCUuid, -} - -impl CList for TCUuidList { - type Element = TCUuid; - - unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self { - TCUuidList { - len, - capacity: cap, - items, - } - } - - fn slice(&mut self) -> &mut [Self::Element] { - // SAFETY: - // - because we have &mut self, we have read/write access to items[0..len] - // - all items are properly initialized Element's - // - return value lifetime is equal to &mmut self's, so access is exclusive - // - items and len came from Vec, so total size is < isize::MAX - unsafe { std::slice::from_raw_parts_mut(self.items, self.len) } - } - - fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) { - (self.items, self.len, self.capacity) - } -} - -#[ffizz_header::item] -#[ffizz(order = 302)] -/// Create a new, randomly-generated UUID. -/// -/// ```c -/// EXTERN_C struct TCUuid tc_uuid_new_v4(void); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_uuid_new_v4() -> TCUuid { - // SAFETY: - // - value is not allocated - unsafe { TCUuid::return_val(Uuid::new_v4()) } -} - -#[ffizz_header::item] -#[ffizz(order = 302)] -/// Create a new UUID with the nil value. -/// -/// ```c -/// EXTERN_C struct TCUuid tc_uuid_nil(void); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_uuid_nil() -> TCUuid { - // SAFETY: - // - value is not allocated - unsafe { TCUuid::return_val(Uuid::nil()) } -} - -#[ffizz_header::item] -#[ffizz(order = 302)] -/// Write the string representation of a TCUuid into the given buffer, which must be -/// at least TC_UUID_STRING_BYTES long. No NUL terminator is added. -/// -/// ```c -/// EXTERN_C void tc_uuid_to_buf(struct TCUuid tcuuid, char *buf); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_uuid_to_buf(tcuuid: TCUuid, buf: *mut libc::c_char) { - debug_assert!(!buf.is_null()); - // SAFETY: - // - buf is valid for len bytes (by C convention) - // - (no alignment requirements for a byte slice) - // - content of buf will not be mutated during the lifetime of this slice (lifetime - // does not outlive this function call) - // - the length of the buffer is less than isize::MAX (promised by caller) - let buf: &mut [u8] = - unsafe { std::slice::from_raw_parts_mut(buf as *mut u8, TC_UUID_STRING_BYTES) }; - // SAFETY: - // - tcuuid is a valid TCUuid (all byte patterns are valid) - let uuid: Uuid = unsafe { TCUuid::val_from_arg(tcuuid) }; - uuid.as_hyphenated().encode_lower(buf); -} - -#[ffizz_header::item] -#[ffizz(order = 302)] -/// Return the hyphenated string representation of a TCUuid. The returned string -/// must be freed with tc_string_free. -/// -/// ```c -/// EXTERN_C struct TCString tc_uuid_to_str(struct TCUuid tcuuid); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_uuid_to_str(tcuuid: TCUuid) -> TCString { - // SAFETY: - // - tcuuid is a valid TCUuid (all byte patterns are valid) - let uuid: Uuid = unsafe { TCUuid::val_from_arg(tcuuid) }; - let s = uuid.to_string(); - // SAFETY: - // - caller promises to free this value. - unsafe { TCString::return_val(s.into()) } -} - -#[ffizz_header::item] -#[ffizz(order = 302)] -/// Parse the given string as a UUID. Returns TC_RESULT_ERROR on parse failure or if the given -/// string is not valid. -/// -/// ```c -/// EXTERN_C TCResult tc_uuid_from_str(struct TCString s, struct TCUuid *uuid_out); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_uuid_from_str(s: TCString, uuid_out: *mut TCUuid) -> TCResult { - debug_assert!(!s.is_null()); - debug_assert!(!uuid_out.is_null()); - // SAFETY: - // - s is valid (promised by caller) - // - caller will not use s after this call (convention) - let mut s = unsafe { TCString::val_from_arg(s) }; - if let Ok(s) = s.as_str() { - if let Ok(u) = Uuid::parse_str(s) { - // SAFETY: - // - uuid_out is not NULL (promised by caller) - // - alignment is not required - unsafe { TCUuid::val_to_arg_out(u, uuid_out) }; - return TCResult::Ok; - } - } - TCResult::Error -} - -#[ffizz_header::item] -#[ffizz(order = 312)] -/// Free a TCUuidList instance. The instance, and all TCUuids it contains, must not be used after -/// this call. -/// -/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUuidList. -/// -/// ```c -/// EXTERN_C void tc_uuid_list_free(struct TCUuidList *tcuuids); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_uuid_list_free(tcuuids: *mut TCUuidList) { - // SAFETY: - // - tcuuids is not NULL and points to a valid TCUuidList (caller is not allowed to - // modify the list) - // - caller promises not to use the value after return - unsafe { drop_value_list(tcuuids) }; -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn empty_list_has_non_null_pointer() { - let tcuuids = unsafe { TCUuidList::return_val(Vec::new()) }; - assert!(!tcuuids.items.is_null()); - assert_eq!(tcuuids.len, 0); - assert_eq!(tcuuids.capacity, 0); - } - - #[test] - fn free_sets_null_pointer() { - let mut tcuuids = unsafe { TCUuidList::return_val(Vec::new()) }; - // SAFETY: testing expected behavior - unsafe { tc_uuid_list_free(&mut tcuuids) }; - assert!(tcuuids.items.is_null()); - assert_eq!(tcuuids.len, 0); - assert_eq!(tcuuids.capacity, 0); - } -} diff --git a/src/tc/lib/src/workingset.rs b/src/tc/lib/src/workingset.rs deleted file mode 100644 index ef9ceaf99..000000000 --- a/src/tc/lib/src/workingset.rs +++ /dev/null @@ -1,141 +0,0 @@ -use crate::traits::*; -use crate::types::*; -use taskchampion::{Uuid, WorkingSet}; - -#[ffizz_header::item] -#[ffizz(order = 1100)] -/// ***** TCWorkingSet ***** -/// -/// A TCWorkingSet represents a snapshot of the working set for a replica. It is not automatically -/// updated based on changes in the replica. Its lifetime is independent of the replica and it can -/// be freed at any time. -/// -/// To iterate over a working set, search indexes 1 through largest_index. -/// -/// # Safety -/// -/// The `*TCWorkingSet` returned from `tc_replica_working_set` is owned by the caller and -/// must later be freed to avoid a memory leak. Its lifetime is independent of the replica -/// from which it was generated. -/// -/// Any function taking a `*TCWorkingSet` requires: -/// - the pointer must not be NUL; -/// - the pointer must be one previously returned from `tc_replica_working_set` -/// - the memory referenced by the pointer must never be accessed by C code; and -/// - except for `tc_replica_free`, ownership of a `*TCWorkingSet` remains with the caller. -/// -/// Once passed to `tc_replica_free`, a `*TCWorkingSet` becomes invalid and must not be used again. -/// -/// TCWorkingSet is not threadsafe. -/// -/// ```c -/// typedef struct TCWorkingSet TCWorkingSet; -/// ``` -pub struct TCWorkingSet(WorkingSet); - -impl PassByPointer for TCWorkingSet {} - -impl From for TCWorkingSet { - fn from(ws: WorkingSet) -> TCWorkingSet { - TCWorkingSet(ws) - } -} - -/// Utility function to get a shared reference to the underlying WorkingSet. -fn wrap(ws: *mut TCWorkingSet, f: F) -> T -where - F: FnOnce(&WorkingSet) -> T, -{ - // SAFETY: - // - ws is not null (promised by caller) - // - ws outlives 'a (promised by caller) - let tcws: &TCWorkingSet = unsafe { TCWorkingSet::from_ptr_arg_ref(ws) }; - f(&tcws.0) -} - -#[ffizz_header::item] -#[ffizz(order = 1101)] -/// Get the working set's length, or the number of UUIDs it contains. -/// -/// ```c -/// EXTERN_C size_t tc_working_set_len(struct TCWorkingSet *ws); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_working_set_len(ws: *mut TCWorkingSet) -> usize { - wrap(ws, |ws| ws.len()) -} - -#[ffizz_header::item] -#[ffizz(order = 1101)] -/// Get the working set's largest index. -/// -/// ```c -/// EXTERN_C size_t tc_working_set_largest_index(struct TCWorkingSet *ws); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_working_set_largest_index(ws: *mut TCWorkingSet) -> usize { - wrap(ws, |ws| ws.largest_index()) -} - -#[ffizz_header::item] -#[ffizz(order = 1101)] -/// Get the UUID for the task at the given index. Returns true if the UUID exists in the working -/// set. If not, returns false and does not change uuid_out. -/// -/// ```c -/// EXTERN_C bool tc_working_set_by_index(struct TCWorkingSet *ws, size_t index, struct TCUuid *uuid_out); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_working_set_by_index( - ws: *mut TCWorkingSet, - index: usize, - uuid_out: *mut TCUuid, -) -> bool { - debug_assert!(!uuid_out.is_null()); - wrap(ws, |ws| { - if let Some(uuid) = ws.by_index(index) { - // SAFETY: - // - uuid_out is not NULL (promised by caller) - // - alignment is not required - unsafe { TCUuid::val_to_arg_out(uuid, uuid_out) }; - true - } else { - false - } - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1101)] -/// Get the working set index for the task with the given UUID. Returns 0 if the task is not in -/// the working set. -/// -/// ```c -/// EXTERN_C size_t tc_working_set_by_uuid(struct TCWorkingSet *ws, struct TCUuid uuid); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_working_set_by_uuid(ws: *mut TCWorkingSet, uuid: TCUuid) -> usize { - wrap(ws, |ws| { - // SAFETY: - // - tcuuid is a valid TCUuid (all byte patterns are valid) - let uuid: Uuid = unsafe { TCUuid::val_from_arg(uuid) }; - ws.by_uuid(uuid).unwrap_or(0) - }) -} - -#[ffizz_header::item] -#[ffizz(order = 1102)] -/// Free a TCWorkingSet. The given value must not be NULL. The value must not be used after this -/// function returns, and must not be freed more than once. -/// -/// ```c -/// EXTERN_C void tc_working_set_free(struct TCWorkingSet *ws); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn tc_working_set_free(ws: *mut TCWorkingSet) { - // SAFETY: - // - rep is not NULL (promised by caller) - // - caller will not use the TCWorkingSet after this (promised by caller) - let ws = unsafe { TCWorkingSet::take_from_ptr_arg(ws) }; - drop(ws); -} diff --git a/src/tc/lib/taskchampion.h b/src/tc/lib/taskchampion.h deleted file mode 100644 index 3d0cf5e9a..000000000 --- a/src/tc/lib/taskchampion.h +++ /dev/null @@ -1,937 +0,0 @@ -// TaskChampion -// -// This file defines the C interface to libtaskchampion. This is a thin wrapper around the Rust -// `taskchampion` crate. Refer to the documentation for that crate at -// https://docs.rs/taskchampion/latest/taskchampion/ for API details. The comments in this file -// focus mostly on the low-level details of passing values to and from TaskChampion. -// -// # Overview -// -// This library defines four major types used to interact with the API, that map directly to Rust -// types. -// -// * TCReplica - see https://docs.rs/taskchampion/latest/taskchampion/struct.Replica.html -// * TCTask - see https://docs.rs/taskchampion/latest/taskchampion/struct.Task.html -// * TCServer - see https://docs.rs/taskchampion/latest/taskchampion/trait.Server.html -// * TCWorkingSet - see https://docs.rs/taskchampion/latest/taskchampion/struct.WorkingSet.html -// -// It also defines a few utility types: -// -// * TCString - a wrapper around both C (NUL-terminated) and Rust (always utf-8) strings. -// * TC…List - a list of objects represented as a C array -// * see below for the remainder -// -// # Safety -// -// Each type contains specific instructions to ensure memory safety. The general rules are as -// follows. -// -// No types in this library are threadsafe. All values should be used in only one thread for their -// entire lifetime. It is safe to use unrelated values in different threads (for example, -// different threads may use different TCReplica values concurrently). -// -// ## Pass by Pointer -// -// Several types such as TCReplica and TCString are "opaque" types and always handled as pointers -// in C. The bytes these pointers address are private to the Rust implementation and must not be -// accessed from C. -// -// Pass-by-pointer values have exactly one owner, and that owner is responsible for freeing the -// value (using a `tc_…_free` function), or transferring ownership elsewhere. Except where -// documented otherwise, when a value is passed to C, ownership passes to C as well. When a value -// is passed to Rust, ownership stays with the C code. The exception is TCString, ownership of -// which passes to Rust when it is used as a function argument. -// -// The limited circumstances where one value must not outlive another, due to pointer references -// between them, are documented below. -// -// ## Pass by Value -// -// Types such as TCUuid and TC…List are passed by value, and contain fields that are accessible -// from C. C code is free to access the content of these types in a _read_only_ fashion. -// -// Pass-by-value values that contain pointers also have exactly one owner, responsible for freeing -// the value or transferring ownership. The tc_…_free functions for these types will replace the -// pointers with NULL to guard against use-after-free errors. The interior pointers in such values -// should never be freed directly (for example, `tc_string_free(tcuda.value)` is an error). -// -// TCUuid is a special case, because it does not contain pointers. It can be freely copied and -// need not be freed. -// -// ## Lists -// -// Lists are a special kind of pass-by-value type. Each contains `len` and `items`, where `items` -// is an array of length `len`. Lists, and the values in the `items` array, must be treated as -// read-only. On return from an API function, a list's ownership is with the C caller, which must -// eventually free the list. List data must be freed with the `tc_…_list_free` function. It is an -// error to free any value in the `items` array of a list. - -#ifndef TASKCHAMPION_H -#define TASKCHAMPION_H - -#include -#include -#include - -#ifdef __cplusplus -#define EXTERN_C extern "C" -#else -#define EXTERN_C -#endif // __cplusplus - -// ***** TCResult ***** -// -// A result from a TC operation. Typically if this value is TC_RESULT_ERROR, -// the associated object's `tc_.._error` method will return an error message. -enum TCResult -#ifdef __cplusplus - : int32_t -#endif // __cplusplus -{ - TC_RESULT_ERROR = -1, - TC_RESULT_OK = 0, -}; -#ifndef __cplusplus -typedef int32_t TCResult; -#endif // __cplusplus - -// ***** TCString ***** -// -// TCString supports passing strings into and out of the TaskChampion API. -// -// # Rust Strings and C Strings -// -// A Rust string can contain embedded NUL characters, while C considers such a character to mark -// the end of a string. Strings containing embedded NULs cannot be represented as a "C string" -// and must be accessed using `tc_string_content_and_len` and `tc_string_clone_with_len`. In -// general, these two functions should be used for handling arbitrary data, while more convenient -// forms may be used where embedded NUL characters are impossible, such as in static strings. -// -// # UTF-8 -// -// TaskChampion expects all strings to be valid UTF-8. `tc_string_…` functions will fail if given -// a `*TCString` containing invalid UTF-8. -// -// # Safety -// -// The `ptr` field may be checked for NULL, where documentation indicates this is possible. All -// other fields in a TCString are private and must not be used from C. They exist in the struct -// to ensure proper allocation and alignment. -// -// When a `TCString` appears as a return value or output argument, ownership is passed to the -// caller. The caller must pass that ownership back to another function or free the string. -// -// Any function taking a `TCString` requires: -// - the pointer must not be NUL; -// - the pointer must be one previously returned from a tc_… function; and -// - the memory referenced by the pointer must never be modified by C code. -// -// Unless specified otherwise, TaskChampion functions take ownership of a `TCString` when it is -// given as a function argument, and the caller must not use or free TCStrings after passing them -// to such API functions. -// -// A TCString with a NULL `ptr` field need not be freed, although tc_free_string will not fail -// for such a value. -// -// TCString is not threadsafe. -typedef struct TCString { - void *ptr; // opaque, but may be checked for NULL - size_t _u1; // reserved - size_t _u2; // reserved - uint8_t _u3; // reserved -} TCString; - -// Create a new TCString referencing the given C string. The C string must remain valid and -// unchanged until after the TCString is freed. It's typically easiest to ensure this by using a -// static string. -// -// NOTE: this function does _not_ take responsibility for freeing the given C string. The -// given string can be freed once the TCString referencing it has been freed. -// -// For example: -// -// ```text -// char *url = get_item_url(..); // dynamically allocate C string -// tc_task_annotate(task, tc_string_borrow(url)); // TCString created, passed, and freed -// free(url); // string is no longer referenced and can be freed -// ``` -EXTERN_C struct TCString tc_string_borrow(const char *cstr); - -// Create a new TCString by cloning the content of the given C string. The resulting TCString -// is independent of the given string, which can be freed or overwritten immediately. -EXTERN_C struct TCString tc_string_clone(const char *cstr); - -// Create a new TCString containing the given string with the given length. This allows creation -// of strings containing embedded NUL characters. As with `tc_string_clone`, the resulting -// TCString is independent of the passed buffer, which may be reused or freed immediately. -// -// The length should _not_ include any trailing NUL. -// -// The given length must be less than half the maximum value of usize. -EXTERN_C struct TCString tc_string_clone_with_len(const char *buf, size_t len); - -// Get the content of the string as a regular C string. The given string must be valid. The -// returned value is NULL if the string contains NUL bytes or (in some cases) invalid UTF-8. The -// returned C string is valid until the TCString is freed or passed to another TC API function. -// -// In general, prefer [`tc_string_content_with_len`] except when it's certain that the string is -// valid and NUL-free. -// -// This function takes the TCString by pointer because it may be modified in-place to add a NUL -// terminator. The pointer must not be NULL. -// -// This function does _not_ take ownership of the TCString. -EXTERN_C const char *tc_string_content(const struct TCString *tcstring); - -// Get the content of the string as a pointer and length. The given string must not be NULL. -// This function can return any string, even one including NUL bytes or invalid UTF-8. The -// returned buffer is valid until the TCString is freed or passed to another TaskChampio -// function. -// -// This function takes the TCString by pointer because it may be modified in-place to add a NUL -// terminator. The pointer must not be NULL. -// -// This function does _not_ take ownership of the TCString. -EXTERN_C const char *tc_string_content_with_len(const struct TCString *tcstring, size_t *len_out); - -// Free a TCString. The given string must not be NULL. The string must not be used -// after this function returns, and must not be freed more than once. -EXTERN_C void tc_string_free(struct TCString *tcstring); - -// ***** TCStringList ***** -// -// TCStringList represents a list of strings. -// -// The content of this struct must be treated as read-only. -typedef struct TCStringList { - // number of strings in items - size_t len; - // reserved - size_t _u1; - // TCStringList representing each string. These remain owned by the TCStringList instance and will - // be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the - // *TCStringList at indexes 0..len-1 are not NULL. - struct TCString *items; -} TCStringList; - -// Free a TCStringList instance. The instance, and all TCStringList it contains, must not be used -// after this call. -// -// When this call returns, the `items` pointer will be NULL, signalling an invalid TCStringList. -EXTERN_C void tc_string_list_free(struct TCStringList *tcstrings); - -// ***** TCUuid ***** -// -// TCUuid is used as a task identifier. Uuids do not contain any pointers and need not be freed. -// Uuids are typically treated as opaque, but the bytes are available in big-endian format. -typedef struct TCUuid { - uint8_t bytes[16]; -} TCUuid; - -// Length, in bytes, of the string representation of a UUID (without NUL terminator) -#define TC_UUID_STRING_BYTES 36 - -// Parse the given string as a UUID. Returns TC_RESULT_ERROR on parse failure or if the given -// string is not valid. -EXTERN_C TCResult tc_uuid_from_str(struct TCString s, struct TCUuid *uuid_out); - -// Create a new, randomly-generated UUID. -EXTERN_C struct TCUuid tc_uuid_new_v4(void); - -// Create a new UUID with the nil value. -EXTERN_C struct TCUuid tc_uuid_nil(void); - -// Write the string representation of a TCUuid into the given buffer, which must be -// at least TC_UUID_STRING_BYTES long. No NUL terminator is added. -EXTERN_C void tc_uuid_to_buf(struct TCUuid tcuuid, char *buf); - -// Return the hyphenated string representation of a TCUuid. The returned string -// must be freed with tc_string_free. -EXTERN_C struct TCString tc_uuid_to_str(struct TCUuid tcuuid); - -// TCUuidList represents a list of uuids. -// -// The content of this struct must be treated as read-only. -typedef struct TCUuidList { - // number of uuids in items - size_t len; - // reserved - size_t _u1; - // Array of uuids. This pointer is never NULL for a valid TCUuidList. - struct TCUuid *items; -} TCUuidList; - -// Free a TCUuidList instance. The instance, and all TCUuids it contains, must not be used after -// this call. -// -// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUuidList. -EXTERN_C void tc_uuid_list_free(struct TCUuidList *tcuuids); - -// ***** TCAnnotation ***** -// -// TCAnnotation contains the details of an annotation. -// -// # Safety -// -// An annotation must be initialized from a tc_.. function, and later freed -// with `tc_annotation_free` or `tc_annotation_list_free`. -// -// Any function taking a `*TCAnnotation` requires: -// - the pointer must not be NUL; -// - the pointer must be one previously returned from a tc_… function; -// - the memory referenced by the pointer must never be modified by C code; and -// - ownership transfers to the called function, and the value must not be used -// after the call returns. In fact, the value will be zeroed out to ensure this. -// -// TCAnnotations are not threadsafe. -typedef struct TCAnnotation { - // Time the annotation was made. Must be nonzero. - time_t entry; - // Content of the annotation. Must not be NULL. - TCString description; -} TCAnnotation; - -// Free a TCAnnotation instance. The instance, and the TCString it contains, must not be used -// after this call. -EXTERN_C void tc_annotation_free(struct TCAnnotation *tcann); - -// ***** TCAnnotationList ***** -// -// TCAnnotationList represents a list of annotations. -// -// The content of this struct must be treated as read-only. -typedef struct TCAnnotationList { - // number of annotations in items - size_t len; - // reserved - size_t _u1; - // Array of annotations. These remain owned by the TCAnnotationList instance and will be freed by - // tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList. - struct TCAnnotation *items; -} TCAnnotationList; - -// Free a TCAnnotationList instance. The instance, and all TCAnnotations it contains, must not be -// used after this call. -// -// When this call returns, the `items` pointer will be NULL, signalling an invalid TCAnnotationList. -EXTERN_C void tc_annotation_list_free(struct TCAnnotationList *tcanns); - -// ***** TCUda ***** -// -// TCUda contains the details of a UDA. -typedef struct TCUda { - // Namespace of the UDA. For legacy UDAs, this may have a NULL ptr field. - struct TCString ns; - // UDA key. Must not be NULL. - struct TCString key; - // Content of the UDA. Must not be NULL. - struct TCString value; -} TCUda; - -// Free a TCUda instance. The instance, and the TCStrings it contains, must not be used -// after this call. -EXTERN_C void tc_uda_free(struct TCUda *tcuda); - -// ***** TCUdaList ***** -// -// TCUdaList represents a list of UDAs. -// -// The content of this struct must be treated as read-only. -typedef struct TCUdaList { - // number of UDAs in items - size_t len; - // reserved - size_t _u1; - // Array of UDAs. These remain owned by the TCUdaList instance and will be freed by - // tc_uda_list_free. This pointer is never NULL for a valid TCUdaList. - struct TCUda *items; -} TCUdaList; - -// Free a TCUdaList instance. The instance, and all TCUdas it contains, must not be used after -// this call. -// -// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUdaList. -EXTERN_C void tc_uda_list_free(struct TCUdaList *tcudas); - -// ***** TCKV ***** -// -// TCKV contains a key/value pair that is part of a task. -// -// Neither key nor value are ever NULL. They remain owned by the TCKV and -// will be freed when it is freed with tc_kv_list_free. -typedef struct TCKV { - struct TCString key; - struct TCString value; -} TCKV; - -// ***** TCKVList ***** -// -// TCKVList represents a list of key/value pairs. -// -// The content of this struct must be treated as read-only. -typedef struct TCKVList { - // number of key/value pairs in items - size_t len; - // reserved - size_t _u1; - // Array of TCKV's. These remain owned by the TCKVList instance and will be freed by - // tc_kv_list_free. This pointer is never NULL for a valid TCKVList. - struct TCKV *items; -} TCKVList; - -// Free a TCKVList instance. The instance, and all TCKVs it contains, must not be used after -// this call. -// -// When this call returns, the `items` pointer will be NULL, signalling an invalid TCKVList. -EXTERN_C void tc_kv_list_free(struct TCKVList *tckvs); - -// ***** TCStatus ***** -// -// The status of a task, as defined by the task data model. -#ifdef __cplusplus -typedef enum TCStatus : int32_t { -#else // __cplusplus -typedef int32_t TCStatus; -enum TCStatus { -#endif // __cplusplus - TC_STATUS_PENDING = 0, - TC_STATUS_COMPLETED = 1, - TC_STATUS_DELETED = 2, - TC_STATUS_RECURRING = 3, - // Unknown signifies a status in the task DB that was not - // recognized. - TC_STATUS_UNKNOWN = -1, -#ifdef __cplusplus -} TCStatus; -#else // __cplusplus -}; -#endif // __cplusplus - -// ***** TCServer ***** -// -// TCServer represents an interface to a sync server. Aside from new and free, a server -// has no C-accessible API, but is designed to be passed to `tc_replica_sync`. -// -// ## Safety -// -// TCServer are not threadsafe, and must not be used with multiple replicas simultaneously. -typedef struct TCServer TCServer; - -// Create a new TCServer that operates locally (on-disk). See the TaskChampion docs for the -// description of the arguments. -// -// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is -// returned. The caller must free this string. -// -// The server must be freed after it is used - tc_replica_sync does not automatically free it. -EXTERN_C struct TCServer *tc_server_new_local(struct TCString server_dir, - struct TCString *error_out); - -// Create a new TCServer that connects to a remote server. See the TaskChampion docs for the -// description of the arguments. -// -// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is -// returned. The caller must free this string. -// -// The server must be freed after it is used - tc_replica_sync does not automatically free it. -EXTERN_C struct TCServer *tc_server_new_sync(struct TCString url, struct TCUuid client_id, - struct TCString encryption_secret, - struct TCString *error_out); - -// Create a new TCServer that connects to the Google Cloud Platform. See the TaskChampion docs -// for the description of the arguments. -// -// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is -// returned. The caller must free this string. -// -// The server must be freed after it is used - tc_replica_sync does not automatically free it. -EXTERN_C struct TCServer *tc_server_new_gcp(struct TCString bucket, struct TCString credential_path, - struct TCString encryption_secret, - struct TCString *error_out); - -// Free a server. The server may not be used after this function returns and must not be freed -// more than once. -EXTERN_C void tc_server_free(struct TCServer *server); - -// ***** TCReplica ***** -// -// A replica represents an instance of a user's task data, providing an easy interface -// for querying and modifying that data. -// -// # Error Handling -// -// When a `tc_replica_..` function that returns a TCResult returns TC_RESULT_ERROR, then -// `tc_replica_error` will return the error message. -// -// # Safety -// -// The `*TCReplica` returned from `tc_replica_new…` functions is owned by the caller and -// must later be freed to avoid a memory leak. -// -// Any function taking a `*TCReplica` requires: -// - the pointer must not be NUL; -// - the pointer must be one previously returned from a tc_… function; -// - the memory referenced by the pointer must never be modified by C code; and -// - except for `tc_replica_free`, ownership of a `*TCReplica` remains with the caller. -// -// Once passed to `tc_replica_free`, a `*TCReplica` becomes invalid and must not be used again. -// -// TCReplicas are not threadsafe. -typedef struct TCReplica TCReplica; - -// ***** TCReplicaOpType ***** -enum TCReplicaOpType -#ifdef __cplusplus - : uint32_t -#endif // __cplusplus -{ - Create = 0, - Delete = 1, - Update = 2, - UndoPoint = 3, -}; -#ifndef __cplusplus -typedef uint32_t TCReplicaOpType; -#endif // __cplusplus - -// ***** TCReplicaOp ***** -struct TCReplicaOp { - TCReplicaOpType operation_type; - void *inner; -}; - -typedef struct TCReplicaOp TCReplicaOp; - -// ***** TCReplicaOpList ***** -struct TCReplicaOpList { - struct TCReplicaOp *items; - size_t len; - size_t capacity; -}; - -typedef struct TCReplicaOpList TCReplicaOpList; - -// Create a new TCReplica with an in-memory database. The contents of the database will be -// lost when it is freed with tc_replica_free. -EXTERN_C struct TCReplica *tc_replica_new_in_memory(void); - -// Create a new TCReplica with an on-disk database having the given filename. On error, a string -// is written to the error_out parameter (if it is not NULL) and NULL is returned. The caller -// must free this string. -EXTERN_C struct TCReplica *tc_replica_new_on_disk(struct TCString path, bool create_if_missing, - struct TCString *error_out); - -// Add an UndoPoint, if one has not already been added by this Replica. This occurs automatically -// when a change is made. The `force` flag allows forcing a new UndoPoint even if one has already -// been created by this Replica, and may be useful when a Replica instance is held for a long time -// and used to apply more than one user-visible change. -EXTERN_C TCResult tc_replica_add_undo_point(struct TCReplica *rep, bool force); - -// Get a list of all uuids for tasks in the replica. -// -// Returns a TCUuidList with a NULL items field on error. -// -// The caller must free the UUID list with `tc_uuid_list_free`. -EXTERN_C struct TCUuidList tc_replica_all_task_uuids(struct TCReplica *rep); - -// Get a list of all tasks in the replica. -// -// Returns a TCTaskList with a NULL items field on error. -EXTERN_C struct TCTaskList tc_replica_all_tasks(struct TCReplica *rep); - -// Undo local operations in storage. -// -// If undone_out is not NULL, then on success it is set to 1 if operations were undone, or 0 if -// there are no operations that can be done. -EXTERN_C TCResult tc_replica_commit_undo_ops(struct TCReplica *rep, TCReplicaOpList tc_undo_ops, - int32_t *undone_out); - -// Delete a task. The task must exist. Note that this is different from setting status to -// Deleted; this is the final purge of the task. -// -// Deletion may interact poorly with modifications to the same task on other replicas. For -// example, if a task is deleted on replica 1 and its description modified on replica 1, then -// after both replicas have fully synced, the resulting task will only have a `description` -// property. -EXTERN_C TCResult tc_replica_delete_task(struct TCReplica *rep, struct TCUuid tcuuid); - -// Get the latest error for a replica, or a string with NULL ptr if no error exists. Subsequent -// calls to this function will return NULL. The rep pointer must not be NULL. The caller must -// free the returned string. -EXTERN_C struct TCString tc_replica_error(struct TCReplica *rep); - -// Expire old, deleted tasks. -// -// Expiration entails removal of tasks from the replica. Any modifications that occur after -// the deletion (such as operations synchronized from other replicas) will do nothing. -// -// Tasks are eligible for expiration when they have status Deleted and have not been modified -// for 180 days (about six months). Note that completed tasks are not eligible. -EXTERN_C TCResult tc_replica_expire_tasks(struct TCReplica *rep); - -// Get an existing task by its UUID. -// -// Returns NULL when the task does not exist, and on error. Consult tc_replica_error -// to distinguish the two conditions. -EXTERN_C struct TCTask *tc_replica_get_task(struct TCReplica *rep, struct TCUuid tcuuid); - -// Return undo local operations until the most recent UndoPoint. -EXTERN_C TCReplicaOpList tc_replica_get_undo_ops(struct TCReplica *rep); - -// Create a new task. The task must not already exist. -// -// Returns the task, or NULL on error. -EXTERN_C struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, - struct TCUuid tcuuid); - -// Create a new task. The task must not already exist. -// -// Returns the task, or NULL on error. -EXTERN_C struct TCTask *tc_replica_new_task(struct TCReplica *rep, enum TCStatus status, - struct TCString description); - -// Get the number of local, un-synchronized operations (not including undo points), or -1 on -// error. -EXTERN_C int64_t tc_replica_num_local_operations(struct TCReplica *rep); - -// Get the number of undo points (number of undo calls possible), or -1 on error. -EXTERN_C int64_t tc_replica_num_undo_points(struct TCReplica *rep); - -// Rebuild this replica's working set, based on whether tasks are pending or not. If `renumber` -// is true, then existing tasks may be moved to new working-set indices; in any case, on -// completion all pending tasks are in the working set and all non- pending tasks are not. -EXTERN_C TCResult tc_replica_rebuild_working_set(struct TCReplica *rep, bool renumber); - -// Synchronize this replica with a server. -// -// The `server` argument remains owned by the caller, and must be freed explicitly. -EXTERN_C TCResult tc_replica_sync(struct TCReplica *rep, struct TCServer *server, - bool avoid_snapshots); - -// Get the current working set for this replica. The resulting value must be freed -// with tc_working_set_free. -// -// Returns NULL on error. -EXTERN_C struct TCWorkingSet *tc_replica_working_set(struct TCReplica *rep); - -// Free a replica. The replica may not be used after this function returns and must not be freed -// more than once. -EXTERN_C void tc_replica_free(struct TCReplica *rep); - -// Return description field of old task field of ReplicaOp. -EXTERN_C struct TCString tc_replica_op_get_old_task_description(struct TCReplicaOp *op); - -// Return old value field of ReplicaOp. -EXTERN_C struct TCString tc_replica_op_get_old_value(struct TCReplicaOp *op); - -// Return property field of ReplicaOp. -EXTERN_C struct TCString tc_replica_op_get_property(struct TCReplicaOp *op); - -// Return timestamp field of ReplicaOp. -EXTERN_C struct TCString tc_replica_op_get_timestamp(struct TCReplicaOp *op); - -// Return uuid field of ReplicaOp. -EXTERN_C struct TCString tc_replica_op_get_uuid(struct TCReplicaOp *op); - -// Return value field of ReplicaOp. -EXTERN_C struct TCString tc_replica_op_get_value(struct TCReplicaOp *op); - -// Free a vector of ReplicaOp. The vector may not be used after this function returns and must not -// be freed more than once. -EXTERN_C void tc_replica_op_list_free(struct TCReplicaOpList *oplist); - -// ***** TCTask ***** -// -// A task, as publicly exposed by this library. -// -// A task begins in "immutable" mode. It must be converted to "mutable" mode -// to make any changes, and doing so requires exclusive access to the replica -// until the task is freed or converted back to immutable mode. -// -// An immutable task carries no reference to the replica that created it, and can be used until it -// is freed or converted to a TaskMut. A mutable task carries a reference to the replica and -// must be freed or made immutable before the replica is freed. -// -// All `tc_task_..` functions taking a task as an argument require that it not be NULL. -// -// When a `tc_task_..` function that returns a TCResult returns TC_RESULT_ERROR, then -// `tc_task_error` will return the error message. -// -// # Safety -// -// A task is an owned object, and must be freed with tc_task_free (or, if part of a list, -// with tc_task_list_free). -// -// Any function taking a `*TCTask` requires: -// - the pointer must not be NUL; -// - the pointer must be one previously returned from a tc_… function; -// - the memory referenced by the pointer must never be modified by C code; and -// - except for `tc_{task,task_list}_free`, ownership of a `*TCTask` remains with the caller. -// -// Once passed to tc_task_free, a `*TCTask` becomes invalid and must not be used again. -// -// TCTasks are not threadsafe. -typedef struct TCTask TCTask; - -// Get the annotations for the task. -// -// The caller must free the returned TCAnnotationList instance. The TCStringList instance does not -// reference the task and the two may be freed in any order. -EXTERN_C struct TCAnnotationList tc_task_get_annotations(struct TCTask *task); - -// Get all dependencies for a task. -EXTERN_C struct TCUuidList tc_task_get_dependencies(struct TCTask *task); - -// Get a task's description. -EXTERN_C struct TCString tc_task_get_description(struct TCTask *task); - -// Get the entry timestamp for a task (when it was created), or 0 if not set. -EXTERN_C time_t tc_task_get_entry(struct TCTask *task); - -// Get the named legacy UDA from the task. -// -// Returns NULL if the UDA does not exist. -EXTERN_C struct TCString tc_task_get_legacy_uda(struct TCTask *task, struct TCString key); - -// Get all UDAs for this task. -// -// All TCUdas in this list have a NULL ns field. The entire UDA key is -// included in the key field. The caller must free the returned list. -EXTERN_C struct TCUdaList tc_task_get_legacy_udas(struct TCTask *task); - -// Get the modified timestamp for a task, or 0 if not set. -EXTERN_C time_t tc_task_get_modified(struct TCTask *task); - -// Get a task's status. -EXTERN_C enum TCStatus tc_task_get_status(struct TCTask *task); - -// Get the tags for the task. -// -// The caller must free the returned TCStringList instance. The TCStringList instance does not -// reference the task and the two may be freed in any order. -EXTERN_C struct TCStringList tc_task_get_tags(struct TCTask *task); - -// Get the underlying key/value pairs for this task. The returned TCKVList is -// a "snapshot" of the task and will not be updated if the task is subsequently -// modified. It is the caller's responsibility to free the TCKVList. -EXTERN_C struct TCKVList tc_task_get_taskmap(struct TCTask *task); - -// Get the named UDA from the task. -// -// Returns a TCString with NULL ptr field if the UDA does not exist. -EXTERN_C struct TCString tc_task_get_uda(struct TCTask *task, struct TCString ns, - struct TCString key); - -// Get all UDAs for this task. -// -// Legacy UDAs are represented with an empty string in the ns field. -EXTERN_C struct TCUdaList tc_task_get_udas(struct TCTask *task); - -// Get a task's UUID. -EXTERN_C struct TCUuid tc_task_get_uuid(struct TCTask *task); - -// Get a task property's value, or NULL if the task has no such property, (including if the -// property name is not valid utf-8). -EXTERN_C struct TCString tc_task_get_value(struct TCTask *task, struct TCString property); - -// Get the wait timestamp for a task, or 0 if not set. -EXTERN_C time_t tc_task_get_wait(struct TCTask *task); - -// Check if a task has the given tag. If the tag is invalid, this function will return false, as -// that (invalid) tag is not present. No error will be reported via `tc_task_error`. -EXTERN_C bool tc_task_has_tag(struct TCTask *task, struct TCString tag); - -// Check if a task is active (started and not stopped). -EXTERN_C bool tc_task_is_active(struct TCTask *task); - -// Check if a task is blocked (depends on at least one other task). -EXTERN_C bool tc_task_is_blocked(struct TCTask *task); - -// Check if a task is blocking (at least one other task depends on it). -EXTERN_C bool tc_task_is_blocking(struct TCTask *task); - -// Check if a task is waiting. -EXTERN_C bool tc_task_is_waiting(struct TCTask *task); - -// Convert an immutable task into a mutable task. -// -// The task must not be NULL. It is modified in-place, and becomes mutable. -// -// The replica must not be NULL. After this function returns, the replica _cannot be used at all_ -// until this task is made immutable again. This implies that it is not allowed for more than one -// task associated with a replica to be mutable at any time. -// -// Typically mutation of tasks is bracketed with `tc_task_to_mut` and `tc_task_to_immut`: -// -// ```text -// tc_task_to_mut(task, rep); -// success = tc_task_done(task); -// tc_task_to_immut(task, rep); -// if (!success) { ... } -// ``` -EXTERN_C void tc_task_to_mut(struct TCTask *task, struct TCReplica *tcreplica); - -// Add an annotation to a mutable task. This call takes ownership of the -// passed annotation, which must not be used after the call returns. -EXTERN_C TCResult tc_task_add_annotation(struct TCTask *task, struct TCAnnotation *annotation); - -// Add a dependency. -EXTERN_C TCResult tc_task_add_dependency(struct TCTask *task, struct TCUuid dep); - -// Add a tag to a mutable task. -EXTERN_C TCResult tc_task_add_tag(struct TCTask *task, struct TCString tag); - -// Mark a task as deleted. -EXTERN_C TCResult tc_task_delete(struct TCTask *task); - -// Mark a task as done. -EXTERN_C TCResult tc_task_done(struct TCTask *task); - -// Remove an annotation from a mutable task. -EXTERN_C TCResult tc_task_remove_annotation(struct TCTask *task, int64_t entry); - -// Remove a dependency. -EXTERN_C TCResult tc_task_remove_dependency(struct TCTask *task, struct TCUuid dep); - -// Remove a UDA fraom a mutable task. -EXTERN_C TCResult tc_task_remove_legacy_uda(struct TCTask *task, struct TCString key); - -// Remove a tag from a mutable task. -EXTERN_C TCResult tc_task_remove_tag(struct TCTask *task, struct TCString tag); - -// Remove a UDA fraom a mutable task. -EXTERN_C TCResult tc_task_remove_uda(struct TCTask *task, struct TCString ns, struct TCString key); - -// Set a mutable task's description. -EXTERN_C TCResult tc_task_set_description(struct TCTask *task, struct TCString description); - -// Set a mutable task's entry (creation time). Pass entry=0 to unset -// the entry field. -EXTERN_C TCResult tc_task_set_entry(struct TCTask *task, time_t entry); - -// Set a legacy UDA on a mutable task. -EXTERN_C TCResult tc_task_set_legacy_uda(struct TCTask *task, struct TCString key, - struct TCString value); - -// Set a mutable task's modified timestamp. The value cannot be zero. -EXTERN_C TCResult tc_task_set_modified(struct TCTask *task, time_t modified); - -// Set a mutable task's status. -EXTERN_C TCResult tc_task_set_status(struct TCTask *task, enum TCStatus status); - -// Set a UDA on a mutable task. -EXTERN_C TCResult tc_task_set_uda(struct TCTask *task, struct TCString ns, struct TCString key, - struct TCString value); - -// Set a mutable task's property value by name. If value.ptr is NULL, the property is removed. -EXTERN_C TCResult tc_task_set_value(struct TCTask *task, struct TCString property, - struct TCString value); - -// Set a mutable task's wait timestamp. Pass wait=0 to unset the wait field. -EXTERN_C TCResult tc_task_set_wait(struct TCTask *task, time_t wait); - -// Start a task. -EXTERN_C TCResult tc_task_start(struct TCTask *task); - -// Stop a task. -EXTERN_C TCResult tc_task_stop(struct TCTask *task); - -// Convert a mutable task into an immutable task. -// -// The task must not be NULL. It is modified in-place, and becomes immutable. -// -// The replica passed to `tc_task_to_mut` may be used freely after this call. -EXTERN_C void tc_task_to_immut(struct TCTask *task); - -// Get the latest error for a task, or a string NULL ptr field if the last operation succeeded. -// Subsequent calls to this function will return NULL. The task pointer must not be NULL. The -// caller must free the returned string. -EXTERN_C struct TCString tc_task_error(struct TCTask *task); - -// Free a task. The given task must not be NULL. The task must not be used after this function -// returns, and must not be freed more than once. -// -// If the task is currently mutable, it will first be made immutable. -EXTERN_C void tc_task_free(struct TCTask *task); - -// ***** TCTaskList ***** -// -// TCTaskList represents a list of tasks. -// -// The content of this struct must be treated as read-only: no fields or anything they reference -// should be modified directly by C code. -// -// When an item is taken from this list, its pointer in `items` is set to NULL. -typedef struct TCTaskList { - // number of tasks in items - size_t len; - // reserved - size_t _u1; - // Array of pointers representing each task. These remain owned by the TCTaskList instance and - // will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList. - // Pointers in the array may be NULL after `tc_task_list_take`. - struct TCTask **items; -} TCTaskList; - -// Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after -// this call. -// -// When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList. -EXTERN_C void tc_task_list_free(struct TCTaskList *tasks); - -// Take an item from a TCTaskList. After this call, the indexed item is no longer associated -// with the list and becomes the caller's responsibility, just as if it had been returned from -// `tc_replica_get_task`. -// -// The corresponding element in the `items` array will be set to NULL. If that field is already -// NULL (that is, if the item has already been taken), this function will return NULL. If the -// index is out of bounds, this function will also return NULL. -// -// The passed TCTaskList remains owned by the caller. -EXTERN_C struct TCTask *tc_task_list_take(struct TCTaskList *tasks, size_t index); - -// ***** TCWorkingSet ***** -// -// A TCWorkingSet represents a snapshot of the working set for a replica. It is not automatically -// updated based on changes in the replica. Its lifetime is independent of the replica and it can -// be freed at any time. -// -// To iterate over a working set, search indexes 1 through largest_index. -// -// # Safety -// -// The `*TCWorkingSet` returned from `tc_replica_working_set` is owned by the caller and -// must later be freed to avoid a memory leak. Its lifetime is independent of the replica -// from which it was generated. -// -// Any function taking a `*TCWorkingSet` requires: -// - the pointer must not be NUL; -// - the pointer must be one previously returned from `tc_replica_working_set` -// - the memory referenced by the pointer must never be accessed by C code; and -// - except for `tc_replica_free`, ownership of a `*TCWorkingSet` remains with the caller. -// -// Once passed to `tc_replica_free`, a `*TCWorkingSet` becomes invalid and must not be used again. -// -// TCWorkingSet is not threadsafe. -typedef struct TCWorkingSet TCWorkingSet; - -// Get the UUID for the task at the given index. Returns true if the UUID exists in the working -// set. If not, returns false and does not change uuid_out. -EXTERN_C bool tc_working_set_by_index(struct TCWorkingSet *ws, size_t index, - struct TCUuid *uuid_out); - -// Get the working set index for the task with the given UUID. Returns 0 if the task is not in -// the working set. -EXTERN_C size_t tc_working_set_by_uuid(struct TCWorkingSet *ws, struct TCUuid uuid); - -// Get the working set's largest index. -EXTERN_C size_t tc_working_set_largest_index(struct TCWorkingSet *ws); - -// Get the working set's length, or the number of UUIDs it contains. -EXTERN_C size_t tc_working_set_len(struct TCWorkingSet *ws); - -// Free a TCWorkingSet. The given value must not be NULL. The value must not be used after this -// function returns, and must not be freed more than once. -EXTERN_C void tc_working_set_free(struct TCWorkingSet *ws); - -#endif /* TASKCHAMPION_H */ diff --git a/src/tc/util.cpp b/src/tc/util.cpp deleted file mode 100644 index 3339615ce..000000000 --- a/src/tc/util.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#include -// cmake.h include header must come first - -#include -#include - -#include "tc/Replica.h" -#include "tc/Task.h" - -using namespace tc::ffi; - -namespace tc { -//////////////////////////////////////////////////////////////////////////////// -TCString string2tc(const std::string& str) { - return tc_string_clone_with_len(str.data(), str.size()); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc2string_clone(const TCString& str) { - size_t len; - auto ptr = tc_string_content_with_len(&str, &len); - auto rv = std::string(ptr, len); - return rv; -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc2string(TCString& str) { - auto rv = tc2string_clone(str); - tc_string_free(&str); - return rv; -} - -//////////////////////////////////////////////////////////////////////////////// -TCUuid uuid2tc(const std::string& str) { - TCString tcstr = tc_string_borrow(str.c_str()); - TCUuid rv; - if (TC_RESULT_OK != tc_uuid_from_str(tcstr, &rv)) { - throw std::string("invalid UUID"); - } - return rv; -} - -//////////////////////////////////////////////////////////////////////////////// -std::string tc2uuid(TCUuid& uuid) { - char s[TC_UUID_STRING_BYTES]; - tc_uuid_to_buf(uuid, s); - std::string str; - str.assign(s, TC_UUID_STRING_BYTES); - return str; -} - -//////////////////////////////////////////////////////////////////////////////// -} // namespace tc diff --git a/src/tc/util.h b/src/tc/util.h deleted file mode 100644 index 87d54cd16..000000000 --- a/src/tc/util.h +++ /dev/null @@ -1,52 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022, Dustin J. Mitchell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// https://www.opensource.org/licenses/mit-license.php -// -//////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDED_TC_UTIL -#define INCLUDED_TC_UTIL - -#include - -#include "tc/ffi.h" - -namespace tc { -// convert a std::string into a TCString, copying the contained data -tc::ffi::TCString string2tc(const std::string&); - -// convert a TCString into a std::string, leaving the TCString as-is -std::string tc2string_clone(const tc::ffi::TCString&); - -// convert a TCString into a std::string, freeing the TCString -std::string tc2string(tc::ffi::TCString&); - -// convert a TCUuid into a std::string -std::string tc2uuid(tc::ffi::TCUuid&); - -// parse a std::string into a TCUuid (throwing if parse fails) -tc::ffi::TCUuid uuid2tc(const std::string&); -} // namespace tc - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5af30aba0..d8de86d9f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,7 +2,6 @@ cmake_minimum_required (VERSION 3.22) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/src/tc/lib ${CMAKE_SOURCE_DIR}/src/commands ${CMAKE_SOURCE_DIR}/src/columns ${CMAKE_SOURCE_DIR}/src/libshared/src @@ -48,7 +47,7 @@ add_custom_target (build_tests DEPENDS ${test_SRCS} foreach (src_FILE ${test_SRCS}) add_executable (${src_FILE} ${src_FILE} test.cpp) - target_link_libraries (${src_FILE} task tc commands columns libshared task tc commands columns libshared task commands columns libshared ${TASK_LIBRARIES}) + target_link_libraries (${src_FILE} task commands columns libshared task commands columns libshared task commands columns libshared ${TASK_LIBRARIES}) add_dependencies (${src_FILE} task_executable) if (DARWIN) target_link_libraries (${src_FILE} "-framework CoreFoundation -framework Security -framework SystemConfiguration") diff --git a/test/feature.559.test.py b/test/feature.559.test.py index c0c5afa8f..f637e104e 100755 --- a/test/feature.559.test.py +++ b/test/feature.559.test.py @@ -69,7 +69,11 @@ class TestFeature559(TestCase): self.assertNotIn("footask", out) self.assertNotIn("Error", out) self.assertRegex( - err, re.compile("Could not.+unable to open database file", re.DOTALL) + err, + re.compile( + "unable to open database file:.*Unable to open the database file", + re.DOTALL, + ), ) diff --git a/test/tc.test.cpp b/test/tc.test.cpp index e3351e101..747c8e628 100644 --- a/test/tc.test.cpp +++ b/test/tc.test.cpp @@ -27,89 +27,130 @@ #include // cmake.h include header must come first +#include #include +#include +#include #include #include -#include "tc/Replica.h" -#include "tc/Task.h" -#include "tc/WorkingSet.h" -#include "tc/util.h" -#include "test.h" +std::string uuid2str(tc::Uuid uuid) { return static_cast(uuid.to_string()); } //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { - UnitTest t(23); +// Tests for the basic cxxbridge functionality. This focuses on the methods with +// complex cxxbridge implementations, rather than those with complex Rust +// implementations but simple APIs, like sync. +int main(int, char **) { + UnitTest t; + std::string str; - // This function contains unit tests for the various bits of the wrappers for - // taskchampion-lib (that is, for `src/tc/*.cpp`). + auto replica = tc::new_replica_in_memory(); + auto uuid = tc::uuid_v4(); + auto uuid2 = tc::uuid_v4(); + t.is(uuid2str(uuid).size(), (size_t)36, "uuid string is the right length"); - //// util + rust::Vec ops; + auto task = tc::create_task(uuid, ops); + t.is(uuid2str(task->get_uuid()), uuid2str(uuid), "new task has correct uuid"); + task->update("status", "pending", ops); + task->update("description", "a task", ops); + task->update("description", "a cool task", ops); + tc::add_undo_point(ops); + task->delete_task(ops); - { - auto s1 = std::string("a\0string!"); - auto stc = tc::string2tc(s1); - auto s2 = tc::tc2string(stc); - t.is(s1, s2, "round-trip to tc string and back (containing an embedded NUL)"); + t.is(ops[0].is_create(), true, "ops[0] is create"); + t.is(uuid2str(ops[0].get_uuid()), uuid2str(uuid), "ops[0] has correct uuid"); + + t.is(ops[1].is_update(), true, "ops[1] is update"); + t.is(uuid2str(ops[1].get_uuid()), uuid2str(uuid), "ops[1] has correct uuid"); + ops[1].get_property(str); + t.is(str, "status", "ops[1] property is 'status'"); + t.ok(ops[1].get_value(str), "get_value succeeds"); + t.is(str, "pending", "ops[1] value is 'pending'"); + t.ok(!ops[1].get_old_value(str), "get_old_value has no old value"); + + t.is(ops[2].is_update(), true, "ops[2] is update"); + t.is(uuid2str(ops[2].get_uuid()), uuid2str(uuid), "ops[2] has correct uuid"); + ops[2].get_property(str); + t.is(str, "description", "ops[2] property is 'description'"); + t.ok(ops[2].get_value(str), "get_value succeeds"); + t.is(str, "a task", "ops[2] value is 'a task'"); + t.ok(!ops[2].get_old_value(str), "get_old_value has no old value"); + + t.is(ops[3].is_update(), true, "ops[3] is update"); + t.is(uuid2str(ops[3].get_uuid()), uuid2str(uuid), "ops[3] has correct uuid"); + ops[3].get_property(str); + t.is(str, "description", "ops[3] property is 'description'"); + t.ok(ops[3].get_value(str), "get_value succeeds"); + t.is(str, "a cool task", "ops[3] value is 'a cool task'"); + t.ok(ops[3].get_old_value(str), "get_old_value succeeds"); + t.is(str, "a task", "ops[3] old value is 'a task'"); + + t.is(ops[4].is_undo_point(), true, "ops[4] is undo_point"); + + t.is(ops[5].is_delete(), true, "ops[5] is delete"); + t.is(uuid2str(ops[5].get_uuid()), uuid2str(uuid), "ops[5] has correct uuid"); + auto old_task = ops[5].get_old_task(); + // old_task is in arbitrary order, so just check that status is in there. + bool found = false; + for (auto &pv : old_task) { + std::string p = static_cast(pv.prop); + if (p == "status") { + std::string v = static_cast(pv.value); + t.is(v, "pending", "old_task has status:pending"); + found = true; + } + } + t.ok(found, "found the status property in ops[5].old_task"); + + replica->commit_operations(std::move(ops)); + auto maybe_task2 = replica->get_task_data(tc::uuid_v4()); + t.ok(maybe_task2.is_none(), "looking up a random uuid gets nothing"); + + // The last operation deleted the task, but we want to see the task, so undo it.. + auto undo_ops = replica->get_undo_operations(); + t.ok(replica->commit_reversed_operations(std::move(undo_ops)), "undo committed successfully"); + + auto maybe_task3 = replica->get_task_data(uuid); + t.ok(maybe_task3.is_some(), "looking up the original uuid get TaskData"); + rust::Box task3 = maybe_task3.take(); + t.is(uuid2str(task3->get_uuid()), uuid2str(uuid), "reloaded task has correct uuid"); + t.ok(task3->get("description", str), "reloaded task has a description"); + t.is(str, "a cool task", "reloaded task has correct description"); + t.ok(task3->get("status", str), "reloaded task has a status"); + t.is(str, "pending", "reloaded task has correct status"); + + t.is(task3->properties().size(), (size_t)2, "task has 2 properties"); + t.is(task3->items().size(), (size_t)2, "task has 2 items"); + + rust::Vec ops2; + auto task4 = tc::create_task(uuid2, ops2); + task4->update("description", "another", ops2); + replica->commit_operations(std::move(ops2)); + + auto all_tasks = replica->all_task_data(); + t.is(all_tasks.size(), (size_t)2, "now there are 2 tasks"); + for (auto &maybe_task : all_tasks) { + t.ok(maybe_task.is_some(), "all_tasks is fully populated"); + auto task = maybe_task.take(); + if (task->get_uuid() == uuid) { + t.ok(task->get("description", str), "get_value succeeds"); + t.is(str, "a cool task", "description is 'a cool task'"); + } } - { - auto s1 = std::string("62123ec9-c443-4f7e-919a-35362a8bef8d"); - auto tcuuid = tc::uuid2tc(s1); - auto s2 = tc::tc2uuid(tcuuid); - t.is(s1, s2, "round-trip to TCUuid and back"); + // Check exception formatting. + try { + replica->sync_to_local("/does/not/exist", false); + // tc::new_replica_on_disk("/does/not/exist", false); + } catch (rust::Error &err) { + t.is(err.what(), + "unable to open database file: /does/not/exist/taskchampion-local-sync-server.sqlite3: " + "Error code 14: Unable to open the database file", + "error message has full context"); } - //// Replica - - auto rep = tc::Replica(); - t.pass("replica constructed"); - - auto maybe_task = rep.get_task("24478a28-4609-4257-bc19-44ec51391431"); - t.notok(maybe_task.has_value(), "task with fixed uuid does not exist"); - - auto task = rep.new_task(tc::Status::Pending, "a test"); - t.pass("new task constructed"); - t.is(task.get_description(), std::string("a test"), "task description round-trip"); - t.is(task.get_status(), tc::Status::Pending, "task status round-trip"); - - auto uuid = task.get_uuid(); - - auto maybe_task2 = rep.get_task(uuid); - t.ok(maybe_task2.has_value(), "task lookup by uuid finds task"); - t.is((*maybe_task2).get_description(), std::string("a test"), "task description round-trip"); - - rep.rebuild_working_set(true); - t.pass("rebuild_working_set"); - - auto tasks = rep.all_tasks(); - t.is((int)tasks.size(), 1, "all_tasks returns one task"); - - //// Task - - task = std::move(tasks[0]); - - t.is(task.get_uuid(), uuid, "returned task has correct uuid"); - t.is(task.get_status(), tc::Status::Pending, "returned task is pending"); - auto map = task.get_taskmap(); - t.is(map["description"], "a test", "task description in taskmap"); - t.is(task.get_description(), "a test", "returned task has correct description"); - t.is(task.is_waiting(), false, "task is not waiting"); - t.is(task.is_active(), false, "task is not active"); - - //// WorkingSet - - auto ws = rep.working_set(); - - t.is(ws.len(), (size_t)1, "WorkingSet::len"); - t.is(ws.largest_index(), (size_t)1, "WorkingSet::largest_index"); - t.is(ws.by_index(1).value(), uuid, "WorkingSet::by_index"); - t.is(ws.by_index(2).has_value(), false, "WorkingSet::by_index for unknown index"); - t.is(ws.by_uuid(uuid).value(), (size_t)1, "WorkingSet::by_uuid"); - t.is(ws.by_uuid("3e18a306-e3a8-4a53-a85c-fa7c057759a2").has_value(), false, - "WorkingSet::by_uuid for unknown uuid"); - return 0; } diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml deleted file mode 100644 index ffcbbe838..000000000 --- a/xtask/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "xtask" -version = "0.4.1" -edition = "2021" - -[dependencies] -anyhow.workspace = true -taskchampion-lib = { path = "../src/tc/lib" } -regex.workspace = true diff --git a/xtask/src/main.rs b/xtask/src/main.rs deleted file mode 100644 index 003844ea2..000000000 --- a/xtask/src/main.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! This executable defines the `cargo xtask` subcommands. -//! -//! At the moment it is very simple, but if this grows more subcommands then -//! it will be sensible to use `clap` or another similar library. - -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::{Path, PathBuf}; - -pub fn main() -> anyhow::Result<()> { - let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); - let workspace_dir = manifest_dir.parent().unwrap(); - let arguments: Vec = env::args().collect(); - - if arguments.len() < 2 { - anyhow::bail!("xtask: Valid arguments are: `codegen`"); - } - - match arguments[1].as_str() { - "codegen" => codegen(workspace_dir), - _ => anyhow::bail!("xtask: unknown xtask"), - } -} - -/// `cargo xtask codegen` -/// -/// This uses ffizz-header to generate `lib/taskchampion.h`. -fn codegen(workspace_dir: &Path) -> anyhow::Result<()> { - let lib_crate_dir = workspace_dir.join("src/tc/lib"); - let mut file = File::create(lib_crate_dir.join("taskchampion.h")).unwrap(); - write!(&mut file, "{}", ::taskchampion_lib::generate_header()).unwrap(); - - Ok(()) -} From c719cce4f1af9595e07d39636f339288508e477c Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 11 Aug 2024 20:20:17 -0400 Subject: [PATCH 102/242] Run all C++ tests from a single executable (#3582) --- .github/workflows/tests.yaml | 2 +- Cargo.lock | 24 +++++ doc/devel/contrib/development.md | 4 +- test/CMakeLists.txt | 102 ++++++++++-------- test/README.md | 2 +- test/{col.test.cpp => col_test.cpp} | 2 +- test/docker/arch | 2 +- test/docker/debianstable | 2 +- test/docker/debiantesting | 2 +- test/docker/fedora39 | 2 +- test/docker/fedora40 | 2 +- test/docker/opensuse | 2 +- test/docker/ubuntu2004 | 2 +- test/docker/ubuntu2204 | 2 +- test/{dom.test.cpp => dom_test.cpp} | 6 +- test/{eval.test.cpp => eval_test.cpp} | 6 +- test/{lexer.test.cpp => lexer_test.cpp} | 2 +- test/{t.test.cpp => t_test.cpp} | 2 +- test/{tc.test.cpp => tc_cpp_test.cpp} | 2 +- test/{tdb2.test.cpp => tdb2_test.cpp} | 6 +- test/{tw-2689.test.cpp => tw_2689_test.cpp} | 2 +- test/{util.test.cpp => util_test.cpp} | 2 +- ...iant_add.test.cpp => variant_add_test.cpp} | 2 +- ...iant_and.test.cpp => variant_and_test.cpp} | 2 +- ...nt_cast.test.cpp => variant_cast_test.cpp} | 2 +- ...ivide.test.cpp => variant_divide_test.cpp} | 2 +- ..._equal.test.cpp => variant_equal_test.cpp} | 2 +- ...iant_exp.test.cpp => variant_exp_test.cpp} | 2 +- ...ariant_gt.test.cpp => variant_gt_test.cpp} | 2 +- ...iant_gte.test.cpp => variant_gte_test.cpp} | 2 +- ...qual.test.cpp => variant_inequal_test.cpp} | 2 +- ...ariant_lt.test.cpp => variant_lt_test.cpp} | 2 +- ...iant_lte.test.cpp => variant_lte_test.cpp} | 2 +- ..._match.test.cpp => variant_match_test.cpp} | 6 +- ...nt_math.test.cpp => variant_math_test.cpp} | 2 +- ...odulo.test.cpp => variant_modulo_test.cpp} | 2 +- ...ply.test.cpp => variant_multiply_test.cpp} | 2 +- ...atch.test.cpp => variant_nomatch_test.cpp} | 6 +- ...iant_not.test.cpp => variant_not_test.cpp} | 2 +- ...ariant_or.test.cpp => variant_or_test.cpp} | 2 +- ...tial.test.cpp => variant_partial_test.cpp} | 2 +- ...act.test.cpp => variant_subtract_test.cpp} | 2 +- ...iant_xor.test.cpp => variant_xor_test.cpp} | 2 +- test/{view.test.cpp => view_test.cpp} | 2 +- 44 files changed, 138 insertions(+), 94 deletions(-) rename test/{col.test.cpp => col_test.cpp} (99%) rename test/{dom.test.cpp => dom_test.cpp} (98%) rename test/{eval.test.cpp => eval_test.cpp} (99%) rename test/{lexer.test.cpp => lexer_test.cpp} (99%) rename test/{t.test.cpp => t_test.cpp} (99%) rename test/{tc.test.cpp => tc_cpp_test.cpp} (99%) rename test/{tdb2.test.cpp => tdb2_test.cpp} (98%) rename test/{tw-2689.test.cpp => tw_2689_test.cpp} (99%) rename test/{util.test.cpp => util_test.cpp} (99%) rename test/{variant_add.test.cpp => variant_add_test.cpp} (99%) rename test/{variant_and.test.cpp => variant_and_test.cpp} (99%) rename test/{variant_cast.test.cpp => variant_cast_test.cpp} (99%) rename test/{variant_divide.test.cpp => variant_divide_test.cpp} (99%) rename test/{variant_equal.test.cpp => variant_equal_test.cpp} (99%) rename test/{variant_exp.test.cpp => variant_exp_test.cpp} (99%) rename test/{variant_gt.test.cpp => variant_gt_test.cpp} (99%) rename test/{variant_gte.test.cpp => variant_gte_test.cpp} (99%) rename test/{variant_inequal.test.cpp => variant_inequal_test.cpp} (99%) rename test/{variant_lt.test.cpp => variant_lt_test.cpp} (99%) rename test/{variant_lte.test.cpp => variant_lte_test.cpp} (99%) rename test/{variant_match.test.cpp => variant_match_test.cpp} (99%) rename test/{variant_math.test.cpp => variant_math_test.cpp} (98%) rename test/{variant_modulo.test.cpp => variant_modulo_test.cpp} (99%) rename test/{variant_multiply.test.cpp => variant_multiply_test.cpp} (99%) rename test/{variant_nomatch.test.cpp => variant_nomatch_test.cpp} (99%) rename test/{variant_not.test.cpp => variant_not_test.cpp} (98%) rename test/{variant_or.test.cpp => variant_or_test.cpp} (99%) rename test/{variant_partial.test.cpp => variant_partial_test.cpp} (99%) rename test/{variant_subtract.test.cpp => variant_subtract_test.cpp} (99%) rename test/{variant_xor.test.cpp => variant_xor_test.cpp} (99%) rename test/{view.test.cpp => view_test.cpp} (99%) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 0dd06b8e4..7bda63c2f 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -17,7 +17,7 @@ jobs: run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS=--coverage - name: Build project - run: cmake --build build --target build_tests + run: cmake --build build --target test_runner --target task_executable - name: Test project run: ctest --test-dir build -j 8 --output-on-failure diff --git a/Cargo.lock b/Cargo.lock index 100bc047d..a98e23563 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1141,6 +1141,21 @@ dependencies = [ "sct", ] +[[package]] +name = "rustls" +version = "0.23.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.6", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -1807,6 +1822,15 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "windows-core" version = "0.52.0" diff --git a/doc/devel/contrib/development.md b/doc/devel/contrib/development.md index 0301eea07..0c9ae90bb 100644 --- a/doc/devel/contrib/development.md +++ b/doc/devel/contrib/development.md @@ -52,9 +52,9 @@ cmake --build build-clang ## Run the Test Suite: For running the test suite [ctest](https://cmake.org/cmake/help/latest/manual/ctest.1.html) is used. Before one can run the test suite the `task_executable` must be built. -After that also the `build_tests` target must be build, which can be done over: +After that also the `test_runner` target must be build, which can be done over: ```sh -cmake --build build --target build_tests +cmake --build build --target test_runner ``` Again you may also use the `-j ` option for parallel builds. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d8de86d9f..b6813661e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required (VERSION 3.22) +# -- C++ tests + include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/commands @@ -8,56 +10,64 @@ include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/test ${TASK_INCLUDE_DIRS}) -set (test_SRCS - col.test.cpp - dom.test.cpp - eval.test.cpp - lexer.test.cpp - t.test.cpp - tw-2689.test.cpp - tdb2.test.cpp - tc.test.cpp - util.test.cpp - variant_add.test.cpp - variant_and.test.cpp - variant_cast.test.cpp - variant_divide.test.cpp - variant_equal.test.cpp - variant_exp.test.cpp - variant_gt.test.cpp - variant_gte.test.cpp - variant_inequal.test.cpp - variant_lt.test.cpp - variant_lte.test.cpp - variant_match.test.cpp - variant_math.test.cpp - variant_modulo.test.cpp - variant_multiply.test.cpp - variant_nomatch.test.cpp - variant_not.test.cpp - variant_or.test.cpp - variant_partial.test.cpp - variant_subtract.test.cpp - variant_xor.test.cpp - view.test.cpp +# All C++ test files. Note that the portion before `.cpp` must be a valid, +# unique C++ identifier. +set(test_SRCS + col_test.cpp + dom_test.cpp + eval_test.cpp + lexer_test.cpp + t_test.cpp + tw_2689_test.cpp + tdb2_test.cpp + tc_cpp_test.cpp + util_test.cpp + variant_add_test.cpp + variant_and_test.cpp + variant_cast_test.cpp + variant_divide_test.cpp + variant_equal_test.cpp + variant_exp_test.cpp + variant_gt_test.cpp + variant_gte_test.cpp + variant_inequal_test.cpp + variant_lt_test.cpp + variant_lte_test.cpp + variant_match_test.cpp + variant_math_test.cpp + variant_modulo_test.cpp + variant_multiply_test.cpp + variant_nomatch_test.cpp + variant_not_test.cpp + variant_or_test.cpp + variant_partial_test.cpp + variant_subtract_test.cpp + variant_xor_test.cpp + view_test.cpp ) -add_custom_target (build_tests DEPENDS ${test_SRCS} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/test) +# Build `test_runner` containing all CPP tests, linked once. +create_test_sourcelist (cpp_test_SRCS cpp_tests.cpp ${test_SRCS}) +add_executable(test_runner + test.cpp + ${cpp_test_SRCS} +) +target_link_libraries (test_runner task commands columns libshared task commands columns libshared task commands columns libshared ${TASK_LIBRARIES}) +if (DARWIN) + target_link_libraries (test_runner "-framework CoreFoundation -framework Security -framework SystemConfiguration") +endif (DARWIN) -foreach (src_FILE ${test_SRCS}) - add_executable (${src_FILE} ${src_FILE} test.cpp) - target_link_libraries (${src_FILE} task commands columns libshared task commands columns libshared task commands columns libshared ${TASK_LIBRARIES}) - add_dependencies (${src_FILE} task_executable) - if (DARWIN) - target_link_libraries (${src_FILE} "-framework CoreFoundation -framework Security -framework SystemConfiguration") - endif (DARWIN) - - add_test(NAME ${src_FILE} - COMMAND ${src_FILE} +foreach (test_FILE ${test_SRCS}) + get_filename_component (test_NAME ${test_FILE} NAME_WE) + # Tell the source file what its own name is + set_source_files_properties(${test_FILE} PROPERTIES COMPILE_FLAGS -DTEST_NAME=${test_NAME}) + add_test(NAME ${test_FILE} + COMMAND test_runner ${test_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) -endforeach (src_FILE) + ) +endforeach (test_FILE) + +# -- Python tests add_subdirectory(basetest) add_subdirectory(simpletap) diff --git a/test/README.md b/test/README.md index eafc2d956..e00621ffc 100644 --- a/test/README.md +++ b/test/README.md @@ -1,7 +1,7 @@ ## Running Tests Do this to run all tests: ```shell -cmake --build build --target build_tests +cmake --build build --target test_runner --target task_executable ctest --test-dir build ``` diff --git a/test/col.test.cpp b/test/col_test.cpp similarity index 99% rename from test/col.test.cpp rename to test/col_test.cpp index 0e7d8290b..d2ffd378c 100644 --- a/test/col.test.cpp +++ b/test/col_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest test(12); // Ensure environment has no influence. diff --git a/test/docker/arch b/test/docker/arch index e4ef88b81..1f01e38b1 100644 --- a/test/docker/arch +++ b/test/docker/arch @@ -26,5 +26,5 @@ RUN cmake --install build RUN task --version # Setup tests -RUN cmake --build build --target build_tests -j 8 +RUN cmake --build build --target test_runner -j 8 CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/debianstable b/test/docker/debianstable index 94e75a604..fc31168c6 100644 --- a/test/docker/debianstable +++ b/test/docker/debianstable @@ -27,5 +27,5 @@ RUN cmake --install build RUN task --version # Setup tests -RUN cmake --build build --target build_tests -j 8 +RUN cmake --build build --target test_runner -j 8 CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/debiantesting b/test/docker/debiantesting index 9d11a619c..dbf886149 100644 --- a/test/docker/debiantesting +++ b/test/docker/debiantesting @@ -27,5 +27,5 @@ RUN cmake --install build RUN task --version # Setup tests -RUN cmake --build build --target build_tests -j 8 +RUN cmake --build build --target test_runner -j 8 CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/fedora39 b/test/docker/fedora39 index 02659a415..f4aa357a0 100644 --- a/test/docker/fedora39 +++ b/test/docker/fedora39 @@ -26,5 +26,5 @@ RUN cmake --install build RUN task --version # Setup tests -RUN cmake --build build --target build_tests -j 8 +RUN cmake --build build --target test_runner -j 8 CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/fedora40 b/test/docker/fedora40 index 0574356a3..8dfc23dd3 100644 --- a/test/docker/fedora40 +++ b/test/docker/fedora40 @@ -26,5 +26,5 @@ RUN cmake --install build RUN task --version # Setup tests -RUN cmake --build build --target build_tests -j 8 +RUN cmake --build build --target test_runner -j 8 CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/opensuse b/test/docker/opensuse index 6ea38d4f0..cff676ef1 100644 --- a/test/docker/opensuse +++ b/test/docker/opensuse @@ -25,5 +25,5 @@ RUN cmake --install build RUN task --version # Setup tests -RUN cmake --build build --target build_tests -j 8 +RUN cmake --build build --target test_runner -j 8 CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/ubuntu2004 b/test/docker/ubuntu2004 index ea11d1f59..dd0ec6f57 100644 --- a/test/docker/ubuntu2004 +++ b/test/docker/ubuntu2004 @@ -34,5 +34,5 @@ RUN cmake --install build RUN task --version # Setup tests -RUN cmake --build build --target build_tests -j 8 +RUN cmake --build build --target test_runner -j 8 CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/docker/ubuntu2204 b/test/docker/ubuntu2204 index 110547ade..c1c3f6aef 100644 --- a/test/docker/ubuntu2204 +++ b/test/docker/ubuntu2204 @@ -27,5 +27,5 @@ RUN cmake --install build RUN task --version # Setup tests -RUN cmake --build build --target build_tests -j 8 +RUN cmake --build build --target test_runner -j 8 CMD ctest --test-dir build -j 8 --output-on-failure --rerun-failed diff --git a/test/dom.test.cpp b/test/dom_test.cpp similarity index 98% rename from test/dom.test.cpp rename to test/dom_test.cpp index 2a031e835..f542b3e30 100644 --- a/test/dom.test.cpp +++ b/test/dom_test.cpp @@ -31,6 +31,8 @@ #include #include +namespace { + //////////////////////////////////////////////////////////////////////////////// bool providerString(const std::string& path, Variant& var) { if (path == "name") { @@ -50,8 +52,10 @@ bool providerString(const std::string& path, Variant& var) { return false; } +} // namespace + //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(12); DOM dom; diff --git a/test/eval.test.cpp b/test/eval_test.cpp similarity index 99% rename from test/eval.test.cpp rename to test/eval_test.cpp index 3875da5ac..e55d612a2 100644 --- a/test/eval.test.cpp +++ b/test/eval_test.cpp @@ -31,6 +31,8 @@ #include #include +namespace { + //////////////////////////////////////////////////////////////////////////////// // A few hard-coded symbols. bool get(const std::string& name, Variant& value) { @@ -42,8 +44,10 @@ bool get(const std::string& name, Variant& value) { return true; } +} // namespace + //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(52); Context context; Context::setContext(&context); diff --git a/test/lexer.test.cpp b/test/lexer_test.cpp similarity index 99% rename from test/lexer.test.cpp rename to test/lexer_test.cpp index cac825f17..715c46081 100644 --- a/test/lexer.test.cpp +++ b/test/lexer_test.cpp @@ -37,7 +37,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { #ifdef PRODUCT_TASKWARRIOR UnitTest t(1255); #else diff --git a/test/t.test.cpp b/test/t_test.cpp similarity index 99% rename from test/t.test.cpp rename to test/t_test.cpp index d56015263..a7063b19f 100644 --- a/test/t.test.cpp +++ b/test/t_test.cpp @@ -32,7 +32,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest test(48); Context context; Context::setContext(&context); diff --git a/test/tc.test.cpp b/test/tc_cpp_test.cpp similarity index 99% rename from test/tc.test.cpp rename to test/tc_cpp_test.cpp index 747c8e628..c92e5e66d 100644 --- a/test/tc.test.cpp +++ b/test/tc_cpp_test.cpp @@ -41,7 +41,7 @@ std::string uuid2str(tc::Uuid uuid) { return static_cast(uuid.to_st // Tests for the basic cxxbridge functionality. This focuses on the methods with // complex cxxbridge implementations, rather than those with complex Rust // implementations but simple APIs, like sync. -int main(int, char **) { +int TEST_NAME(int, char **) { UnitTest t; std::string str; diff --git a/test/tdb2.test.cpp b/test/tdb2_test.cpp similarity index 98% rename from test/tdb2.test.cpp rename to test/tdb2_test.cpp index e9a0b1508..22134d250 100644 --- a/test/tdb2.test.cpp +++ b/test/tdb2_test.cpp @@ -34,7 +34,7 @@ #include -Context context; +namespace { void cleardb() { // Remove any residual test files. @@ -42,8 +42,10 @@ void cleardb() { unlink("./taskchampion.sqlite3"); } +} // namespace + //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(12); Context context; Context::setContext(&context); diff --git a/test/tw-2689.test.cpp b/test/tw_2689_test.cpp similarity index 99% rename from test/tw-2689.test.cpp rename to test/tw_2689_test.cpp index 14f95a784..cbbad40f1 100644 --- a/test/tw-2689.test.cpp +++ b/test/tw_2689_test.cpp @@ -34,7 +34,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest test(12); // Ensure environment has no influence. diff --git a/test/util.test.cpp b/test/util_test.cpp similarity index 99% rename from test/util.test.cpp rename to test/util_test.cpp index a627c16a9..c4cf4e0bd 100644 --- a/test/util.test.cpp +++ b/test/util_test.cpp @@ -35,7 +35,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(19); Context context; Context::setContext(&context); diff --git a/test/variant_add.test.cpp b/test/variant_add_test.cpp similarity index 99% rename from test/variant_add.test.cpp rename to test/variant_add_test.cpp index 1cbd1eb92..b17a3812c 100644 --- a/test/variant_add.test.cpp +++ b/test/variant_add_test.cpp @@ -35,7 +35,7 @@ #define EPSILON 0.001 //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(80); Variant v0(true); diff --git a/test/variant_and.test.cpp b/test/variant_and_test.cpp similarity index 99% rename from test/variant_and.test.cpp rename to test/variant_and_test.cpp index 78267656b..97196ea78 100644 --- a/test/variant_and.test.cpp +++ b/test/variant_and_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(76); Variant v0(true); diff --git a/test/variant_cast.test.cpp b/test/variant_cast_test.cpp similarity index 99% rename from test/variant_cast.test.cpp rename to test/variant_cast_test.cpp index e93a82ac6..327881dc0 100644 --- a/test/variant_cast.test.cpp +++ b/test/variant_cast_test.cpp @@ -35,7 +35,7 @@ #define EPSILON 0.001 //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(81); time_t now = time(nullptr); diff --git a/test/variant_divide.test.cpp b/test/variant_divide_test.cpp similarity index 99% rename from test/variant_divide.test.cpp rename to test/variant_divide_test.cpp index a42c61664..c1df0c158 100644 --- a/test/variant_divide.test.cpp +++ b/test/variant_divide_test.cpp @@ -35,7 +35,7 @@ #define EPSILON 0.0001 //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(44); Variant v0(true); diff --git a/test/variant_equal.test.cpp b/test/variant_equal_test.cpp similarity index 99% rename from test/variant_equal.test.cpp rename to test/variant_equal_test.cpp index f09f9a28d..2383c6e96 100644 --- a/test/variant_equal.test.cpp +++ b/test/variant_equal_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(72); Variant v0(true); diff --git a/test/variant_exp.test.cpp b/test/variant_exp_test.cpp similarity index 99% rename from test/variant_exp.test.cpp rename to test/variant_exp_test.cpp index 20fec4fd2..00fd48121 100644 --- a/test/variant_exp.test.cpp +++ b/test/variant_exp_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(38); Variant v0(true); diff --git a/test/variant_gt.test.cpp b/test/variant_gt_test.cpp similarity index 99% rename from test/variant_gt.test.cpp rename to test/variant_gt_test.cpp index a35f3cef4..2abab3add 100644 --- a/test/variant_gt.test.cpp +++ b/test/variant_gt_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(72); Variant v0(true); diff --git a/test/variant_gte.test.cpp b/test/variant_gte_test.cpp similarity index 99% rename from test/variant_gte.test.cpp rename to test/variant_gte_test.cpp index 6da52047e..4dbfa8f8e 100644 --- a/test/variant_gte.test.cpp +++ b/test/variant_gte_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(72); Variant v0(true); diff --git a/test/variant_inequal.test.cpp b/test/variant_inequal_test.cpp similarity index 99% rename from test/variant_inequal.test.cpp rename to test/variant_inequal_test.cpp index eb1dc0b20..29222899b 100644 --- a/test/variant_inequal.test.cpp +++ b/test/variant_inequal_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(72); Variant v0(true); diff --git a/test/variant_lt.test.cpp b/test/variant_lt_test.cpp similarity index 99% rename from test/variant_lt.test.cpp rename to test/variant_lt_test.cpp index f5ae1331c..80967edd5 100644 --- a/test/variant_lt.test.cpp +++ b/test/variant_lt_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(72); Variant v0(true); diff --git a/test/variant_lte.test.cpp b/test/variant_lte_test.cpp similarity index 99% rename from test/variant_lte.test.cpp rename to test/variant_lte_test.cpp index 4b2eaba9a..f68d4bf8a 100644 --- a/test/variant_lte.test.cpp +++ b/test/variant_lte_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(72); Variant v0(true); diff --git a/test/variant_match.test.cpp b/test/variant_match_test.cpp similarity index 99% rename from test/variant_match.test.cpp rename to test/variant_match_test.cpp index 2777eb551..879adc8d3 100644 --- a/test/variant_match.test.cpp +++ b/test/variant_match_test.cpp @@ -33,12 +33,12 @@ #include -Task task; - //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(120); + Task task; + Variant vs0("untrue"); // ~ true Variant vs1(8421); // ~ 42 Variant vs2(3.14159); // ~ 3.14 diff --git a/test/variant_math.test.cpp b/test/variant_math_test.cpp similarity index 98% rename from test/variant_math.test.cpp rename to test/variant_math_test.cpp index 417a13adc..46b4dc10f 100644 --- a/test/variant_math.test.cpp +++ b/test/variant_math_test.cpp @@ -35,7 +35,7 @@ #define EPSILON 0.001 //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(1); Variant v0(10.0); diff --git a/test/variant_modulo.test.cpp b/test/variant_modulo_test.cpp similarity index 99% rename from test/variant_modulo.test.cpp rename to test/variant_modulo_test.cpp index 90a6da82c..39ef6a2b6 100644 --- a/test/variant_modulo.test.cpp +++ b/test/variant_modulo_test.cpp @@ -35,7 +35,7 @@ #define EPSILON 0.0001 //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(40); Variant v0(true); diff --git a/test/variant_multiply.test.cpp b/test/variant_multiply_test.cpp similarity index 99% rename from test/variant_multiply.test.cpp rename to test/variant_multiply_test.cpp index 6d253ecbe..159fbb8cb 100644 --- a/test/variant_multiply.test.cpp +++ b/test/variant_multiply_test.cpp @@ -35,7 +35,7 @@ #define EPSILON 0.0001 //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(54); Variant v0(true); diff --git a/test/variant_nomatch.test.cpp b/test/variant_nomatch_test.cpp similarity index 99% rename from test/variant_nomatch.test.cpp rename to test/variant_nomatch_test.cpp index 827facb25..31820f8ac 100644 --- a/test/variant_nomatch.test.cpp +++ b/test/variant_nomatch_test.cpp @@ -33,12 +33,12 @@ #include -Task task; - //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(120); + Task task; + Variant vs0("untrue"); // !~ true Variant vs1(8421); // !~ 42 Variant vs2(3.14159); // !~ 3.14 diff --git a/test/variant_not.test.cpp b/test/variant_not_test.cpp similarity index 98% rename from test/variant_not.test.cpp rename to test/variant_not_test.cpp index cafc4aa5c..15b6ad476 100644 --- a/test/variant_not.test.cpp +++ b/test/variant_not_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(14); Variant v0(true); diff --git a/test/variant_or.test.cpp b/test/variant_or_test.cpp similarity index 99% rename from test/variant_or.test.cpp rename to test/variant_or_test.cpp index 056e47a8e..9c91e2151 100644 --- a/test/variant_or.test.cpp +++ b/test/variant_or_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(76); Variant v0(true); diff --git a/test/variant_partial.test.cpp b/test/variant_partial_test.cpp similarity index 99% rename from test/variant_partial.test.cpp rename to test/variant_partial_test.cpp index 78893c4a6..40eef0c48 100644 --- a/test/variant_partial.test.cpp +++ b/test/variant_partial_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(72); Variant v0(true); diff --git a/test/variant_subtract.test.cpp b/test/variant_subtract_test.cpp similarity index 99% rename from test/variant_subtract.test.cpp rename to test/variant_subtract_test.cpp index d0e405047..967589123 100644 --- a/test/variant_subtract.test.cpp +++ b/test/variant_subtract_test.cpp @@ -35,7 +35,7 @@ #define EPSILON 0.001 //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(55); Variant v0(true); diff --git a/test/variant_xor.test.cpp b/test/variant_xor_test.cpp similarity index 99% rename from test/variant_xor.test.cpp rename to test/variant_xor_test.cpp index 3e15fd212..0917d35a3 100644 --- a/test/variant_xor.test.cpp +++ b/test/variant_xor_test.cpp @@ -33,7 +33,7 @@ #include //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(76); Variant v0(true); diff --git a/test/view.test.cpp b/test/view_test.cpp similarity index 99% rename from test/view.test.cpp rename to test/view_test.cpp index 0d8b05acc..76a27541a 100644 --- a/test/view.test.cpp +++ b/test/view_test.cpp @@ -43,7 +43,7 @@ Context context; extern std::string configurationDefaults; //////////////////////////////////////////////////////////////////////////////// -int main(int, char**) { +int TEST_NAME(int, char**) { UnitTest t(1); Context context; Context::setContext(&context); From 05da133eb606960853594beef546dc0fe315574a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 08:22:36 -0400 Subject: [PATCH 103/242] Bump sigstore/cosign-installer from 3.5.0 to 3.6.0 (#3594) Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.5.0 to 3.6.0. - [Release notes](https://github.com/sigstore/cosign-installer/releases) - [Commits](https://github.com/sigstore/cosign-installer/compare/v3.5.0...v3.6.0) --- updated-dependencies: - dependency-name: sigstore/cosign-installer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 9414d0408..efa275233 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -29,7 +29,7 @@ jobs: submodules: "recursive" - name: Install cosign - uses: sigstore/cosign-installer@v3.5.0 + uses: sigstore/cosign-installer@v3.6.0 - name: Log into registry ${{ env.REGISTRY }} uses: docker/login-action@v3.3.0 From d46e5eca580f49c741da45a65019ca551a9b9ec5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 08:23:45 -0400 Subject: [PATCH 104/242] Bump docker/build-push-action from 6.5.0 to 6.6.1 (#3595) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.5.0 to 6.6.1. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.5.0...v6.6.1) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index efa275233..222bf9554 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -40,7 +40,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.5.0 + uses: docker/build-push-action@v6.6.1 with: context: . file: "./docker/task.dockerfile" From 70632b088e94a6babe21526ecde22044fbfc16ca Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 14 Aug 2024 08:35:34 -0400 Subject: [PATCH 105/242] Do not count undo operations in the 'would be reverted..' message (#3598) --- src/TDB2.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 30009b42c..5b93ad4ac 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -242,7 +242,16 @@ void TDB2::revert() { bool TDB2::confirm_revert(rust::Vec& undo_ops) { // TODO Use show_diff rather than this basic listing of operations, though // this might be a worthy undo.style itself. - std::cout << "The following " << undo_ops.size() << " operations would be reverted:\n"; + + // Count non-undo operations + int ops_count = 0; + for (auto& op : undo_ops) { + if (!op.is_undo_point()) { + ops_count++; + } + } + + std::cout << "The following " << ops_count << " operations would be reverted:\n"; for (auto& op : undo_ops) { if (op.is_undo_point()) { continue; From 6cfbb1696683f1a54591a8dabb9ebbbc648adb95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 09:22:41 -0400 Subject: [PATCH 106/242] Bump docker/build-push-action from 6.6.1 to 6.7.0 (#3602) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.6.1 to 6.7.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.6.1...v6.7.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 222bf9554..63c4b0819 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -40,7 +40,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: context: . file: "./docker/task.dockerfile" From 2e3badbf991e726ba0f0c4b5bb6b243ea2dcdfc3 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 26 Aug 2024 21:45:19 -0400 Subject: [PATCH 107/242] Add some instructions to the MSRV (#3604) There is no easy way to determine the MSRV for TaskChampion, other than somehow pulling the right version of the source and grepping for it. In practice, if we update the `taskchampion` dependency to one that has a higher MSRV, we'll get a build error and find this comment. And if we get an error building Taskwarrior due to an old MSRV (for example if something changes on `crates.io`) then we will also find this comment. This also removes some superfluous dependency versions from the root workspace. `src/taskchampion-cpp/Cargo.toml` specifies versions directly. --- .github/workflows/checks.yml | 3 +++ Cargo.toml | 10 ---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 0e8621486..01f37d13a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -29,6 +29,9 @@ jobs: - 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. toolchain: "1.73.0" # MSRV override: true diff --git a/Cargo.toml b/Cargo.toml index 7dabb449b..1e4fec553 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,13 +5,3 @@ members = [ ] resolver = "2" - -# All Rust dependencies are defined here, and then referenced by the -# Cargo.toml's in the members with `foo.workspace = true`. -[workspace.dependencies] -anyhow = "1.0" -ffizz-header = "0.5" -libc = "0.2.136" -pretty_assertions = "1" -regex = "^1.10.2" -taskchampion = "0.6" From 44d443a8d6471111c5b33e4c3ec47444cc78c5a2 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 2 Sep 2024 08:53:50 -0400 Subject: [PATCH 108/242] Update INSTALL file (#3606) --- INSTALL | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/INSTALL b/INSTALL index 4e885f645..bbcad9a4e 100644 --- a/INSTALL +++ b/INSTALL @@ -21,18 +21,20 @@ You will need a C++ compiler that supports full C++17, which includes: You will need the following libraries: - libuuid (not needed for OSX) +You will need a Rust toolchain of the Minimum Supported Rust Version (MSRV): + - rust 1.73.0 Basic Installation ------------------ Briefly, these shell commands will unpack, build and install Taskwarrior: - $ tar xzf task-X.Y.Z.tar.gz [1] - $ cd task-X.Y.Z [2] - $ cmake -DCMAKE_BUILD_TYPE=release . [3] - $ make [4] - $ sudo make install [5] - $ cd .. ; rm -r task-X.Y.Z [6] + $ tar xzf task-X.Y.Z.tar.gz [1] + $ cd task-X.Y.Z [2] + $ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release . [3] + $ cmake --build build [4] + $ sudo make -C build install [5] + $ cd .. ; rm -r task-X.Y.Z [6] These commands are explained below: From 72f9cd91a519034b592fc3069da1e764776a203b Mon Sep 17 00:00:00 2001 From: Tobias Predel Date: Tue, 3 Sep 2024 00:22:33 +0200 Subject: [PATCH 109/242] Refine INSTALL file (#3615) Update INSTALL file CMake can also abstract the install procedure from the underlying Makefile --- INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index bbcad9a4e..2b3c8da21 100644 --- a/INSTALL +++ b/INSTALL @@ -33,7 +33,7 @@ Briefly, these shell commands will unpack, build and install Taskwarrior: $ cd task-X.Y.Z [2] $ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release . [3] $ cmake --build build [4] - $ sudo make -C build install [5] + $ sudo cmake --install build [5] $ cd .. ; rm -r task-X.Y.Z [6] These commands are explained below: From 6a24510473729a6e78d2ba7c897ddf6ddeee4ef1 Mon Sep 17 00:00:00 2001 From: Gagan Nagaraj <117705519+gagankonana@users.noreply.github.com> Date: Mon, 9 Sep 2024 05:12:19 -0700 Subject: [PATCH 110/242] Exclude attributes starting with tag_ (#3619) * Exclude attributes starting with tag_ * Check only for tag_* --- src/commands/CmdInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp index e6d52d0a6..5795409f6 100644 --- a/src/commands/CmdInfo.cpp +++ b/src/commands/CmdInfo.cpp @@ -373,7 +373,7 @@ int CmdInfo::execute(std::string& output) { // Show any orphaned UDAs, which are identified by not being represented in // the context.columns map. for (auto& att : all) { - if (att.substr(0, 11) != "annotation_" && att.substr(0, 5) != "tags_" && + if (att.substr(0, 11) != "annotation_" && att.substr(0, 4) != "tag_" && att.substr(0, 4) != "dep_" && Context::getContext().columns.find(att) == Context::getContext().columns.end()) { row = view.addRow(); From c00c0e941bfcced2f35a763e31c8a07dc8446105 Mon Sep 17 00:00:00 2001 From: Gagan Nagaraj <117705519+gagankonana@users.noreply.github.com> Date: Wed, 11 Sep 2024 07:20:22 -0700 Subject: [PATCH 111/242] Throw error when task config write is unsuccessfully (#3620) --- src/commands/CmdConfig.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/commands/CmdConfig.cpp b/src/commands/CmdConfig.cpp index fab9b7631..543bf1432 100644 --- a/src/commands/CmdConfig.cpp +++ b/src/commands/CmdConfig.cpp @@ -95,7 +95,9 @@ bool CmdConfig::setConfigVariable(const std::string& name, const std::string& va change = true; } - if (change) File::write(Context::getContext().config.file(), contents); + if (change) + if (!File::write(Context::getContext().config.file(), contents)) + throw format("Could not write to '{1}'.", Context::getContext().config.file()); return change; } @@ -133,7 +135,9 @@ int CmdConfig::unsetConfigVariable(const std::string& name, bool confirmation /* if (!lineDeleted) line++; } - if (change) File::write(Context::getContext().config.file(), contents); + if (change) + if (!File::write(Context::getContext().config.file(), contents)) + throw format("Could not write to '{1}'.", Context::getContext().config.file()); if (change && found) return 0; From d75ef7f1977cf7017a4fcba08e27aeb56ac815f5 Mon Sep 17 00:00:00 2001 From: Gagan Nagaraj <117705519+gagankonana@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:16:20 -0700 Subject: [PATCH 112/242] Check if end date is being set to a pending task (#3622) check if end date is being set to a pending task -throw error if end date is being set to a pending task - add test for the bug --- src/commands/CmdModify.cpp | 5 +++++ test/modify.test.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/commands/CmdModify.cpp b/src/commands/CmdModify.cpp index 659c076d3..ba86ea1f9 100644 --- a/src/commands/CmdModify.cpp +++ b/src/commands/CmdModify.cpp @@ -116,6 +116,11 @@ void CmdModify::checkConsistency(Task &before, Task &after) { if (before.has("recur") && (!after.has("recur") || after.get("recur") == "")) throw std::string("You cannot remove the recurrence from a recurring task."); + + if ((before.getStatus() == Task::pending) && (after.getStatus() == Task::pending) && + (after.get("end") != "")) + throw format("Could not modify task {1}. You cannot set an end date on a pending task.", + before.identifier(true)); } //////////////////////////////////////////////////////////////////////////////// diff --git a/test/modify.test.py b/test/modify.test.py index 2a992e530..6b5fd55af 100755 --- a/test/modify.test.py +++ b/test/modify.test.py @@ -56,6 +56,20 @@ class TestBug1763(TestCase): self.assertIn("Modified 0 tasks.", out) +class TestBug3584(TestCase): + def setUp(self): + self.t = Task() + + def test_mod_pending_task_end_date(self): + """Adding end date for a pending task throws an error""" + self.t("add foo") + code, out, err = self.t.runError("1 modify end:1d") + self.assertIn( + "Could not modify task 1. You cannot set an end date on a pending task.", + err, + ) + + if __name__ == "__main__": from simpletap import TAPTestRunner From c95dc9d149b979da9557593b9cb3c9bfe00dc651 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 22 Sep 2024 18:57:46 -0400 Subject: [PATCH 113/242] Ignore SIGPIPE (#3627) This replicates what the Rust runtime does, and matches what Rust code expects, for example when writing to a socket which is no longer connected to the remote end. --- src/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index cbdeb0994..b202cd6ad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -39,6 +40,10 @@ int main(int argc, const char** argv) { int status{0}; + // Ignore SIGPIPE from writes to network sockets after the remote end has hung + // up. Rust code expects this, and the Rust runtime ignores this signal at startup. + signal(SIGPIPE, SIG_IGN); + Context globalContext; Context::setContext(&globalContext); From cfe92ce845da752433660a330820aa265f7361b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:06:38 -0400 Subject: [PATCH 114/242] Bump threeal/gcovr-action from 1.0.0 to 1.1.0 (#3636) Bumps [threeal/gcovr-action](https://github.com/threeal/gcovr-action) from 1.0.0 to 1.1.0. - [Release notes](https://github.com/threeal/gcovr-action/releases) - [Commits](https://github.com/threeal/gcovr-action/compare/v1.0.0...v1.1.0) --- updated-dependencies: - dependency-name: threeal/gcovr-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 7bda63c2f..c2b2cb709 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -23,7 +23,7 @@ jobs: run: ctest --test-dir build -j 8 --output-on-failure - name: Generate a code coverage report - uses: threeal/gcovr-action@v1.0.0 + uses: threeal/gcovr-action@v1.1.0 with: coveralls-out: coverage.coveralls.json excludes: | From ff2b1cb888610fbc62348497fdb27d161efd6fa4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:59:26 -0400 Subject: [PATCH 115/242] Bump docker/build-push-action from 6.7.0 to 6.8.0 (#3637) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.7.0 to 6.8.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.7.0...v6.8.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 63c4b0819..3a0c8c30f 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -40,7 +40,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.7.0 + uses: docker/build-push-action@v6.8.0 with: context: . file: "./docker/task.dockerfile" From 28628e5dcaf99bca0ef4e391167ceeebd3d8d713 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 3 Oct 2024 18:32:13 -0400 Subject: [PATCH 116/242] Add newline in sync error message (#3603) --- src/TDB2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 5b93ad4ac..fa6431613 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -226,7 +226,7 @@ void TDB2::get_changes(std::vector& changes) { void TDB2::revert() { rust::Vec undo_ops = replica()->get_undo_operations(); if (undo_ops.size() == 0) { - std::cout << "No operations to undo."; + std::cout << "No operations to undo.\n"; return; } if (confirm_revert(undo_ops)) { From a8b4bcdda875b1b79edfd0a7a7f0dbdefcde4a9c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:36:44 -0400 Subject: [PATCH 117/242] [pre-commit.ci] pre-commit autoupdate (#3638) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0) - [github.com/pre-commit/mirrors-clang-format: v18.1.8 → v19.1.1](https://github.com/pre-commit/mirrors-clang-format/compare/v18.1.8...v19.1.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 013d08459..7702c7b92 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,14 +2,14 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v18.1.8 + rev: v19.1.1 hooks: - id: clang-format types_or: [c++, c] From 26c383d615caecb478b3df7ea93fd9b6654cd354 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 10 Oct 2024 01:33:02 -0400 Subject: [PATCH 118/242] Restore 'load' timer (#3635) * Restore 'load' timer * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/TDB2.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/TDB2.cpp b/src/TDB2.cpp index fa6431613..2240c5213 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -348,6 +348,7 @@ int TDB2::latest_id() { //////////////////////////////////////////////////////////////////////////////// const std::vector TDB2::all_tasks() { + Timer timer; auto all_tctasks = replica()->all_task_data(); std::vector all; for (auto& maybe_tctask : all_tctasks) { @@ -357,12 +358,14 @@ const std::vector TDB2::all_tasks() { dependency_scan(all); + Context::getContext().time_load_us += timer.total_us(); return all; } //////////////////////////////////////////////////////////////////////////////// const std::vector TDB2::pending_tasks() { if (!_pending_tasks) { + Timer timer; auto& ws = working_set(); auto largest_index = ws->largest_index(); @@ -379,6 +382,7 @@ const std::vector TDB2::pending_tasks() { dependency_scan(result); + Context::getContext().time_load_us += timer.total_us(); _pending_tasks = result; } From 0bd3989babaf61f47893bcb8435fc03c8216a4a8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 17:47:39 -0400 Subject: [PATCH 119/242] [pre-commit.ci] pre-commit autoupdate (#3650) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 24.8.0 → 24.10.0](https://github.com/psf/black/compare/24.8.0...24.10.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7702c7b92..8507c27d0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,6 @@ repos: - id: clang-format types_or: [c++, c] - repo: https://github.com/psf/black - rev: 24.8.0 + rev: 24.10.0 hooks: - id: black From 7bd3d1b8928a80a5da5a28fb699f6093a742611f Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 14 Oct 2024 17:48:41 -0400 Subject: [PATCH 120/242] Install uuid-dev in GitHub action (#3647) --- .github/workflows/release-check.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release-check.yaml b/.github/workflows/release-check.yaml index 7ec39eb1d..8138356a7 100644 --- a/.github/workflows/release-check.yaml +++ b/.github/workflows/release-check.yaml @@ -17,6 +17,9 @@ jobs: toolchain: "stable" override: true + - name: Install uuid-dev + run: sudo apt install uuid-dev + - name: make a release tarball and build from it run: | cmake -S. -Bbuild && From 3e20ad6f6f6065dbc1db5a3ba2a62cb20c58d5bd Mon Sep 17 00:00:00 2001 From: Scott Mcdermott Date: Sun, 20 Oct 2024 04:00:50 +0800 Subject: [PATCH 121/242] Pass rc.weekstart to libshared for ISO8601 weeknum parsing if "monday" (#3654) * libshared: bump for weekstart, epoch defines, eopww fix mainly those visible changes, and miscellaneous others see GothenburgBitFactory/taskwarrior#3623 (weekstart) see GothenburgBitFactory/taskwarrior#3651 (epoch limit defines) see GothenburgBitFactory/libshared#73 (eopww fix) * Initialize libshared's weekstart from user's rc.weekstart config This enables use of newer libshared code that can parse week numbers according to ISO8601 instead of existing code which is always using Sunday-based weeks. To get ISO behavior, set rc.weekstart=monday. Default is still Sunday / old algorithm, as before, since Sunday is in the hardcoded default rcfile. Weekstart does not yet fix week-relative shortcuts, which will still always use Monday. See #3623 for further details. --- src/Context.cpp | 5 +++++ src/commands/CmdCalendar.cpp | 7 +------ src/libshared | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Context.cpp b/src/Context.cpp index 3ca9e6c2c..2ba1352fb 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -1105,6 +1105,11 @@ void Context::staticInitialization() { Task::regex = Variant::searchUsingRegex = config.getBoolean("regex"); Lexer::dateFormat = Variant::dateFormat = config.get("dateformat"); + auto weekStart = Datetime::dayOfWeek(config.get("weekstart")); + if (weekStart != 0 && weekStart != 1) + throw std::string( + "The 'weekstart' configuration variable may only contain 'Sunday' or 'Monday'."); + Datetime::weekstart = weekStart; Datetime::isoEnabled = config.getBoolean("date.iso"); Datetime::standaloneDateEnabled = false; Datetime::standaloneTimeEnabled = false; diff --git a/src/commands/CmdCalendar.cpp b/src/commands/CmdCalendar.cpp index 3fae4394d..88129a44f 100644 --- a/src/commands/CmdCalendar.cpp +++ b/src/commands/CmdCalendar.cpp @@ -404,12 +404,7 @@ int CmdCalendar::execute(std::string& output) { std::string CmdCalendar::renderMonths(int firstMonth, int firstYear, const Datetime& today, std::vector& all, int monthsPerLine) { auto& config = Context::getContext().config; - - // What day of the week does the user consider the first? - auto weekStart = Datetime::dayOfWeek(config.get("weekstart")); - if (weekStart != 0 && weekStart != 1) - throw std::string( - "The 'weekstart' configuration variable may only contain 'Sunday' or 'Monday'."); + auto weekStart = Datetime::weekstart; // Build table for the number of months to be displayed. Table view; diff --git a/src/libshared b/src/libshared index 47c3262fa..e0d493d16 160000 --- a/src/libshared +++ b/src/libshared @@ -1 +1 @@ -Subproject commit 47c3262fa97c4b69542040d39be6c516c38d0e57 +Subproject commit e0d493d16357d14f0f6092c6670777cde8eca76c From 4bf6144dafb16f78a2dcb3ae8f5c671e84cd4140 Mon Sep 17 00:00:00 2001 From: Thomas Lauf Date: Mon, 21 Oct 2024 21:16:25 +0200 Subject: [PATCH 122/242] Add SECURITY.md (#3655) --- SECURITY.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..1fb4ee7bc --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security + +To report a vulnerability, please contact [dustin@cs.uchicago.edu](mailto:dustin@cs.uchicago.edu), you may use GPG public-key D8097934A92E4B4210368102FF8B7AC6154E3226 which is available [here](https://keybase.io/djmitche/pgp_keys.asc?fingerprint=d8097934a92e4b4210368102ff8b7ac6154e3226). +Initial response is expected within ~48h. + +We kindly ask to follow the responsible disclosure model and refrain from sharing information until: + +1. Vulnerabilities are patched in Taskwarrior + 60 days to coordinate with distributions. +2. 90 days since the vulnerability is disclosed to us. + +We recognise the legitimacy of public interest and accept that security researchers can publish information after 90-days deadline unilaterally. + +We will assist with obtaining CVE and acknowledge the vulnerabilities reported. From 96c72f3e06e5d33f29057e18c89afd61a53ddb1c Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 22 Oct 2024 15:15:51 -0400 Subject: [PATCH 123/242] Issue warnings instead of errors for 'weird' tasks (#3646) * Issue warnings instead of errors for 'weird' tasks * Support more comprehensive checks when adding a task --- src/TDB2.cpp | 4 ++++ src/Task.cpp | 48 ++++++++++++++++++++++++++------------ src/Task.h | 1 + src/commands/CmdImport.cpp | 3 +++ test/import.test.py | 4 ++-- 5 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 2240c5213..d5dacd769 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -57,6 +57,10 @@ void TDB2::open_replica(const std::string& location, bool create_if_missing) { //////////////////////////////////////////////////////////////////////////////// // Add the new task to the replica. void TDB2::add(Task& task) { + // Validate a task for addition. This is stricter than `task.validate`, as any + // inconsistency is probably user error. + task.validate_add(); + // Ensure the task is consistent, and provide defaults if necessary. // bool argument to validate() is "applyDefault", to apply default values for // properties not otherwise given. diff --git a/src/Task.cpp b/src/Task.cpp index 6474c4384..6c51dedbe 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -1408,13 +1408,29 @@ void Task::substitute(const std::string& from, const std::string& to, const std: } #endif +//////////////////////////////////////////////////////////////////////////////// +// Validate a task for addition, raising user-visible errors for inconsistent or +// incorrect inputs. This is called before `Task::validate`. +void Task::validate_add() { + // There is no fixing a missing description. + if (!has("description")) + throw std::string("A task must have a description."); + else if (get("description") == "") + throw std::string("Cannot add a task that is blank."); + + // Cannot have an old-style recur frequency with no due date - when would it recur? + if (has("recur") && (!has("due") || get("due") == "")) + throw std::string("A recurring task must also have a 'due' date."); +} + //////////////////////////////////////////////////////////////////////////////// // The purpose of Task::validate is three-fold: // 1) To provide missing attributes where possible // 2) To provide suitable warnings about odd states -// 3) To generate errors when the inconsistencies are not fixable -// 4) To update status depending on other attributes +// 3) To update status depending on other attributes // +// As required by TaskChampion, no combination of properties and values is an +// error. This function will try to make sensible defaults and resolve inconsistencies. // Critically, note that despite the name this is not a read-only function. // void Task::validate(bool applyDefault /* = true */) { @@ -1428,6 +1444,8 @@ void Task::validate(bool applyDefault /* = true */) { Lexer lex(uid); std::string token; Lexer::Type type; + // `uuid` is not a property in the TaskChampion model, so an invalid UUID is + // actually an error. if (!lex.isUUID(token, type, true)) throw format("Not a valid UUID '{1}'.", uid); } else set("uuid", uuid()); @@ -1543,27 +1561,27 @@ void Task::validate(bool applyDefault /* = true */) { validate_before("scheduled", "due"); validate_before("scheduled", "end"); - // 3) To generate errors when the inconsistencies are not fixable + if (!has("description") || get("description") == "") + Context::getContext().footnote(format("Warning: task has no description.")); - // There is no fixing a missing description. - if (!has("description")) - throw std::string("A task must have a description."); - else if (get("description") == "") - throw std::string("Cannot add a task that is blank."); + // Cannot have an old-style recur frequency with no due date - when would it recur? + if (has("recur") && (!has("due") || get("due") == "")) { + Context::getContext().footnote(format("Warning: recurring task has no due date.")); + remove("recur"); + } - // Cannot have a recur frequency with no due date - when would it recur? - if (has("recur") && (!has("due") || get("due") == "")) - throw std::string("A recurring task must also have a 'due' date."); - - // Recur durations must be valid. + // Old-style recur durations must be valid. if (has("recur")) { std::string value = get("recur"); if (value != "") { Duration p; std::string::size_type i = 0; - if (!p.parse(value, i)) + if (!p.parse(value, i)) { // TODO Ideal location to map unsupported old recurrence periods to supported values. - throw format("The recurrence value '{1}' is not valid.", value); + Context::getContext().footnote( + format("Warning: The recurrence value '{1}' is not valid.", value)); + remove("recur"); + } } } } diff --git a/src/Task.h b/src/Task.h index 08d508f0a..f36e79b83 100644 --- a/src/Task.h +++ b/src/Task.h @@ -164,6 +164,7 @@ class Task { void substitute(const std::string&, const std::string&, const std::string&); #endif + void validate_add(); void validate(bool applyDefault = true); float urgency_c() const; diff --git a/src/commands/CmdImport.cpp b/src/commands/CmdImport.cpp index 8c7aa3486..31a2e141a 100644 --- a/src/commands/CmdImport.cpp +++ b/src/commands/CmdImport.cpp @@ -174,6 +174,9 @@ void CmdImport::importSingleTask(json::object* obj) { // Parse the whole thing, validate the data. Task task(obj); + // An empty task is probably not intentional - at least a UUID should be included. + if (task.is_empty()) throw format("Cannot import an empty task."); + auto hasGeneratedEntry = not task.has("entry"); auto hasExplicitEnd = task.has("end"); diff --git a/test/import.test.py b/test/import.test.py index 7771345a3..1c709c23d 100755 --- a/test/import.test.py +++ b/test/import.test.py @@ -284,10 +284,10 @@ class TestImportValidate(TestCase): self.t = Task() def test_import_empty_json(self): - """Verify empty JSON is caught""" + """Verify empty JSON is ignored""" j = "{}" code, out, err = self.t.runError("import", input=j) - self.assertIn("A task must have a description.", err) + self.assertIn("Cannot import an empty task.", err) def test_import_invalid_uuid(self): """Verify invalid UUID is caught""" From 2db373d631cf98785a3eb4232a262d88d97bee3e Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 22 Oct 2024 19:37:47 -0400 Subject: [PATCH 124/242] Update to TaskChampion 0.8.0 (#3648) * Update to TaskChampion 0.8.0 * Cargo update --- Cargo.lock | 442 +++++++++++++++++--------------- src/taskchampion-cpp/Cargo.toml | 2 +- 2 files changed, 230 insertions(+), 214 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a98e23563..109187613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "ahash" @@ -61,15 +61,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -78,9 +78,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -100,23 +100,23 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -145,9 +145,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -172,15 +172,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.0.99" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -200,7 +203,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -231,15 +234,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -265,9 +268,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.124" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273dcfd3acd4e1e276af13ed2a43eea7001318823e7a726a6b3ed39b4acc0b82" +checksum = "54ccead7d199d584d139148b04b4a368d1ec7556a1d9ea2548febb1b9d49f9a4" dependencies = [ "cc", "cxxbridge-flags", @@ -277,9 +280,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.97" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0c11acd0e63bae27dcd2afced407063312771212b7a823b4fd72d633be30fb" +checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1" dependencies = [ "cc", "codespan-reporting", @@ -292,15 +295,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.124" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839fcd5e43464614ffaa989eaf1c139ef1f0c51672a1ed08023307fa1b909ccd" +checksum = "65777e06cc48f0cb0152024c77d6cf9e4bdb4408e7b48bea993d42fa0f5b02b6" [[package]] name = "cxxbridge-macro" -version = "1.0.124" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2c1c1776b986979be68bb2285da855f8d8a35851a769fca8740df7c3d07877" +checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" dependencies = [ "proc-macro2", "quote", @@ -367,9 +370,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", @@ -392,30 +395,30 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -424,21 +427,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-io", @@ -476,9 +479,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "google-cloud-auth" @@ -582,13 +585,19 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hashlink" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -642,9 +651,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -654,9 +663,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.29" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -692,9 +701,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -725,19 +734,19 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "itoa" @@ -747,9 +756,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -771,9 +780,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libsqlite3-sys" @@ -807,9 +816,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" @@ -825,9 +834,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -835,29 +844,30 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -887,30 +897,20 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" -version = "0.36.0" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "parking_lot" @@ -932,7 +932,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -984,9 +984,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "powerfmt" @@ -996,36 +996,36 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.5" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -1035,9 +1035,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -1046,9 +1046,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -1115,7 +1115,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1143,15 +1143,15 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.6", + "rustls-webpki 0.102.8", "subtle", "zeroize", ] @@ -1167,9 +1167,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-webpki" @@ -1183,9 +1183,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -1228,18 +1228,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -1248,11 +1248,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1280,6 +1281,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "simple_asn1" version = "0.6.2" @@ -1354,15 +1361,15 @@ dependencies = [ [[package]] name = "subtle" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d0208408ba0c3df17ed26eb06992cb1a1268d41b2c0e12e65203fbe3972cee5" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.66" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -1398,9 +1405,9 @@ dependencies = [ [[package]] name = "taskchampion" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664ff35fa1777f8a7aabec512bf43926e14fb073de947b588355bea1814c3577" +checksum = "f1f199799caef799e37d56743d39fa247ace2f08546c372dc588555e7ec53138" dependencies = [ "anyhow", "byteorder", @@ -1441,18 +1448,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -1492,9 +1499,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -1507,27 +1514,26 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -1546,9 +1552,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -1559,9 +1565,9 @@ dependencies = [ [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -1617,30 +1623,30 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "untrusted" @@ -1650,18 +1656,18 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea" +checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" dependencies = [ "base64 0.22.1", "flate2", "log", "once_cell", - "rustls 0.23.12", + "rustls 0.23.14", "rustls-pki-types", "url", - "webpki-roots 0.26.3", + "webpki-roots 0.26.6", ] [[package]] @@ -1699,9 +1705,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" @@ -1720,19 +1726,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -1745,9 +1752,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -1757,9 +1764,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1767,9 +1774,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -1780,15 +1787,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" dependencies = [ "futures-util", "js-sys", @@ -1799,9 +1806,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", @@ -1815,9 +1822,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.3" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] @@ -1828,7 +1835,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -1837,7 +1844,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -1855,7 +1862,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -1875,18 +1891,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1897,9 +1913,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1909,9 +1925,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -1921,15 +1937,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -1939,9 +1955,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -1951,9 +1967,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -1963,9 +1979,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -1975,9 +1991,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winreg" @@ -1991,18 +2007,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", diff --git a/src/taskchampion-cpp/Cargo.toml b/src/taskchampion-cpp/Cargo.toml index 91e557587..fb352ad94 100644 --- a/src/taskchampion-cpp/Cargo.toml +++ b/src/taskchampion-cpp/Cargo.toml @@ -8,7 +8,7 @@ publish = false crate-type = ["staticlib"] [dependencies] -taskchampion = "0.7.0" +taskchampion = "0.8.0" cxx = "1.0.124" [build-dependencies] From af8c5d58c8b864be64eeaaa24edd81d2aabe03f4 Mon Sep 17 00:00:00 2001 From: Fredrik Lanker Date: Thu, 24 Oct 2024 01:18:21 +0200 Subject: [PATCH 125/242] Limit the allowed epoch timestamps (#3651) The code for parsing epoch timestamps when displaying tasks only supports values between year 1980 and 9999. Previous to this change, it was possible to set e.g., the due timestamp to a value outside of these limits, which would make it impossible to later on show the task. With this change, we only allow setting values within the same limits used by the code for displaying tasks. --- src/columns/ColTypeDate.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/columns/ColTypeDate.cpp b/src/columns/ColTypeDate.cpp index 21597d34f..cb459cd3b 100644 --- a/src/columns/ColTypeDate.cpp +++ b/src/columns/ColTypeDate.cpp @@ -190,7 +190,11 @@ void ColumnTypeDate::modify(Task& task, const std::string& value) { if (value != "" && evaluatedValue.get_date() == 0) throw format("'{1}' is not a valid date in the '{2}' format.", value, Variant::dateFormat); - task.set(_name, evaluatedValue.get_date()); + time_t epoch = evaluatedValue.get_date(); + if (epoch < EPOCH_MIN_VALUE || epoch >= EPOCH_MAX_VALUE) { + throw format("'{1}' is not a valid date.", value); + } + task.set(_name, epoch); } //////////////////////////////////////////////////////////////////////////////// From c3b850898fdd55d33e4ff06daeb23c9e4c5934ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 27 Oct 2024 15:22:12 -0400 Subject: [PATCH 126/242] Bump sigstore/cosign-installer from 3.6.0 to 3.7.0 (#3641) Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.6.0 to 3.7.0. - [Release notes](https://github.com/sigstore/cosign-installer/releases) - [Commits](https://github.com/sigstore/cosign-installer/compare/v3.6.0...v3.7.0) --- updated-dependencies: - dependency-name: sigstore/cosign-installer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 3a0c8c30f..cc2197259 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -29,7 +29,7 @@ jobs: submodules: "recursive" - name: Install cosign - uses: sigstore/cosign-installer@v3.6.0 + uses: sigstore/cosign-installer@v3.7.0 - name: Log into registry ${{ env.REGISTRY }} uses: docker/login-action@v3.3.0 From 0bb32d188c2a31ac5059cc46c05c62af8b457ec6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 27 Oct 2024 15:22:29 -0400 Subject: [PATCH 127/242] [pre-commit.ci] pre-commit autoupdate (#3657) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v19.1.1 → v19.1.2](https://github.com/pre-commit/mirrors-clang-format/compare/v19.1.1...v19.1.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8507c27d0..7ecaeb92e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v19.1.1 + rev: v19.1.2 hooks: - id: clang-format types_or: [c++, c] From 8bad3cdcbc14fbb3b6640f91a4dbd2223bc8e093 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 27 Oct 2024 15:22:38 -0400 Subject: [PATCH 128/242] Bump docker/build-push-action from 6.8.0 to 6.9.0 (#3642) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.8.0 to 6.9.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.8.0...v6.9.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index cc2197259..cf3607ddc 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -40,7 +40,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.8.0 + uses: docker/build-push-action@v6.9.0 with: context: . file: "./docker/task.dockerfile" From 6ff900f3fcd4dcaa0203fd1cdd90b1c66360ebea Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 30 Oct 2024 21:49:04 -0400 Subject: [PATCH 129/242] Use `Replica::pending_tasks` (#3661) This replaces a loop over _all_ tasks with one that fetches only pending tasks, as determined by the working set. This should be faster for task DB's with large numbers of completed tasks, although on my medium-sized installation (~5000 total tasks) the difference is negligible. --- src/TDB2.cpp | 14 ++++---------- src/taskchampion-cpp/src/lib.rs | 20 ++++++++++++++++++-- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/TDB2.cpp b/src/TDB2.cpp index d5dacd769..2b01bc3b5 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -370,18 +370,12 @@ const std::vector TDB2::all_tasks() { const std::vector TDB2::pending_tasks() { if (!_pending_tasks) { Timer timer; - auto& ws = working_set(); - auto largest_index = ws->largest_index(); + auto pending_tctasks = replica()->pending_task_data(); std::vector result; - for (size_t i = 0; i <= largest_index; i++) { - auto uuid = ws->by_index(i); - if (!uuid.is_nil()) { - auto maybe_task = replica()->get_task_data(uuid); - if (maybe_task.is_some()) { - result.push_back(Task(maybe_task.take())); - } - } + for (auto& maybe_tctask : pending_tctasks) { + auto tctask = maybe_tctask.take(); + result.push_back(Task(std::move(tctask))); } dependency_scan(result); diff --git a/src/taskchampion-cpp/src/lib.rs b/src/taskchampion-cpp/src/lib.rs index d58af7a60..622eebc49 100644 --- a/src/taskchampion-cpp/src/lib.rs +++ b/src/taskchampion-cpp/src/lib.rs @@ -114,13 +114,16 @@ mod ffi { fn commit_reversed_operations(&mut self, ops: Vec) -> Result; /// Get `TaskData` values for all tasks in the replica. - + /// /// This contains `OptionTaskData` to allow C++ to `take` values out of the vector and use /// them as `rust::Box`. Cxx does not support `Vec>`. Cxx also does not /// handle `HashMap`, so the result is not a map from uuid to task. The returned Vec is /// fully populated, so it is safe to call `take` on each value in the returned Vec once . fn all_task_data(&mut self) -> Result>; + /// Simiar to all_task_data, but returing only pending tasks (those in the working set). + fn pending_task_data(&mut self) -> Result>; + /// Get the UUIDs of all tasks. fn all_task_uuids(&mut self) -> Result>; @@ -500,6 +503,15 @@ impl Replica { .collect()) } + fn pending_task_data(&mut self) -> Result, CppError> { + Ok(self + .0 + .pending_task_data()? + .drain(..) + .map(|t| Some(t).into()) + .collect()) + } + fn all_task_uuids(&mut self) -> Result, CppError> { Ok(self .0 @@ -870,10 +882,14 @@ mod test { add_undo_point(&mut operations); create_task(uuid_v4(), &mut operations); create_task(uuid_v4(), &mut operations); - create_task(uuid_v4(), &mut operations); + let mut t = create_task(uuid_v4(), &mut operations); + cxx::let_cxx_string!(status = "status"); + cxx::let_cxx_string!(pending = "pending"); + t.update(&status, &pending, &mut operations); rep.commit_operations(operations).unwrap(); assert_eq!(rep.all_task_data().unwrap().len(), 3); + assert_eq!(rep.pending_task_data().unwrap().len(), 1); assert_eq!(rep.all_task_uuids().unwrap().len(), 3); } From 94c95563abbf8eaca0bceaddf019c11230a06e2a Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 31 Oct 2024 08:59:49 -0400 Subject: [PATCH 130/242] Upgrade to TaskChampion 0.9.0 (#3662) See https://github.com/GothenburgBitFactory/taskchampion/releases/tag/v0.9.0 --- Cargo.lock | 8 ++++---- src/taskchampion-cpp/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 109187613..426fe3ebc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1405,9 +1405,9 @@ dependencies = [ [[package]] name = "taskchampion" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f199799caef799e37d56743d39fa247ace2f08546c372dc588555e7ec53138" +checksum = "5719b8204660c0f5efc5777bd82e012a45ec36ab7315965c73b32526db2a7eb4" dependencies = [ "anyhow", "byteorder", @@ -1689,9 +1689,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", "serde", diff --git a/src/taskchampion-cpp/Cargo.toml b/src/taskchampion-cpp/Cargo.toml index fb352ad94..56259de71 100644 --- a/src/taskchampion-cpp/Cargo.toml +++ b/src/taskchampion-cpp/Cargo.toml @@ -8,7 +8,7 @@ publish = false crate-type = ["staticlib"] [dependencies] -taskchampion = "0.8.0" +taskchampion = "0.9.0" cxx = "1.0.124" [build-dependencies] From 81842263195423754889676e3432e7e3007d286a Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sat, 2 Nov 2024 06:16:23 -0400 Subject: [PATCH 131/242] Support ENABLE_TLS_NATIVE_ROOTS to use system TLS CAs (#3660) --- Cargo.lock | 63 ++++++++++++++++++++++++++++- src/taskchampion-cpp/CMakeLists.txt | 10 ++++- src/taskchampion-cpp/Cargo.toml | 4 ++ 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 426fe3ebc..8a634a74b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -912,6 +912,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1075,7 +1081,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.12", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", @@ -1156,6 +1162,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -1165,6 +1184,15 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.9.0" @@ -1204,6 +1232,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1226,6 +1263,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.210" @@ -1665,6 +1725,7 @@ dependencies = [ "log", "once_cell", "rustls 0.23.14", + "rustls-native-certs", "rustls-pki-types", "url", "webpki-roots 0.26.6", diff --git a/src/taskchampion-cpp/CMakeLists.txt b/src/taskchampion-cpp/CMakeLists.txt index 241273d6c..a0d4a42ab 100644 --- a/src/taskchampion-cpp/CMakeLists.txt +++ b/src/taskchampion-cpp/CMakeLists.txt @@ -7,12 +7,20 @@ else() add_subdirectory(${CMAKE_SOURCE_DIR}/src/taskchampion-cpp/corrosion) endif() +OPTION (ENABLE_TLS_NATIVE_ROOTS "Use the system's TLS root certificates" OFF) + +if (ENABLE_TLS_NATIVE_ROOTS) + message ("Enabling native TLS roots") + set(TASKCHAMPION_FEATURES "tls-native-roots") +endif (ENABLE_TLS_NATIVE_ROOTS) + # Import taskchampion-lib as a CMake library. This implements the Rust side of # the cxxbridge, and depends on the `taskchampion` crate. corrosion_import_crate( MANIFEST_PATH "${CMAKE_SOURCE_DIR}/Cargo.toml" LOCKED - CRATES "taskchampion-lib") + CRATES "taskchampion-lib" + FEATURES "${TASKCHAMPION_FEATURES}") # Set up `taskchampion-cpp`, the C++ side of the bridge. corrosion_add_cxxbridge(taskchampion-cpp diff --git a/src/taskchampion-cpp/Cargo.toml b/src/taskchampion-cpp/Cargo.toml index 56259de71..d84a00521 100644 --- a/src/taskchampion-cpp/Cargo.toml +++ b/src/taskchampion-cpp/Cargo.toml @@ -11,5 +11,9 @@ crate-type = ["staticlib"] taskchampion = "0.9.0" cxx = "1.0.124" +[features] +# use native CA roots, instead of bundled +tls-native-roots = ["taskchampion/tls-native-roots"] + [build-dependencies] cxx-build = "1.0" From 023e7958c94e2f925ab587ba81fb952ad00a33b8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:42:35 -0500 Subject: [PATCH 132/242] [pre-commit.ci] pre-commit autoupdate (#3664) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v19.1.2 → v19.1.3](https://github.com/pre-commit/mirrors-clang-format/compare/v19.1.2...v19.1.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7ecaeb92e..18eca6b11 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v19.1.2 + rev: v19.1.3 hooks: - id: clang-format types_or: [c++, c] From 0ff78447322a10e24c18b4ce2950745cf17962a1 Mon Sep 17 00:00:00 2001 From: "Denis Zh." Date: Tue, 5 Nov 2024 17:00:43 +0400 Subject: [PATCH 133/242] Fix missing line in man task-color (#3665) Escape leading single quote to prevent groff misinterpretation as a control character. --- doc/man/task-color.5.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/man/task-color.5.in b/doc/man/task-color.5.in index 6464b8023..fe698f998 100644 --- a/doc/man/task-color.5.in +++ b/doc/man/task-color.5.in @@ -278,7 +278,7 @@ The keyword rule shown here as 'keyword.' corresponds to a wildcard pattern, meaning 'color.keyword.*', or in other words all the keyword rules. There is also 'color.project.none', 'color.tag.none' and -'color.uda.priority.none' to specifically represent missing data. +\[aq]color.uda.priority.none' to specifically represent missing data. .SH THEMES Taskwarrior supports themes. What this really means is that with the ability to From 5b1be95f7dd79a48f152962dca42e3189ca58e14 Mon Sep 17 00:00:00 2001 From: "Denis Zh." Date: Tue, 5 Nov 2024 17:54:49 +0400 Subject: [PATCH 134/242] Add `color.calendar.scheduled` to `no-color.theme` (#3666) * Add scheduled color setting for calendar report * Add default color.calendar.scheduled to all themes --- doc/rc/bubblegum-256.theme | 1 + doc/rc/dark-16.theme | 1 + doc/rc/dark-256.theme | 1 + doc/rc/dark-blue-256.theme | 1 + doc/rc/dark-gray-256.theme | 1 + doc/rc/dark-gray-blue-256.theme | 1 + doc/rc/dark-green-256.theme | 1 + doc/rc/dark-red-256.theme | 1 + doc/rc/dark-violets-256.theme | 1 + doc/rc/dark-yellow-green.theme | 1 + doc/rc/light-16.theme | 1 + doc/rc/light-256.theme | 1 + doc/rc/no-color.theme | 1 + doc/rc/solarized-dark-256.theme | 1 + doc/rc/solarized-light-256.theme | 1 + 15 files changed, 15 insertions(+) diff --git a/doc/rc/bubblegum-256.theme b/doc/rc/bubblegum-256.theme index ac7b87e73..c79db532a 100644 --- a/doc/rc/bubblegum-256.theme +++ b/doc/rc/bubblegum-256.theme @@ -84,6 +84,7 @@ color.calendar.due=color0 on rgb325 color.calendar.due.today=color0 on rgb404 color.calendar.holiday=color15 on rgb102 color.calendar.overdue=color0 on color5 +color.calendar.scheduled= color.calendar.today=color15 on rgb103 color.calendar.weekend=gray12 on gray3 color.calendar.weeknumber=rgb104 diff --git a/doc/rc/dark-16.theme b/doc/rc/dark-16.theme index c7778848e..41cb07234 100644 --- a/doc/rc/dark-16.theme +++ b/doc/rc/dark-16.theme @@ -86,6 +86,7 @@ color.calendar.due=white on red color.calendar.due.today=bold white on red color.calendar.holiday=black on bright yellow color.calendar.overdue=black on bright red +color.calendar.scheduled= color.calendar.today=bold white on bright blue color.calendar.weekend=white on bright black color.calendar.weeknumber=bold blue diff --git a/doc/rc/dark-256.theme b/doc/rc/dark-256.theme index 17fcb8fe6..c6b385371 100644 --- a/doc/rc/dark-256.theme +++ b/doc/rc/dark-256.theme @@ -82,6 +82,7 @@ color.summary.bar=black on rgb141 color.calendar.due.today=color15 on color1 color.calendar.due=color0 on color1 color.calendar.holiday=color0 on color11 +color.calendar.scheduled= color.calendar.overdue=color0 on color9 color.calendar.today=color15 on rgb013 color.calendar.weekend=on color235 diff --git a/doc/rc/dark-blue-256.theme b/doc/rc/dark-blue-256.theme index ec974ce7e..3ab5022a9 100644 --- a/doc/rc/dark-blue-256.theme +++ b/doc/rc/dark-blue-256.theme @@ -83,6 +83,7 @@ color.calendar.due.today=color0 on color252 color.calendar.due=color0 on color249 color.calendar.holiday=color255 on rgb013 color.calendar.overdue=color0 on color255 +color.calendar.scheduled= color.calendar.today=color0 on rgb115 color.calendar.weekend=on color235 color.calendar.weeknumber=rgb015 diff --git a/doc/rc/dark-gray-256.theme b/doc/rc/dark-gray-256.theme index b6fdb343e..3b10a7766 100644 --- a/doc/rc/dark-gray-256.theme +++ b/doc/rc/dark-gray-256.theme @@ -83,6 +83,7 @@ color.calendar.due=on gray8 color.calendar.due.today=black on gray15 color.calendar.holiday=black on gray20 color.calendar.overdue=gray2 on gray10 +color.calendar.scheduled= color.calendar.today=bold white color.calendar.weekend=on gray2 color.calendar.weeknumber=gray6 diff --git a/doc/rc/dark-gray-blue-256.theme b/doc/rc/dark-gray-blue-256.theme index 8b65e615d..4d495fb2a 100644 --- a/doc/rc/dark-gray-blue-256.theme +++ b/doc/rc/dark-gray-blue-256.theme @@ -83,6 +83,7 @@ color.calendar.due=color0 on gray10 color.calendar.due.today=color0 on gray15 color.calendar.holiday=color15 on rgb005 color.calendar.overdue=color0 on gray20 +color.calendar.scheduled= color.calendar.today=underline black on color15 color.calendar.weekend=on gray4 color.calendar.weeknumber=gray10 diff --git a/doc/rc/dark-green-256.theme b/doc/rc/dark-green-256.theme index b95aa51c3..01d0c0111 100644 --- a/doc/rc/dark-green-256.theme +++ b/doc/rc/dark-green-256.theme @@ -83,6 +83,7 @@ color.calendar.due.today=color0 on color225 color.calendar.due=color0 on color249 color.calendar.holiday=rgb151 on rgb020 color.calendar.overdue=color0 on color255 +color.calendar.scheduled= color.calendar.today=color0 on rgb151 color.calendar.weekend=on color235 color.calendar.weeknumber=rgb010 diff --git a/doc/rc/dark-red-256.theme b/doc/rc/dark-red-256.theme index 8c655d2ae..a06ab992d 100644 --- a/doc/rc/dark-red-256.theme +++ b/doc/rc/dark-red-256.theme @@ -83,6 +83,7 @@ color.calendar.due.today=color0 on color252 color.calendar.due=color0 on color249 color.calendar.holiday=rgb522 on rgb300 color.calendar.overdue=color0 on color255 +color.calendar.scheduled= color.calendar.today=color0 on rgb511 color.calendar.weekend=on color235 color.calendar.weeknumber=rgb100 diff --git a/doc/rc/dark-violets-256.theme b/doc/rc/dark-violets-256.theme index 395777cc6..c92aceb38 100644 --- a/doc/rc/dark-violets-256.theme +++ b/doc/rc/dark-violets-256.theme @@ -83,6 +83,7 @@ color.calendar.due=color0 on rgb325 color.calendar.due.today=color0 on rgb404 color.calendar.holiday=color15 on rgb102 color.calendar.overdue=color0 on color5 +color.calendar.scheduled= color.calendar.today=color15 on rgb103 color.calendar.weekend=gray12 on gray3 color.calendar.weeknumber=rgb104 diff --git a/doc/rc/dark-yellow-green.theme b/doc/rc/dark-yellow-green.theme index 4a9dcbc00..15144090c 100644 --- a/doc/rc/dark-yellow-green.theme +++ b/doc/rc/dark-yellow-green.theme @@ -83,6 +83,7 @@ color.calendar.due=color0 on rgb440 color.calendar.due.today=color0 on rgb430 color.calendar.holiday=rgb151 on rgb020 color.calendar.overdue=color0 on rgb420 +color.calendar.scheduled= color.calendar.today=color15 on rgb110 color.calendar.weekend=on color235 color.calendar.weeknumber=rgb110 diff --git a/doc/rc/light-16.theme b/doc/rc/light-16.theme index c310dd301..c5a0d68a2 100644 --- a/doc/rc/light-16.theme +++ b/doc/rc/light-16.theme @@ -83,6 +83,7 @@ color.calendar.due=on bright green color.calendar.due.today=blue on bright yellow color.calendar.holiday=on yellow color.calendar.overdue=on bright red +color.calendar.scheduled= color.calendar.today=blue color.calendar.weekend=on white color.calendar.weeknumber=blue diff --git a/doc/rc/light-256.theme b/doc/rc/light-256.theme index 8044efbb0..0bba1d170 100644 --- a/doc/rc/light-256.theme +++ b/doc/rc/light-256.theme @@ -83,6 +83,7 @@ color.calendar.due=on rgb343 color.calendar.due.today=on rgb353 color.calendar.holiday=color0 on rgb530 color.calendar.overdue=on rgb533 +color.calendar.scheduled= color.calendar.today=rgb005 color.calendar.weekend=on gray21 color.calendar.weeknumber=gray16 diff --git a/doc/rc/no-color.theme b/doc/rc/no-color.theme index ae387e5ba..1f33b83af 100644 --- a/doc/rc/no-color.theme +++ b/doc/rc/no-color.theme @@ -86,6 +86,7 @@ color.calendar.due= color.calendar.due.today= color.calendar.holiday= color.calendar.overdue= +color.calendar.scheduled= color.calendar.today= color.calendar.weekend= color.calendar.weeknumber= diff --git a/doc/rc/solarized-dark-256.theme b/doc/rc/solarized-dark-256.theme index cd61504c3..74809b80a 100644 --- a/doc/rc/solarized-dark-256.theme +++ b/doc/rc/solarized-dark-256.theme @@ -100,6 +100,7 @@ color.calendar.due=color0 on color9 color.calendar.due.today=color0 on color1 color.calendar.holiday=color0 on color3 color.calendar.overdue=color0 on color5 +color.calendar.scheduled= color.calendar.today=color0 on color4 color.calendar.weekend=on color0 color.calendar.weeknumber=color4 diff --git a/doc/rc/solarized-light-256.theme b/doc/rc/solarized-light-256.theme index 9cdd56453..40c84c658 100644 --- a/doc/rc/solarized-light-256.theme +++ b/doc/rc/solarized-light-256.theme @@ -100,6 +100,7 @@ color.calendar.due=color7 on color9 color.calendar.due.today=color7 on color1 color.calendar.holiday=color7 on color3 color.calendar.overdue=color7 on color5 +color.calendar.scheduled= color.calendar.today=color7 on color4 color.calendar.weekend=on color7 color.calendar.weeknumber=color14 From 7da23aee1c63399e277bed70ad899483dd67f63d Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 5 Nov 2024 08:55:10 -0500 Subject: [PATCH 135/242] Run cargo test and fix it (#3663) run cargo test and fix it --- .github/workflows/tests.yaml | 31 +++++++++++++++++++++++++++++++ src/taskchampion-cpp/src/lib.rs | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index c2b2cb709..656dc6f96 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -94,6 +94,37 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + cargo-test: + runs-on: ubuntu-latest + name: "Cargo Test" + + steps: + - uses: actions/checkout@v4 + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo build + uses: actions/cache@v4 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} + + - uses: actions-rs/toolchain@v1 + 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.73.0" # MSRV + override: true + + - uses: actions-rs/cargo@v1.0.3 + with: + command: test + tests: needs: coverage strategy: diff --git a/src/taskchampion-cpp/src/lib.rs b/src/taskchampion-cpp/src/lib.rs index 622eebc49..2fc2f92df 100644 --- a/src/taskchampion-cpp/src/lib.rs +++ b/src/taskchampion-cpp/src/lib.rs @@ -943,7 +943,7 @@ mod test { assert_eq!(t.properties(), vec!["prop".to_string()]); assert_eq!( - t.iter(), + t.items(), vec![ffi::PropValuePair { prop: "prop".into(), value: "value".into(), From c9967c20e2b87ceca1dcc26e8171603f02d5f3ff Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 6 Nov 2024 07:39:39 -0500 Subject: [PATCH 136/242] Restore support for` task info` journal (#3671) This support was removed before Taskwarrior-3.x, and is now restored, including the original tests removed in ddd367232e2dbd7b7666a680e10af8a4094f3f03 --- src/CMakeLists.txt | 1 + src/Operation.cpp | 73 ++++++++++++++ src/Operation.h | 88 +++++++++++++++++ src/TDB2.cpp | 3 +- src/Task.cpp | 94 +----------------- src/Task.h | 11 +-- src/commands/CmdInfo.cpp | 164 ++++++++++++++++++++++++++++++++ src/commands/CmdInfo.h | 7 ++ src/taskchampion-cpp/src/lib.rs | 27 ++++++ test/CMakeLists.txt | 1 + test/info.test.py | 3 + test/tw-1999.test.py | 62 ++++++++++++ test/tw-2514.test.sh | 5 +- 13 files changed, 438 insertions(+), 101 deletions(-) create mode 100644 src/Operation.cpp create mode 100644 src/Operation.h create mode 100755 test/tw-1999.test.py diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 15530c776..9bb7f2e38 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,6 +13,7 @@ add_library (task STATIC CLI2.cpp CLI2.h Filter.cpp Filter.h Hooks.cpp Hooks.h Lexer.cpp Lexer.h + Operation.cpp Operation.h TDB2.cpp TDB2.h Task.cpp Task.h Variant.cpp Variant.h diff --git a/src/Operation.cpp b/src/Operation.cpp new file mode 100644 index 000000000..4bc4ecc48 --- /dev/null +++ b/src/Operation.cpp @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2006 - 2024, 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 +// +//////////////////////////////////////////////////////////////////////////////// + +#include +// cmake.h include header must come first + +#include +#include + +#include + +//////////////////////////////////////////////////////////////////////////////// +Operation::Operation(const tc::Operation& op) : op(&op) {} + +//////////////////////////////////////////////////////////////////////////////// +std::vector Operation::operations(const rust::Vec& operations) { + return {operations.begin(), operations.end()}; +} + +//////////////////////////////////////////////////////////////////////////////// +Operation& Operation::operator=(const Operation& other) { + op = other.op; + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +bool Operation::operator<(Operation& other) const { + if (is_create()) { + return !other.is_create(); + } else if (is_update()) { + if (other.is_create()) { + return false; + } else if (other.is_update()) { + return get_timestamp() < other.get_timestamp(); + } else { + return true; + } + } else if (is_delete()) { + if (other.is_create() || other.is_update() || other.is_delete()) { + return false; + } else { + return true; + } + } else if (is_undo_point()) { + return !other.is_undo_point(); + } + return false; // not reachable +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Operation.h b/src/Operation.h new file mode 100644 index 000000000..707dffd89 --- /dev/null +++ b/src/Operation.h @@ -0,0 +1,88 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2006 - 2024, 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_OPERATIOn +#define INCLUDED_OPERATIOn + +#include + +#include +#include + +// Representation of a TaskChampion operation. +// +// This class wraps `tc::Operation&` and thus cannot outlive that underlying +// type. +class Operation { + public: + explicit Operation(const tc::Operation &); + + Operation(const Operation &other) = default; + Operation &operator=(const Operation &other); + + // Create a vector of Operations given the result of `Replica::get_undo_operations` or + // `Replica::get_task_operations`. The resulting vector must not outlive the input `rust::Vec`. + static std::vector operations(const rust::Vec &); + + // Methods from the underlying `tc::Operation`. + bool is_create() const { return op->is_create(); } + bool is_update() const { return op->is_update(); } + bool is_delete() const { return op->is_delete(); } + bool is_undo_point() const { return op->is_undo_point(); } + std::string get_uuid() const { return std::string(op->get_uuid().to_string()); } + ::rust::Vec<::tc::PropValuePair> get_old_task() const { return op->get_old_task(); }; + std::string get_property() const { + std::string value; + op->get_property(value); + return value; + } + std::optional get_value() const { + std::optional value{std::string()}; + if (!op->get_value(value.value())) { + value = std::nullopt; + } + return value; + } + std::optional get_old_value() const { + std::optional old_value{std::string()}; + if (!op->get_old_value(old_value.value())) { + old_value = std::nullopt; + } + return old_value; + } + time_t get_timestamp() const { return static_cast(op->get_timestamp()); } + + // Define a partial order on Operations: + // - Create < Update < Delete < UndoPoint + // - Given two updates, sort by timestamp + bool operator<(Operation &other) const; + + private: + const tc::Operation *op; +}; + +#endif +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 2b01bc3b5..d7fd81017 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -244,8 +244,7 @@ void TDB2::revert() { //////////////////////////////////////////////////////////////////////////////// bool TDB2::confirm_revert(rust::Vec& undo_ops) { - // TODO Use show_diff rather than this basic listing of operations, though - // this might be a worthy undo.style itself. + // TODO: convert to Operation and use that type for display, similar to CmdInfo. // Count non-undo operations int ops_count = 0; diff --git a/src/Task.cpp b/src/Task.cpp index 6c51dedbe..72cf41975 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -1243,14 +1243,14 @@ void Task::fixTagsAttribute() { bool Task::isTagAttr(const std::string& attr) { return attr.compare(0, 4, "tag_") == 0; } //////////////////////////////////////////////////////////////////////////////// -const std::string Task::tag2Attr(const std::string& tag) const { +std::string Task::tag2Attr(const std::string& tag) { std::stringstream tag_attr; tag_attr << "tag_" << tag; return tag_attr.str(); } //////////////////////////////////////////////////////////////////////////////// -const std::string Task::attr2Tag(const std::string& attr) const { +std::string Task::attr2Tag(const std::string& attr) { assert(isTagAttr(attr)); return attr.substr(4); } @@ -1271,14 +1271,14 @@ void Task::fixDependsAttribute() { bool Task::isDepAttr(const std::string& attr) { return attr.compare(0, 4, "dep_") == 0; } //////////////////////////////////////////////////////////////////////////////// -const std::string Task::dep2Attr(const std::string& tag) const { +std::string Task::dep2Attr(const std::string& tag) { std::stringstream tag_attr; tag_attr << "dep_" << tag; return tag_attr.str(); } //////////////////////////////////////////////////////////////////////////////// -const std::string Task::attr2Dep(const std::string& attr) const { +std::string Task::attr2Dep(const std::string& attr) { assert(isDepAttr(attr)); return attr.substr(4); } @@ -2151,92 +2151,6 @@ std::string Task::diff(const Task& after) const { return out.str(); } -//////////////////////////////////////////////////////////////////////////////// -// Similar to diff, but formatted for inclusion in the output of the info command -std::string Task::diffForInfo(const Task& after, const std::string& dateformat, - long& last_timestamp, const long current_timestamp) const { - // Attributes are all there is, so figure the different attribute names - // between before and after. - std::vector beforeAtts; - for (auto& att : data) beforeAtts.push_back(att.first); - - std::vector afterAtts; - for (auto& att : after.data) afterAtts.push_back(att.first); - - std::vector beforeOnly; - std::vector afterOnly; - listDiff(beforeAtts, afterAtts, beforeOnly, afterOnly); - - // Now start generating a description of the differences. - std::stringstream out; - for (auto& name : beforeOnly) { - if (isAnnotationAttr(name)) { - out << format("Annotation '{1}' deleted.\n", get(name)); - } else if (isTagAttr(name)) { - out << format("Tag '{1}' deleted.\n", attr2Tag(name)); - } else if (isDepAttr(name)) { - out << format("Dependency on '{1}' deleted.\n", attr2Dep(name)); - } else if (name == "depends" || name == "tags") { - // do nothing for legacy attributes - } else if (name == "start") { - Datetime started(get("start")); - Datetime stopped; - - if (after.has("end")) - // Task was marked as finished, use end time - stopped = Datetime(after.get("end")); - else - // Start attribute was removed, use modification time - stopped = Datetime(current_timestamp); - - out << format("{1} deleted (duration: {2}).", Lexer::ucFirst(name), - Duration(stopped - started).format()) - << "\n"; - } else { - out << format("{1} deleted.\n", Lexer::ucFirst(name)); - } - } - - for (auto& name : afterOnly) { - if (isAnnotationAttr(name)) { - out << format("Annotation of '{1}' added.\n", after.get(name)); - } else if (isTagAttr(name)) { - out << format("Tag '{1}' added.\n", attr2Tag(name)); - } else if (isDepAttr(name)) { - out << format("Dependency on '{1}' added.\n", attr2Dep(name)); - } else if (name == "depends" || name == "tags") { - // do nothing for legacy attributes - } else { - if (name == "start") last_timestamp = current_timestamp; - - out << format("{1} set to '{2}'.", Lexer::ucFirst(name), - renderAttribute(name, after.get(name), dateformat)) - << "\n"; - } - } - - for (auto& name : beforeAtts) - if (name != "uuid" && name != "modified" && get(name) != after.get(name) && get(name) != "" && - after.get(name) != "") { - if (name == "depends" || name == "tags") { - // do nothing for legacy attributes - } else if (isTagAttr(name) || isDepAttr(name)) { - // ignore new attributes - } else if (isAnnotationAttr(name)) { - out << format("Annotation changed to '{1}'.\n", after.get(name)); - } else - out << format("{1} changed from '{2}' to '{3}'.", Lexer::ucFirst(name), - renderAttribute(name, get(name), dateformat), - renderAttribute(name, after.get(name), dateformat)) - << "\n"; - } - - // Shouldn't just say nothing. - if (out.str().length() == 0) out << "No changes made.\n"; - - return out.str(); -} - //////////////////////////////////////////////////////////////////////////////// // Similar to diff, but formatted as a side-by-side table for an Undo preview Table Task::diffForUndoSide(const Task& after) const { diff --git a/src/Task.h b/src/Task.h index f36e79b83..1f63b7f5a 100644 --- a/src/Task.h +++ b/src/Task.h @@ -164,6 +164,11 @@ class Task { void substitute(const std::string&, const std::string&, const std::string&); #endif + static std::string tag2Attr(const std::string&); + static std::string attr2Tag(const std::string&); + static std::string dep2Attr(const std::string&); + static std::string attr2Dep(const std::string&); + void validate_add(); void validate(bool applyDefault = true); @@ -176,8 +181,6 @@ class Task { #endif std::string diff(const Task& after) const; - std::string diffForInfo(const Task& after, const std::string& dateformat, long& last_timestamp, - const long current_timestamp) const; Table diffForUndoSide(const Task& after) const; Table diffForUndoPatch(const Task& after, const Datetime& lastChange) const; @@ -190,10 +193,6 @@ class Task { void validate_before(const std::string&, const std::string&); const std::string encode(const std::string&) const; const std::string decode(const std::string&) const; - const std::string tag2Attr(const std::string&) const; - const std::string attr2Tag(const std::string&) const; - const std::string dep2Attr(const std::string&) const; - const std::string attr2Dep(const std::string&) const; void fixDependsAttribute(); void fixTagsAttribute(); diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp index 5795409f6..cfb3263e8 100644 --- a/src/commands/CmdInfo.cpp +++ b/src/commands/CmdInfo.cpp @@ -33,13 +33,16 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include #include //////////////////////////////////////////////////////////////////////////////// @@ -477,9 +480,68 @@ int CmdInfo::execute(std::string& output) { urgencyDetails.set(row, 5, rightJustify(format(task.urgency(), 4, 4), 6)); } + // Create a third table, containing undo log change details. + Table journal; + setHeaderUnderline(journal); + + if (Context::getContext().config.getBoolean("obfuscate")) journal.obfuscate(); + if (Context::getContext().config.getBoolean("color")) journal.forceColor(); + + journal.width(Context::getContext().getWidth()); + journal.add("Date"); + journal.add("Modification"); + + if (Context::getContext().config.getBoolean("journal.info")) { + auto& replica = Context::getContext().tdb2.replica(); + tc::Uuid tcuuid = tc::uuid_from_string(uuid); + auto tcoperations = replica->get_task_operations(tcuuid); + auto operations = Operation::operations(tcoperations); + + // Sort by type (Create < Update < Delete < UndoPoint) and then by timestamp. + std::sort(operations.begin(), operations.end()); + + long last_timestamp = 0; + for (size_t i = 0; i < operations.size(); i++) { + auto& op = operations[i]; + + // Only display updates -- creation and deletion aren't interesting. + if (!op.is_update()) { + continue; + } + + // Group operations that occur within 1s of this one. This is a heuristic + // for operations performed in the same `task` invocation, and allows e.g., + // `task done end:-2h` to take the updated `end` value into account. It also + // groups these events into a single "row" of the table for better layout. + size_t group_start = i; + for (i++; i < operations.size(); i++) { + auto& op2 = operations[i]; + if (!op2.is_update() || op2.get_timestamp() - op.get_timestamp() > 1) { + break; + } + } + size_t group_end = i; + i--; + + std::optional msg = + formatForInfo(operations, group_start, group_end, dateformat, last_timestamp); + + if (!msg) { + continue; + } + + int row = journal.addRow(); + Datetime timestamp(op.get_timestamp()); + journal.set(row, 0, timestamp.toString(dateformat)); + journal.set(row, 1, *msg); + } + } + out << optionalBlankLine() << view.render() << '\n'; if (urgencyDetails.rows() > 0) out << urgencyDetails.render() << '\n'; + + if (journal.rows() > 0) out << journal.render() << '\n'; } output = out.str(); @@ -502,3 +564,105 @@ void CmdInfo::urgencyTerm(Table& view, const std::string& label, float measure, } //////////////////////////////////////////////////////////////////////////////// +std::optional CmdInfo::formatForInfo(const std::vector& operations, + size_t group_start, size_t group_end, + const std::string& dateformat, long& last_start) { + std::stringstream out; + for (auto i = group_start; i < group_end; i++) { + auto& operation = operations[i]; + assert(operation.is_update()); + + // Extract the parts of the Update operation. + std::string prop = operation.get_property(); + std::optional value = operation.get_value(); + std::optional old_value = operation.get_old_value(); + Datetime timestamp(operation.get_timestamp()); + + // Never care about modifying the modification time, or the legacy properties `depends` and + // `tags`. + if (prop == "modified" || prop == "depends" || prop == "tags") { + continue; + } + + // Handle property deletions + if (!value && old_value) { + if (Task::isAnnotationAttr(prop)) { + out << format("Annotation '{1}' deleted.\n", *old_value); + } else if (Task::isTagAttr(prop)) { + out << format("Tag '{1}' deleted.\n", Task::attr2Tag(prop)); + } else if (Task::isDepAttr(prop)) { + out << format("Dependency on '{1}' deleted.\n", Task::attr2Dep(prop)); + } else if (prop == "start") { + Datetime started(last_start); + Datetime stopped = timestamp; + + // If any update in this group sets the `end` property, use that instead of the + // timestamp deleting the `start` property as the stop time. + // See https://github.com/GothenburgBitFactory/taskwarrior/issues/2514 + for (auto i = group_start; i < group_end; i++) { + auto& op = operations[i]; + assert(op.is_update()); + if (op.get_property() == "end") { + try { + stopped = op.get_value().value(); + } catch (std::string) { + // Fall back to the 'start' timestamp if its value is un-parseable. + stopped = op.get_timestamp(); + } + } + } + + out << format("{1} deleted (duration: {2}).", Lexer::ucFirst(prop), + Duration(stopped - started).format()) + << "\n"; + } else { + out << format("{1} deleted.\n", Lexer::ucFirst(prop)); + } + } + + // Handle property additions. + if (value && !old_value) { + if (Task::isAnnotationAttr(prop)) { + out << format("Annotation of '{1}' added.\n", *value); + } else if (Task::isTagAttr(prop)) { + out << format("Tag '{1}' added.\n", Task::attr2Tag(prop)); + } else if (Task::isDepAttr(prop)) { + out << format("Dependency on '{1}' added.\n", Task::attr2Dep(prop)); + } else { + // Record the last start time for later duration calculation. + if (prop == "start") { + last_start = Datetime(value.value()).toEpoch(); + } + + out << format("{1} set to '{2}'.", Lexer::ucFirst(prop), + renderAttribute(prop, *value, dateformat)) + << "\n"; + } + } + + // Handle property changes. + if (value && old_value) { + if (Task::isTagAttr(prop) || Task::isDepAttr(prop)) { + // Dependencies and tags do not have meaningful values. + } else if (Task::isAnnotationAttr(prop)) { + out << format("Annotation changed to '{1}'.\n", *value); + } else { + // Record the last start time for later duration calculation. + if (prop == "start") { + last_start = Datetime(value.value()).toEpoch(); + } + + out << format("{1} changed from '{2}' to '{3}'.", Lexer::ucFirst(prop), + renderAttribute(prop, *old_value, dateformat), + renderAttribute(prop, *value, dateformat)) + << "\n"; + } + } + } + + if (out.str().length() == 0) return std::nullopt; + + return out.str(); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdInfo.h b/src/commands/CmdInfo.h index 06516e920..196d8e8b0 100644 --- a/src/commands/CmdInfo.h +++ b/src/commands/CmdInfo.h @@ -28,9 +28,12 @@ #define INCLUDED_CMDINFO #include +#include #include +#include #include +#include class CmdInfo : public Command { public: @@ -39,6 +42,10 @@ class CmdInfo : public Command { private: void urgencyTerm(Table&, const std::string&, float, float) const; + // Format a group of update operations for display in `task info`. + std::optional formatForInfo(const std::vector& operations, + size_t group_start, size_t group_end, + const std::string& dateformat, long& last_start); }; #endif diff --git a/src/taskchampion-cpp/src/lib.rs b/src/taskchampion-cpp/src/lib.rs index 2fc2f92df..829394f63 100644 --- a/src/taskchampion-cpp/src/lib.rs +++ b/src/taskchampion-cpp/src/lib.rs @@ -133,6 +133,9 @@ mod ffi { /// Get an existing task by its UUID. fn get_task_data(&mut self, uuid: Uuid) -> Result; + /// Get the operations for a task task by its UUID. + fn get_task_operations(&mut self, uuid: Uuid) -> Result>; + /// Return the operations back to and including the last undo point, or since the last sync if /// no undo point is found. fn get_undo_operations(&mut self) -> Result>; @@ -529,6 +532,10 @@ impl Replica { Ok(self.0.get_task_data(uuid.into())?.into()) } + fn get_task_operations(&mut self, uuid: ffi::Uuid) -> Result, CppError> { + Ok(from_tc_operations(self.0.get_task_operations(uuid.into())?)) + } + fn get_undo_operations(&mut self) -> Result, CppError> { Ok(from_tc_operations(self.0.get_undo_operations()?)) } @@ -921,6 +928,26 @@ mod test { assert_eq!(t.take().get_uuid(), uuid); } + #[test] + fn get_task_operations() { + cxx::let_cxx_string!(prop = "prop"); + cxx::let_cxx_string!(value = "value"); + let mut rep = new_replica_in_memory().unwrap(); + + let uuid = uuid_v4(); + assert!(rep.get_task_operations(uuid).unwrap().is_empty()); + + let mut operations = new_operations(); + let mut t = create_task(uuid, &mut operations); + t.update(&prop, &value, &mut operations); + rep.commit_operations(operations).unwrap(); + + let ops = rep.get_task_operations(uuid).unwrap(); + assert_eq!(ops.len(), 2); + assert!(ops[0].is_create()); + assert!(ops[1].is_update()); + } + #[test] fn task_properties() { cxx::let_cxx_string!(prop = "prop"); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b6813661e..0de39af75 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -174,6 +174,7 @@ set (pythonTests timesheet.test.py tw-1379.test.py tw-1837.test.py + tw-1999.test.py tw-20.test.py tw-2575.test.py tw-262.test.py diff --git a/test/info.test.py b/test/info.test.py index 4d6945936..79ffe80fa 100755 --- a/test/info.test.py +++ b/test/info.test.py @@ -98,6 +98,9 @@ class TestInfoCommand(TestCase): self.assertRegex(out, r"Urgency\s+\d+(\.\d+)?") self.assertRegex(out, r"Priority\s+H") + self.assertRegex(out, r"Annotation of 'bar' added\.") + self.assertRegex(out, r"Tag 'tag' added\.") + self.assertRegex(out, r"tatus set to 'recurring'\.") self.assertIn("project", out) self.assertIn("active", out) self.assertIn("annotations", out) diff --git a/test/tw-1999.test.py b/test/tw-1999.test.py new file mode 100755 index 000000000..47bee92d8 --- /dev/null +++ b/test/tw-1999.test.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +############################################################################### +# +# 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 +# +############################################################################### + +import sys +import os +import unittest + +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Task, TestCase + + +class TestBug1999(TestCase): + """Bug 1999: Taskwarrior reports wrong active time""" + + def setUp(self): + self.t = Task() + + def test_correct_active_time(self): + """Ensure correct active time locally""" + desc = "Testing task" + self.t(("add", desc)) + self.t(("start", "1")) + self.t.faketime("+10m") + self.t(("stop", "1")) + + code, out, err = self.t(("info", "1")) + self.assertRegex(out, "duration: 0:10:0[0-5]") + + +if __name__ == "__main__": + from simpletap import TAPTestRunner + + unittest.main(testRunner=TAPTestRunner()) + +# vim: ai sts=4 et sw=4 ft=python diff --git a/test/tw-2514.test.sh b/test/tw-2514.test.sh index 71343ed6e..4033bde81 100755 --- a/test/tw-2514.test.sh +++ b/test/tw-2514.test.sh @@ -6,7 +6,6 @@ task add Something I did yesterday task 1 mod start:yesterday+18h task 1 done end:yesterday+20h -# this does not work without journal.info # Check that 2 hour interval is reported by task info -#task info | grep -F "Start deleted" -#[[ ! -z `task info | grep -F "Start deleted (duration: 2:00:00)."` ]] +task info | grep -F "Start deleted" +[[ ! -z `task info | grep -F "Start deleted (duration: 2:00:00)."` ]] From dcc8a8cddeaf82e5d55cdc717c84a8089b6afcf3 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 6 Nov 2024 07:40:16 -0500 Subject: [PATCH 137/242] bump libshared for bold 256color support (#3670) In particular, commit https://github.com/GothenburgBitFactory/libshared/commit/47a750c385133dd14a0957691fe21cbe46e80b10. --- src/libshared | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libshared b/src/libshared index e0d493d16..47a750c38 160000 --- a/src/libshared +++ b/src/libshared @@ -1 +1 @@ -Subproject commit e0d493d16357d14f0f6092c6670777cde8eca76c +Subproject commit 47a750c385133dd14a0957691fe21cbe46e80b10 From a2f9b92d6cf8e383645359cc6faeb75d4f73ec94 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 7 Nov 2024 14:56:34 -0500 Subject: [PATCH 138/242] Better undo output (and remove `undo.style` config) (#3672) --- doc/man/taskrc.5.in | 9 -- scripts/vim/syntax/taskrc.vim | 1 - src/Context.cpp | 1 - src/TDB2.cpp | 101 ------------------- src/TDB2.h | 4 - src/Task.cpp | 185 ---------------------------------- src/Task.h | 2 - src/commands/CmdShow.cpp | 1 - src/commands/CmdUndo.cpp | 95 ++++++++++++++++- src/commands/CmdUndo.h | 7 +- test/undo.test.py | 30 +----- 11 files changed, 105 insertions(+), 331 deletions(-) diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 96767beea..7ad152ab6 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -508,15 +508,6 @@ weekly recurring task is added with a due date of tomorrow, and recurrence.limit is set to 2, then a report will list 2 pending recurring tasks, one for tomorrow, and one for a week from tomorrow. -.TP -.B undo.style=side -When the 'undo' command is run, Taskwarrior presents a before and after -comparison of the data. This can be in either the 'side' style, which compares -values side-by-side in a table, or 'diff' style, which uses a format similar to -the 'diff' command. - -Currently not supported. - .TP .B abbreviation.minimum=2 Minimum length of any abbreviated command/value. This means that "ve", "ver", diff --git a/scripts/vim/syntax/taskrc.vim b/scripts/vim/syntax/taskrc.vim index 0e5eb13d0..87cd568e4 100644 --- a/scripts/vim/syntax/taskrc.vim +++ b/scripts/vim/syntax/taskrc.vim @@ -168,7 +168,6 @@ syn match taskrcGoodKey '^\s*\Vsugar='he=e-1 syn match taskrcGoodKey '^\s*\Vsync.\(server.\(url\|origin\|client_id\|encryption_secret\)\|local.server_dir\)='he=e-1 syn match taskrcGoodKey '^\s*\Vtag.indicator='he=e-1 syn match taskrcGoodKey '^\s*\Vuda.\S\{-}.\(default\|type\|label\|values\|indicator\)='he=e-1 -syn match taskrcGoodKey '^\s*\Vundo.style='he=e-1 syn match taskrcGoodKey '^\s*\Vurgency.active.coefficient='he=e-1 syn match taskrcGoodKey '^\s*\Vurgency.age.coefficient='he=e-1 syn match taskrcGoodKey '^\s*\Vurgency.age.max='he=e-1 diff --git a/src/Context.cpp b/src/Context.cpp index 2ba1352fb..9b473b98e 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -127,7 +127,6 @@ std::string configurationDefaults = "dependency.indicator=D # What to show as a dependency indicator\n" "recurrence.indicator=R # What to show as a task recurrence indicator\n" "recurrence.limit=1 # Number of future recurring pending tasks\n" - "undo.style=side # Undo style - can be 'side', or 'diff'\n" "regex=1 # Assume all search/filter strings are " "regexes\n" "xterm.title=0 # Sets xterm title for some commands\n" diff --git a/src/TDB2.cpp b/src/TDB2.cpp index d7fd81017..aab85a8e9 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -226,107 +226,6 @@ void TDB2::get_changes(std::vector& changes) { [](const auto& kv) { return kv.second; }); } -//////////////////////////////////////////////////////////////////////////////// -void TDB2::revert() { - rust::Vec undo_ops = replica()->get_undo_operations(); - if (undo_ops.size() == 0) { - std::cout << "No operations to undo.\n"; - return; - } - if (confirm_revert(undo_ops)) { - // Note that commit_reversed_operations rebuilds the working set, so that - // need not be done here. - if (!replica()->commit_reversed_operations(std::move(undo_ops))) { - std::cout << "Could not undo: other operations have occurred."; - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -bool TDB2::confirm_revert(rust::Vec& undo_ops) { - // TODO: convert to Operation and use that type for display, similar to CmdInfo. - - // Count non-undo operations - int ops_count = 0; - for (auto& op : undo_ops) { - if (!op.is_undo_point()) { - ops_count++; - } - } - - std::cout << "The following " << ops_count << " operations would be reverted:\n"; - for (auto& op : undo_ops) { - if (op.is_undo_point()) { - continue; - } - - std::cout << "- "; - std::string uuid = static_cast(op.get_uuid().to_string()); - if (op.is_create()) { - std::cout << "Create " << uuid; - } else if (op.is_delete()) { - std::cout << "Delete "; - auto old_task = op.get_old_task(); - bool found_description = false; - for (auto& pv : old_task) { - if (static_cast(pv.prop) == "description") { - std::cout << static_cast(pv.value) << " (" << uuid << ")"; - found_description = true; - } - } - if (!found_description) { - std::cout << uuid; - } - } else if (op.is_update()) { - std::cout << "Update " << uuid << "\n"; - std::string property; - op.get_property(property); - std::string value; - bool have_value = op.get_value(value); - std::string old_value; - bool have_old_value = op.get_old_value(old_value); - std::cout << " " << property << ": "; - std::cout << (have_old_value ? old_value : "") << " -> "; - std::cout << (have_value ? value : ""); - } - std::cout << "\n"; - } - return !Context::getContext().config.getBoolean("confirmation") || - confirm( - "The undo command is not reversible. Are you sure you want to revert to the previous " - "state?"); - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -void TDB2::show_diff(const std::string& current, const std::string& prior, - const std::string& when) { - Datetime lastChange(strtoll(when.c_str(), nullptr, 10)); - - // Set the colors. - Color color_red( - Context::getContext().color() ? Context::getContext().config.get("color.undo.before") : ""); - Color color_green( - Context::getContext().color() ? Context::getContext().config.get("color.undo.after") : ""); - - auto before = prior == "" ? Task() : Task(prior); - auto after = Task(current); - - if (Context::getContext().config.get("undo.style") == "side") { - Table view = before.diffForUndoSide(after); - - std::cout << '\n' - << format("The last modification was made {1}", lastChange.toString()) << '\n' - << '\n' - << view.render() << '\n'; - } - - else if (Context::getContext().config.get("undo.style") == "diff") { - Table view = before.diffForUndoPatch(after, lastChange); - std::cout << '\n' << view.render() << '\n'; - } -} - //////////////////////////////////////////////////////////////////////////////// void TDB2::gc() { Timer timer; diff --git a/src/TDB2.h b/src/TDB2.h index 9de0d9d7e..69ffd0f20 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -51,7 +51,6 @@ class TDB2 { void modify(Task &); void purge(Task &); void get_changes(std::vector &); - void revert(); void gc(); void expire_tasks(); int latest_id(); @@ -75,8 +74,6 @@ class TDB2 { void dump(); - bool confirm_revert(rust::Vec &); - rust::Box &replica(); private: @@ -93,7 +90,6 @@ class TDB2 { const rust::Box &working_set(); void maybe_add_undo_point(rust::Vec &); - static void show_diff(const std::string &, const std::string &, const std::string &); }; #endif diff --git a/src/Task.cpp b/src/Task.cpp index 72cf41975..bab293341 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -2152,188 +2152,3 @@ std::string Task::diff(const Task& after) const { } //////////////////////////////////////////////////////////////////////////////// -// Similar to diff, but formatted as a side-by-side table for an Undo preview -Table Task::diffForUndoSide(const Task& after) const { - // Set the colors. - Color color_red( - Context::getContext().color() ? Context::getContext().config.get("color.undo.before") : ""); - Color color_green( - Context::getContext().color() ? Context::getContext().config.get("color.undo.after") : ""); - - // Attributes are all there is, so figure the different attribute names - // between before and after. - Table view; - view.width(Context::getContext().getWidth()); - view.intraPadding(2); - view.add(""); - view.add("Prior Values"); - view.add("Current Values"); - setHeaderUnderline(view); - - if (!is_empty()) { - const Task& before = *this; - - std::vector beforeAtts; - for (auto& att : before.data) beforeAtts.push_back(att.first); - - std::vector afterAtts; - for (auto& att : after.data) afterAtts.push_back(att.first); - - std::vector beforeOnly; - std::vector afterOnly; - listDiff(beforeAtts, afterAtts, beforeOnly, afterOnly); - - int row; - for (auto& name : beforeOnly) { - row = view.addRow(); - view.set(row, 0, name); - view.set(row, 1, renderAttribute(name, before.get(name)), color_red); - } - - for (auto& att : before.data) { - std::string priorValue = before.get(att.first); - std::string currentValue = after.get(att.first); - - if (currentValue != "") { - row = view.addRow(); - view.set(row, 0, att.first); - view.set(row, 1, renderAttribute(att.first, priorValue), - (priorValue != currentValue ? color_red : Color())); - view.set(row, 2, renderAttribute(att.first, currentValue), - (priorValue != currentValue ? color_green : Color())); - } - } - - for (auto& name : afterOnly) { - row = view.addRow(); - view.set(row, 0, name); - view.set(row, 2, renderAttribute(name, after.get(name)), color_green); - } - } else { - int row; - for (auto& att : after.data) { - row = view.addRow(); - view.set(row, 0, att.first); - view.set(row, 2, renderAttribute(att.first, after.get(att.first)), color_green); - } - } - - return view; -} - -//////////////////////////////////////////////////////////////////////////////// -// Similar to diff, but formatted as a diff for an Undo preview -Table Task::diffForUndoPatch(const Task& after, const Datetime& lastChange) const { - // This style looks like this: - // --- before 2009-07-04 00:00:25.000000000 +0200 - // +++ after 2009-07-04 00:00:45.000000000 +0200 - // - // - name: old // att deleted - // + name: - // - // - name: old // att changed - // + name: new - // - // - name: - // + name: new // att added - // - - // Set the colors. - Color color_red( - Context::getContext().color() ? Context::getContext().config.get("color.undo.before") : ""); - Color color_green( - Context::getContext().color() ? Context::getContext().config.get("color.undo.after") : ""); - - const Task& before = *this; - - // Generate table header. - Table view; - view.width(Context::getContext().getWidth()); - view.intraPadding(2); - view.add(""); - view.add(""); - - int row = view.addRow(); - view.set(row, 0, "--- previous state", color_red); - view.set(row, 1, "Undo will restore this state", color_red); - - row = view.addRow(); - view.set(row, 0, "+++ current state ", color_green); - view.set(row, 1, - format("Change made {1}", - lastChange.toString(Context::getContext().config.get("dateformat"))), - color_green); - - view.addRow(); - - // Add rows to table showing diffs. - std::vector all = Context::getContext().getColumns(); - - // Now factor in the annotation attributes. - for (auto& it : before.data) - if (it.first.substr(0, 11) == "annotation_") all.push_back(it.first); - - for (auto& it : after.data) - if (it.first.substr(0, 11) == "annotation_") all.push_back(it.first); - - // Now render all the attributes. - std::sort(all.begin(), all.end()); - - std::string before_att; - std::string after_att; - std::string last_att; - for (auto& a : all) { - if (a != last_att) // Skip duplicates. - { - last_att = a; - - before_att = before.get(a); - after_att = after.get(a); - - // Don't report different uuid. - // Show nothing if values are the unchanged. - if (a == "uuid" || before_att == after_att) { - // Show nothing - no point displaying that which did not change. - - // row = view.addRow (); - // view.set (row, 0, *a + ":"); - // view.set (row, 1, before_att); - } - - // Attribute deleted. - else if (before_att != "" && after_att == "") { - row = view.addRow(); - view.set(row, 0, '-' + a + ':', color_red); - view.set(row, 1, before_att, color_red); - - row = view.addRow(); - view.set(row, 0, '+' + a + ':', color_green); - } - - // Attribute added. - else if (before_att == "" && after_att != "") { - row = view.addRow(); - view.set(row, 0, '-' + a + ':', color_red); - - row = view.addRow(); - view.set(row, 0, '+' + a + ':', color_green); - view.set(row, 1, after_att, color_green); - } - - // Attribute changed. - else { - row = view.addRow(); - view.set(row, 0, '-' + a + ':', color_red); - view.set(row, 1, before_att, color_red); - - row = view.addRow(); - view.set(row, 0, '+' + a + ':', color_green); - view.set(row, 1, after_att, color_green); - } - } - } - - return view; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Task.h b/src/Task.h index 1f63b7f5a..66e18b13b 100644 --- a/src/Task.h +++ b/src/Task.h @@ -181,8 +181,6 @@ class Task { #endif std::string diff(const Task& after) const; - Table diffForUndoSide(const Task& after) const; - Table diffForUndoPatch(const Task& after, const Datetime& lastChange) const; private: int determineVersion(const std::string&); diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index bacc5c464..df92df6a1 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -202,7 +202,6 @@ int CmdShow::execute(std::string& output) { " sync.server.url" " sync.server.origin" " tag.indicator" - " undo.style" " urgency.active.coefficient" " urgency.scheduled.coefficient" " urgency.annotations.coefficient" diff --git a/src/commands/CmdUndo.cpp b/src/commands/CmdUndo.cpp index b462ae590..a5257ab93 100644 --- a/src/commands/CmdUndo.cpp +++ b/src/commands/CmdUndo.cpp @@ -29,6 +29,13 @@ #include #include +#include +#include + +#include +#include + +#include "shared.h" //////////////////////////////////////////////////////////////////////////////// CmdUndo::CmdUndo() { @@ -47,8 +54,94 @@ CmdUndo::CmdUndo() { //////////////////////////////////////////////////////////////////////////////// int CmdUndo::execute(std::string&) { - Context::getContext().tdb2.revert(); + auto& replica = Context::getContext().tdb2.replica(); + rust::Vec undo_ops = replica->get_undo_operations(); + if (confirm_revert(Operation::operations(undo_ops))) { + // Note that commit_reversed_operations rebuilds the working set, so that + // need not be done here. + if (!replica->commit_reversed_operations(std::move(undo_ops))) { + std::cout << "Could not undo: other operations have occurred."; + } + } return 0; } //////////////////////////////////////////////////////////////////////////////// +bool CmdUndo::confirm_revert(const std::vector& undo_ops) { + // Count non-undo operations + int ops_count = 0; + for (auto& op : undo_ops) { + if (!op.is_undo_point()) { + ops_count++; + } + } + if (ops_count == 0) { + std::cout << "No operations to undo.\n"; + return true; + } + + std::cout << "The following " << ops_count << " operations would be reverted:\n"; + + Table view; + if (Context::getContext().config.getBoolean("obfuscate")) view.obfuscate(); + view.width(Context::getContext().getWidth()); + view.add("Uuid"); + view.add("Modification"); + + std::string last_uuid; + std::stringstream mods; + for (auto& op : undo_ops) { + if (op.is_undo_point()) { + continue; + } + + if (last_uuid != op.get_uuid()) { + if (last_uuid.size() != 0) { + int row = view.addRow(); + view.set(row, 0, last_uuid); + view.set(row, 1, mods.str()); + } + last_uuid = op.get_uuid(); + mods.clear(); + } + + if (op.is_create()) { + mods << "Create task\n"; + } else if (op.is_delete()) { + mods << "Delete (purge) task"; + } else if (op.is_update()) { + auto property = op.get_property(); + auto old_value = op.get_old_value(); + auto value = op.get_value(); + if (Task::isTagAttr(property)) { + if (value && *value == "x") { + mods << "Add tag '" << Task::attr2Tag(property) << "'\n"; + continue; + } else if (!value && old_value && *old_value == "x") { + mods << "Remove tag '" << Task::attr2Tag(property) << "'\n"; + continue; + } + } + if (old_value && value) { + mods << "Update property '" << property << "' from '" << *old_value << "' to '" << *value + << "'\n"; + } else if (old_value) { + mods << "Delete property '" << property << "' (was '" << *old_value << "')\n"; + } else if (value) { + mods << "Add property '" << property << "' with value '" << *value << "'\n"; + } + } + } + int row = view.addRow(); + view.set(row, 0, last_uuid); + view.set(row, 1, mods.str()); + std::cout << view.render() << "\n"; + + return !Context::getContext().config.getBoolean("confirmation") || + confirm( + "The undo command is not reversible. Are you sure you want to revert to the previous " + "state?"); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdUndo.h b/src/commands/CmdUndo.h index 40993311f..590184178 100644 --- a/src/commands/CmdUndo.h +++ b/src/commands/CmdUndo.h @@ -28,13 +28,18 @@ #define INCLUDED_CMDUNDO #include +#include #include +#include class CmdUndo : public Command { public: CmdUndo(); - int execute(std::string&); + int execute(std::string &); + + private: + bool confirm_revert(const std::vector &); }; #endif diff --git a/test/undo.test.py b/test/undo.test.py index b0beca1b1..c3ce12af0 100755 --- a/test/undo.test.py +++ b/test/undo.test.py @@ -74,33 +74,13 @@ class TestUndoStyle(TestCase): self.t("add one project:foo priority:H") self.t("1 modify +tag project:bar priority:") - @unittest.expectedFailure # undo diffs are not supported - def test_undo_side_style(self): - """Test that 'rc.undo.style:side' generates the right output""" - self.t.config("undo.style", "side") + def test_undo_output(self): + """Test that 'task undo' generates the right output""" code, out, err = self.t("undo", input="n\n") self.assertNotRegex(out, "-tags:\\s*\n\\+tags:\\s+tag") - self.assertRegex(out, r"tags\s+tag\s*") - - @unittest.expectedFailure # undo diffs are not supported - def test_undo_diff_style(self): - """Test that 'rc.undo.style:diff' generates the right output""" - self.t.config("undo.style", "diff") - code, out, err = self.t("undo", input="n\n") - self.assertRegex(out, "-tags:\\s*\n\\+tags:\\s+tag") - self.assertNotRegex(out, r"tags\s+tag\s*") - - def test_undo_diff_operations(self): - code, out, err = self.t("undo", input="n\n") - - # If the clock ticks a second between `add` and `modify` there is a - # fifth operation setting the `modified` property. - self.assertRegex(out, "The following [4|5] operations would be reverted:") - - self.assertIn("tag_tag: -> x", out) - self.assertIn("tags: -> tag", out) - self.assertIn("project: foo -> bar", out) - self.assertIn("priority: H -> ", out) + self.assertRegex(out, r"Delete property 'priority'") + self.assertRegex(out, r"Update property 'project'") + self.assertRegex(out, r"Add tag 'tag'") class TestBug634(TestCase): From 54a94bd18c304c6b16afb1245f88a2adcf0d0328 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 8 Nov 2024 07:10:04 -0500 Subject: [PATCH 139/242] include bubblegum-256.theme in default .taskrc (#3673) --- src/Context.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Context.cpp b/src/Context.cpp index 9b473b98e..c858f4373 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -1171,6 +1171,7 @@ void Context::createDefaultConfig() { << "\n# Color theme (uncomment one to use)\n" << "#include light-16.theme\n" << "#include light-256.theme\n" + << "#include bubblegum-256.theme\n" << "#include dark-16.theme\n" << "#include dark-256.theme\n" << "#include dark-red-256.theme\n" From 7a092bea037517be4dc839ee8141b8d6b00738eb Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 12 Nov 2024 14:52:22 -0500 Subject: [PATCH 140/242] Release v3.2.0 (#3679) --- CMakeLists.txt | 2 +- ChangeLog | 27 +++++++++++++++++++++++++-- src/commands/CmdNews.cpp | 15 +++++++++++++++ src/commands/CmdNews.h | 1 + 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e12fcf675..f73d7a5ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ enable_testing() set (CMAKE_EXPORT_COMPILE_COMMANDS ON) project (task - VERSION 3.1.0 + VERSION 3.2.0 DESCRIPTION "Taskwarrior - a command-line TODO list manager" HOMEPAGE_URL https://taskwarrior.org/) diff --git a/ChangeLog b/ChangeLog index 7b3e87c8e..7595a9835 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,30 @@ ------ current release --------------------------- +3.2.0 - + + - Support for the journal in `task info` has been restored (#3671) and the + task info output no longer contains `tag_` values (#3619). + - The `rc.weekstart` value now affects calculation of week numbers in + expressions like `2013-W49` (#2654). + - Build-time flag `ENABLE_TLS_NATIVE_ROOTS` will cause `task sync` to use the + system TLS roots instead of its built-in roots to authenticate the server (#3660). + - The output from `task undo` is now more human-readable. The `undo.style` + configuraiton option, which has had no effect since 3.0.0, is now removed (3672). + - Fetching pending tasks is now more efficient (#3661). + +Thanks to the following people for contributions to this release: + + - Denis Zh. + - Dustin J. Mitchell + - Fredrik Lanker + - Gagan Nagaraj + - Jan Christian Grünhage + - Scott Mcdermott + - Thomas Lauf + - Tobias Predel + +------ old releases ------------------------------ + 3.1.0 - - Support for `task purge` has been restored, and new support added for automatically @@ -35,8 +60,6 @@ Thanks to the following people for contributions to this release: - Steve Dondley - Will R S Hansen ------- old releases ------------------------------ - 3.0.2 - - Fix an accidentally-included debug print which polluted output of diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 7a3fbb7dc..f88da6812 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -158,6 +158,7 @@ std::vector NewsItem::all() { version2_6_0(items); version3_0_0(items); version3_1_0(items); + version3_2_0(items); return items; } @@ -500,6 +501,20 @@ void NewsItem::version3_1_0(std::vector& items) { items.push_back(news); } +void NewsItem::version3_2_0(std::vector& items) { + Version version("3.2.0"); + NewsItem info{ + version, + /*title=*/"`task info` Journal Restored", + /*bg_title=*/"", + /*background=*/"", + /*punchline=*/"", + /*update=*/ + "Support for the \"journal\" output in `task info` has been restored. The command now\n" + "displays a list of changes made to the task, with timestamps.\n\n"}; + items.push_back(info); +} + //////////////////////////////////////////////////////////////////////////////// int CmdNews::execute(std::string& output) { auto words = Context::getContext().cli2.getWords(); diff --git a/src/commands/CmdNews.h b/src/commands/CmdNews.h index 7423954f7..554f12bdd 100644 --- a/src/commands/CmdNews.h +++ b/src/commands/CmdNews.h @@ -51,6 +51,7 @@ class NewsItem { static void version2_6_0(std::vector&); static void version3_0_0(std::vector&); static void version3_1_0(std::vector&); + static void version3_2_0(std::vector&); private: NewsItem(Version, const std::string&, const std::string& = "", const std::string& = "", From 3e8bda6a2384b098071c41682d1c91b0428cf8eb Mon Sep 17 00:00:00 2001 From: Scott Mcdermott Date: Wed, 13 Nov 2024 22:04:25 -0500 Subject: [PATCH 141/242] release 3.2.0 links wrong PR for weekstart change (#3681) ChangeLog: fix typo in linked PR number for weekstart change off by 1000 --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 7595a9835..a09114d5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,7 +5,7 @@ - Support for the journal in `task info` has been restored (#3671) and the task info output no longer contains `tag_` values (#3619). - The `rc.weekstart` value now affects calculation of week numbers in - expressions like `2013-W49` (#2654). + expressions like `2013-W49` (#3654). - Build-time flag `ENABLE_TLS_NATIVE_ROOTS` will cause `task sync` to use the system TLS roots instead of its built-in roots to authenticate the server (#3660). - The output from `task undo` is now more human-readable. The `undo.style` From 8cc4c461d6902539e12bf6a3e9597813b400fa1e Mon Sep 17 00:00:00 2001 From: Chongyun Lee <45286352+licy183@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:09:08 +0800 Subject: [PATCH 142/242] Fix compile with libc++ 18 (#3680) --- src/Operation.cpp | 2 +- src/Operation.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Operation.cpp b/src/Operation.cpp index 4bc4ecc48..20796803b 100644 --- a/src/Operation.cpp +++ b/src/Operation.cpp @@ -47,7 +47,7 @@ Operation& Operation::operator=(const Operation& other) { } //////////////////////////////////////////////////////////////////////////////// -bool Operation::operator<(Operation& other) const { +bool Operation::operator<(const Operation& other) const { if (is_create()) { return !other.is_create(); } else if (is_update()) { diff --git a/src/Operation.h b/src/Operation.h index 707dffd89..485b422bd 100644 --- a/src/Operation.h +++ b/src/Operation.h @@ -78,7 +78,7 @@ class Operation { // Define a partial order on Operations: // - Create < Update < Delete < UndoPoint // - Given two updates, sort by timestamp - bool operator<(Operation &other) const; + bool operator<(const Operation &other) const; private: const tc::Operation *op; From 01ced3238e87b71647fcdf8179907524e97ff54a Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Fri, 15 Nov 2024 22:32:05 +0100 Subject: [PATCH 143/242] add cpp standard flag to cmake (#3684) add DCMAKE_CXX_STANDARD flag See documentation in CMake. https://cmake.org/cmake/help/latest/prop_tgt/CXX_STANDARD.html --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 656dc6f96..69024309e 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v4.1.6 - name: Configure project - run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS=--coverage + run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS=--coverage -DCMAKE_CXX_STANDARD=17 - name: Build project run: cmake --build build --target test_runner --target task_executable From 096f94d3d14d70a37e25a2c82193452b3e014a16 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sat, 16 Nov 2024 13:45:44 -0500 Subject: [PATCH 144/242] Use Signal instead of PGP to contact me securely (#3685) --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 1fb4ee7bc..203bc5e64 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,6 +1,6 @@ # Security -To report a vulnerability, please contact [dustin@cs.uchicago.edu](mailto:dustin@cs.uchicago.edu), you may use GPG public-key D8097934A92E4B4210368102FF8B7AC6154E3226 which is available [here](https://keybase.io/djmitche/pgp_keys.asc?fingerprint=d8097934a92e4b4210368102ff8b7ac6154e3226). +To report a vulnerability, please contact Dustin via signal, [`djmitche.78`](https://signal.me/#eu/2T98jpkMAzvFL2wg3OkZnNrfhk1DFfu6eqkMEPqcAuCsLZPVk39A67rp4khmrMNF). Initial response is expected within ~48h. We kindly ask to follow the responsible disclosure model and refrain from sharing information until: From 98204b17a6b431ba660a6f1d9f21faf65a390d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kiet=C3=A4v=C3=A4inen?= <1026741+kietavainen@users.noreply.github.com> Date: Sun, 17 Nov 2024 22:18:28 +0200 Subject: [PATCH 145/242] Set CMake C++ standard (#3688) Instead of setting `-std` compiler flag directly, set the `CMAKE_CXX_STANDARD` variable. This lets CMake know the required C++ standard and evaluate the final compiler flag correctly, taking into account compile features set by `target_compile_features()`. This change preserves the existing behavior, where compiler extensions are disabled for other targets than Cygwin. --- cmake/CXXSniffer.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/CXXSniffer.cmake b/cmake/CXXSniffer.cmake index 07c06cdae..ef1ec6511 100644 --- a/cmake/CXXSniffer.cmake +++ b/cmake/CXXSniffer.cmake @@ -6,7 +6,8 @@ include (CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++17" _HAS_CXX17) if (_HAS_CXX17) - set (_CXX14_FLAGS "-std=c++17") + set (CMAKE_CXX_STANDARD 17) + set (CMAKE_CXX_EXTENSIONS OFF) else (_HAS_CXX17) message (FATAL_ERROR "C++17 support missing. Try upgrading your C++ compiler. If you have a good reason for using an outdated compiler, please let us know at support@gothenburgbitfactory.org.") endif (_HAS_CXX17) @@ -32,7 +33,7 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "GNU") set (GNUHURD true) elseif (${CMAKE_SYSTEM_NAME} STREQUAL "CYGWIN") set (CYGWIN true) - set (_CXX14_FLAGS "-std=gnu++17") + set (CMAKE_CXX_EXTENSIONS ON) else (${CMAKE_SYSTEM_NAME} MATCHES "Linux") set (UNKNOWN true) endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") From 066bb3e3314c184f9e0d043f987e04f3fafa2d2f Mon Sep 17 00:00:00 2001 From: geoffpaulsen <32056825+geoffpaulsen@users.noreply.github.com> Date: Thu, 21 Nov 2024 20:19:44 -0600 Subject: [PATCH 146/242] Updating the URL for Migration Documentation (#3694) --- src/commands/CmdCustom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index 0537f265d..5cf528662 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -242,7 +242,7 @@ int CmdCustom::execute(std::string& output) { Color warning = Color(Context::getContext().config.get("color.warning")); std::cerr << warning.colorize(format("Found existing '*.data' files in {1}", location)) << "\n"; std::cerr << " Taskwarrior's storage format changed in 3.0, requiring a manual migration.\n"; - std::cerr << " See https://github.com/GothenburgBitFactory/taskwarrior/releases.\n"; + std::cerr << " See https://taskwarrior.org/docs/upgrade-3/\n"; } feedback_backlog(); From caae3fa37b41d53f240f235f64214e1b9a9f31b3 Mon Sep 17 00:00:00 2001 From: Thomas Lauf Date: Mon, 25 Sep 2023 21:14:13 +0200 Subject: [PATCH 147/242] Use repository owner instead of actor to log into GitHub CR --- .github/workflows/docker-image.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index cf3607ddc..e9f662120 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -23,6 +23,10 @@ jobs: id-token: write steps: + - name: Create lowercase repository name + run: | + GHCR_REPOSITORY="${{ github.repository_owner }}" + echo "REPOSITORY=${GHCR_REPOSITORY,,}" >> ${GITHUB_ENV} - name: Checkout repository uses: actions/checkout@v4 with: @@ -35,7 +39,7 @@ jobs: uses: docker/login-action@v3.3.0 with: registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} + username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push Taskwarrior Docker image @@ -45,9 +49,9 @@ jobs: context: . file: "./docker/task.dockerfile" push: true - tags: ${{ env.REGISTRY }}/${{ github.actor }}/task:${{ github.ref_name }} + tags: ${{ env.REGISTRY }}/${{ env.REPOSITORY }}/task:${{ github.ref_name }} - name: Sign the published Docker image env: COSIGN_EXPERIMENTAL: "true" - run: cosign sign ${{ env.REGISTRY }}/${{ github.actor }}/task@${{ steps.build-and-push.outputs.digest }} + run: cosign sign ${{ env.REGISTRY }}/${{ env.REPOSITORY }}/task@${{ steps.build-and-push.outputs.digest }} From ed3667c19eb13fdfbf642eb772f39a3a5b886d32 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 17 Nov 2024 15:18:38 -0500 Subject: [PATCH 148/242] Revert "add cpp standard flag to cmake (#3684)" This reverts commit 01ced3238e87b71647fcdf8179907524e97ff54a. --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 69024309e..656dc6f96 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v4.1.6 - name: Configure project - run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS=--coverage -DCMAKE_CXX_STANDARD=17 + run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS=--coverage - name: Build project run: cmake --build build --target test_runner --target task_executable From 68c63372c176416dff10eba2921e3cfa309ccd87 Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Mon, 25 Nov 2024 05:54:43 +0100 Subject: [PATCH 149/242] change fedora39 to fedora41 runner (#3698) * change fedora39 to fedora41 runner * update github workflow runner file --- .github/workflows/tests.yaml | 4 ++-- docker-compose.yml | 4 ++-- test/docker/{fedora39 => fedora41} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename test/docker/{fedora39 => fedora41} (98%) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 656dc6f96..f7f0b05e2 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -134,9 +134,9 @@ jobs: - name: "Fedora 40" runner: ubuntu-latest dockerfile: fedora40 - - name: "Fedora 39" + - name: "Fedora 41" runner: ubuntu-latest - dockerfile: fedora39 + dockerfile: fedora41 - name: "Debian Testing" runner: ubuntu-latest dockerfile: debiantesting diff --git a/docker-compose.yml b/docker-compose.yml index 3bf2e0903..6d3b8bb92 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,10 +8,10 @@ services: security_opt: - label=type:container_runtime_t tty: true - test-fedora39: + test-fedora41: build: context: . - dockerfile: test/docker/fedora39 + dockerfile: test/docker/fedora41 network_mode: "host" security_opt: - label=type:container_runtime_t diff --git a/test/docker/fedora39 b/test/docker/fedora41 similarity index 98% rename from test/docker/fedora39 rename to test/docker/fedora41 index f4aa357a0..c1093bfe6 100644 --- a/test/docker/fedora39 +++ b/test/docker/fedora41 @@ -1,4 +1,4 @@ -FROM fedora:39 +FROM fedora:41 RUN dnf update -y RUN dnf install python3 git gcc gcc-c++ cmake make libuuid-devel libfaketime glibc-langpack-en curl -y From 5664182f5e190d4dc3ef824bf77e15ffd56001b5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 20:33:39 +0100 Subject: [PATCH 150/242] [pre-commit.ci] pre-commit autoupdate (#3701) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v19.1.3 → v19.1.4](https://github.com/pre-commit/mirrors-clang-format/compare/v19.1.3...v19.1.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 18eca6b11..9ffb6e81a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v19.1.3 + rev: v19.1.4 hooks: - id: clang-format types_or: [c++, c] From a99b6084e8b54b7c022fad8cf633ecf909b5ae18 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 25 Nov 2024 17:48:06 -0500 Subject: [PATCH 151/242] Only nag to read news when there's news to read (#3699) --- src/commands/CmdCustom.cpp | 7 +++---- src/commands/CmdNews.cpp | 25 +++++++++++++++++++++++++ src/commands/CmdNews.h | 2 ++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index 5cf528662..911ac719d 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -28,6 +28,7 @@ // cmake.h include header must come first #include +#include #include #include #include @@ -222,11 +223,9 @@ int CmdCustom::execute(std::string& output) { } // Inform user about the new release highlights if not presented yet - Version news_version(Context::getContext().config.get("news.version")); - Version current_version = Version::Current(); - auto should_nag = news_version != current_version && Context::getContext().verbose("news"); - if (should_nag) { + if (CmdNews::should_nag()) { std::ostringstream notice; + Version current_version = Version::Current(); notice << "Recently upgraded to " << current_version << ". " "Please run 'task news' to read highlights about the new release."; diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index f88da6812..8ef979882 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -615,3 +615,28 @@ int CmdNews::execute(std::string& output) { return 0; } + +bool CmdNews::should_nag() { + if (!Context::getContext().verbose("news")) { + return false; + } + + Version news_version(Context::getContext().config.get("news.version")); + if (!news_version.is_valid()) news_version = Version("2.6.0"); + + Version current_version = Version::Current(); + + if (news_version == current_version) { + return true; + } + + // Check if there are actually any interesting news items to show. + std::vector items = NewsItem::all(); + for (auto& item : items) { + if (item._version > news_version) { + return true; + } + } + + return false; +} diff --git a/src/commands/CmdNews.h b/src/commands/CmdNews.h index 554f12bdd..78055356f 100644 --- a/src/commands/CmdNews.h +++ b/src/commands/CmdNews.h @@ -63,6 +63,8 @@ class CmdNews : public Command { public: CmdNews(); int execute(std::string&); + + static bool should_nag(); }; #endif From 0b286460b69042b1ed409b981e42ea34c6172574 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 27 Nov 2024 17:59:31 -0500 Subject: [PATCH 152/242] Update rustls to latest version (#3704) --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a634a74b..68852d3ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,9 +1149,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.14" +version = "0.23.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" dependencies = [ "log", "once_cell", @@ -1195,9 +1195,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -1724,7 +1724,7 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls 0.23.14", + "rustls 0.23.18", "rustls-native-certs", "rustls-pki-types", "url", From 4797c4e17e582caf1ad74a7e9e3936a30ce5eebc Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 29 Nov 2024 09:12:20 -0500 Subject: [PATCH 153/242] Check Datetime addition when performing recurrence (#3708) --- src/main.h | 4 +++- src/recur.cpp | 42 +++++++++++++++++++++++++++++++++++++----- test/util_test.cpp | 7 +++++++ 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/main.h b/src/main.h index 9d15a09fa..4b2ff2a57 100644 --- a/src/main.h +++ b/src/main.h @@ -35,13 +35,15 @@ #include #include #include +#include #include #include // recur.cpp void handleRecurrence(); void handleUntil(); -Datetime getNextRecurrence(Datetime&, std::string&); +std::optional checked_add_datetime(Datetime& base, time_t delta); +std::optional getNextRecurrence(Datetime&, std::string&); bool generateDueDates(Task&, std::vector&); void updateRecurrenceMask(Task&); diff --git a/src/recur.cpp b/src/recur.cpp index f5c9f3979..d6bb47454 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -47,8 +47,24 @@ #include #include #include +#include +#include #include +// Add a `time_t` delta to a Datetime, checking for and returning nullopt on integer overflow. +std::optional checked_add_datetime(Datetime& base, time_t delta) { + // Datetime::operator+ takes an integer delta, so check that range + if (static_cast(std::numeric_limits::max()) < delta) { + return std::nullopt; + } + + // Check for time_t overflow in the Datetime. + if (std::numeric_limits::max() - base.toEpoch() < delta) { + return std::nullopt; + } + return base + delta; +} + //////////////////////////////////////////////////////////////////////////////// // Scans all tasks, and for any recurring tasks, determines whether any new // child tasks need to be generated to fill gaps. @@ -95,7 +111,12 @@ void handleRecurrence() { Datetime old_wait(t.get_date("wait")); Datetime old_due(t.get_date("due")); Datetime due(d); - rec.set("wait", format((due + (old_wait - old_due)).toEpoch())); + auto wait = checked_add_datetime(due, old_wait - old_due); + if (wait) { + rec.set("wait", format(wait->toEpoch())); + } else { + rec.remove("wait"); + } rec.setStatus(Task::waiting); mask += 'W'; } else { @@ -148,7 +169,8 @@ bool generateDueDates(Task& parent, std::vector& allDue) { auto recurrence_limit = Context::getContext().config.getInteger("recurrence.limit"); int recurrence_counter = 0; Datetime now; - for (Datetime i = due;; i = getNextRecurrence(i, recur)) { + Datetime i = due; + while (1) { allDue.push_back(i); if (specificEnd && i > until) { @@ -164,13 +186,23 @@ bool generateDueDates(Task& parent, std::vector& allDue) { if (i > now) ++recurrence_counter; if (recurrence_counter >= recurrence_limit) return true; + auto next = getNextRecurrence(i, recur); + if (next) { + i = *next; + } else { + return true; + } } return true; } //////////////////////////////////////////////////////////////////////////////// -Datetime getNextRecurrence(Datetime& current, std::string& period) { +/// Determine the next recurrence of the given period. +/// +/// If no such date can be calculated, such as with a very large period, returns +/// nullopt. +std::optional getNextRecurrence(Datetime& current, std::string& period) { auto m = current.month(); auto d = current.day(); auto y = current.year(); @@ -201,7 +233,7 @@ Datetime getNextRecurrence(Datetime& current, std::string& period) { else days = 1; - return current + (days * 86400); + return checked_add_datetime(current, days * 86400); } else if (unicodeLatinDigit(period[0]) && period[period.length() - 1] == 'm') { @@ -317,7 +349,7 @@ Datetime getNextRecurrence(Datetime& current, std::string& period) { if (!p.parse(period, idx)) throw std::string(format("The recurrence value '{1}' is not valid.", period)); - return current + p.toTime_t(); + return checked_add_datetime(current, p.toTime_t()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/test/util_test.cpp b/test/util_test.cpp index c4cf4e0bd..8e52a723a 100644 --- a/test/util_test.cpp +++ b/test/util_test.cpp @@ -33,6 +33,7 @@ #include #include +#include //////////////////////////////////////////////////////////////////////////////// int TEST_NAME(int, char**) { @@ -91,6 +92,12 @@ int TEST_NAME(int, char**) { t.ok(nontrivial(" \t\ta"), "nontrivial ' \\t\\ta' -> true"); t.ok(nontrivial("a\t\t "), "nontrivial 'a\\t\\t ' -> true"); + Datetime dt(1234526400); + Datetime max(std::numeric_limits::max()); + t.ok(checked_add_datetime(dt, 10).has_value(), "small delta"); + t.ok(!checked_add_datetime(dt, 0x100000000).has_value(), "delta > 32bit"); + t.ok(!checked_add_datetime(max, 1).has_value(), "huge base time"); + return 0; } From e5ab1bc7a5f5a1397f41dc7609f0d5d7ae73a441 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 29 Nov 2024 09:35:21 -0500 Subject: [PATCH 154/242] Update libshared (#3711) This brings in https://github.com/GothenburgBitFactory/libshared/pull/89 --- src/libshared | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libshared b/src/libshared index 47a750c38..1a06cb4ca 160000 --- a/src/libshared +++ b/src/libshared @@ -1 +1 @@ -Subproject commit 47a750c385133dd14a0957691fe21cbe46e80b10 +Subproject commit 1a06cb4caebdae3c5e58fe83e2fd2211d2959815 From c2cb7f36a710913787e5b5e9b26e6846ed89e30b Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 1 Dec 2024 10:22:26 -0500 Subject: [PATCH 155/242] Include cxxbridge-cmd in Cargo.lock, check version consistency (#3712) This adds cxxbridge-cmd to Cargo.lock per https://github.com/dtolnay/cxx/issues/1407#issuecomment-2509136343 It adds an MSRV to `src/taskchampion-cpp/Cargo.toml` so that the version of `Cargo.lock` is stil compatible with the MSRV. It additionally adds a check of the Cargo metadata for all of the cxx* versions agreeing, and for the MSRV's agreeing. --- .github/workflows/checks.yml | 16 +++++++ .github/workflows/metadata-check.sh | 73 +++++++++++++++++++++++++++++ .github/workflows/tests.yaml | 1 + Cargo.lock | 70 +++++++++++++++++++++++---- src/taskchampion-cpp/Cargo.toml | 15 +++++- 5 files changed, 164 insertions(+), 11 deletions(-) create mode 100755 .github/workflows/metadata-check.sh diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 01f37d13a..e1037c5ce 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -64,3 +64,19 @@ jobs: with: command: fmt args: --all -- --check + + cargo-metadata: + runs-on: ubuntu-latest + name: "Cargo Metadata" + steps: + - uses: actions/checkout@v4 + + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + components: rustfmt + toolchain: stable + override: true + + - name: "Check metadata" + run: ".github/workflows/metadata-check.sh" diff --git a/.github/workflows/metadata-check.sh b/.github/workflows/metadata-check.sh new file mode 100755 index 000000000..39f608eb7 --- /dev/null +++ b/.github/workflows/metadata-check.sh @@ -0,0 +1,73 @@ +#! /bin/bash + +# Check the 'cargo metadata' for various requirements + +set -e + +META=$(mktemp) +trap 'rm -rf -- "${META}"' EXIT + +cargo metadata --locked --format-version 1 > "${META}" + +get_version() { + local package="${1}" + jq -r '.packages[] | select(.name == "'"${package}"'") | .version' "${META}" +} + +get_msrv() { + local package="${1}" + jq -r '.packages[] | select(.name == "'"${package}"'") | .rust_version' "${META}" +} + +# check that the cxx packages all have the same version +check_cxx_versions() { + local cxx_version=$(get_version "cxx") + local cxx_build_version=$(get_version "cxx-build") + local cxxbridge_cmd_version=$(get_version "cxx-build") + local cxxbridge_flags_version=$(get_version "cxxbridge-flags") + local cxxbridge_macro_version=$(get_version "cxxbridge-macro") + + ok=true + echo "Found cxx version ${cxx_version}" + if [ "${cxx_version}" != "${cxx_build_version}" ]; then + echo "Found differing cxx-build version ${cxx_build_version}" + ok = false + fi + if [ "${cxx_version}" != "${cxxbridge_cmd_version}" ]; then + echo "Found differing cxxbridge-cmd version ${cxxbridge_cmd_version}" + ok = false + fi + if [ "${cxx_version}" != "${cxxbridge_flags_version}" ]; then + echo "Found differing cxxbridge-flags version ${cxxbridge_flags_version}" + ok = false + fi + if [ "${cxx_version}" != "${cxxbridge_macro_version}" ]; then + echo "Found differing cxxbridge-macro version ${cxxbridge_macro_version}" + ok = false + fi + + if ! $ok; then + echo "All cxx packages must be at the same version. Fix this in src/taskchampion-cpp/Cargo.toml." + exit 1 + else + echo "✓ All cxx packages are at the same version." + fi +} + +check_msrv() { + local taskchampion_msrv=$(get_msrv taskchampion) + local taskchampion_lib_msrv=$(get_msrv taskchampion-lib) + + echo "Found taskchampion MSRV ${taskchampion_msrv}" + echo "Found taskchampion-lib MSRV ${taskchampion_lib_msrv}" + + if [ "${taskchampion_msrv}" != "${taskchampion_lib_msrv}" ]; then + echo "Those MSRVs should be the same (or taskchampion-lib should be greater, in which case adjust this script)" + exit 1 + else + echo "✓ MSRVs are at the same version." + fi +} + +check_cxx_versions +check_msrv diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f7f0b05e2..80f12ac8d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -118,6 +118,7 @@ jobs: # 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.73.0" # MSRV override: true diff --git a/Cargo.lock b/Cargo.lock index 68852d3ce..0c450807b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + [[package]] name = "anyhow" version = "1.0.89" @@ -206,6 +212,32 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "clap" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -268,9 +300,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.128" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ccead7d199d584d139148b04b4a368d1ec7556a1d9ea2548febb1b9d49f9a4" +checksum = "273dcfd3acd4e1e276af13ed2a43eea7001318823e7a726a6b3ed39b4acc0b82" dependencies = [ "cc", "cxxbridge-flags", @@ -280,9 +312,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.128" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1" +checksum = "d8b2766fbd92be34e9ed143898fce6c572dc009de39506ed6903e5a05b68914e" dependencies = [ "cc", "codespan-reporting", @@ -294,16 +326,29 @@ dependencies = [ ] [[package]] -name = "cxxbridge-flags" -version = "1.0.128" +name = "cxxbridge-cmd" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65777e06cc48f0cb0152024c77d6cf9e4bdb4408e7b48bea993d42fa0f5b02b6" +checksum = "de30fc7f8b99c54cfd811c581e5af6423a4c45d4774fb5f2534aefa2f345f634" +dependencies = [ + "clap", + "codespan-reporting", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "839fcd5e43464614ffaa989eaf1c139ef1f0c51672a1ed08023307fa1b909ccd" [[package]] name = "cxxbridge-macro" -version = "1.0.128" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" +checksum = "4b2c1c1776b986979be68bb2285da855f8d8a35851a769fca8740df7c3d07877" dependencies = [ "proc-macro2", "quote", @@ -1400,6 +1445,12 @@ dependencies = [ "der", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.25.0" @@ -1494,6 +1545,7 @@ version = "0.1.0" dependencies = [ "cxx", "cxx-build", + "cxxbridge-cmd", "taskchampion", ] diff --git a/src/taskchampion-cpp/Cargo.toml b/src/taskchampion-cpp/Cargo.toml index d84a00521..925210c0f 100644 --- a/src/taskchampion-cpp/Cargo.toml +++ b/src/taskchampion-cpp/Cargo.toml @@ -3,17 +3,28 @@ name = "taskchampion-lib" version = "0.1.0" edition = "2021" publish = false +rust-version = "1.73.0" # MSRV [lib] crate-type = ["staticlib"] [dependencies] taskchampion = "0.9.0" -cxx = "1.0.124" +# All three cxx* dependencies must have precisely the same version. +cxx = "=1.0.124" [features] # use native CA roots, instead of bundled tls-native-roots = ["taskchampion/tls-native-roots"] [build-dependencies] -cxx-build = "1.0" +# All three cxx* dependencies must have precisely the same version. +cxx-build = "=1.0.124" + +# Include cxxbridge-cmd in Cargo.lock along with the others. This gives a +# warning "ignoring invalid dependency `cxxbridge-cmd` which is missing a lib +# target" but this can be safely ignored. +# See https://github.com/dtolnay/cxx/issues/1407#issuecomment-2509136343 +[target.'cfg(any())'.dependencies] +# All three cxx* dependencies must have precisely the same version. +cxxbridge-cmd = "=1.0.124" From dfc36aefcfc5896bc67d309d1289265eee992303 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 1 Dec 2024 14:12:05 -0800 Subject: [PATCH 156/242] Rely on cxx to enforce matching versions (#3713) --- .github/workflows/metadata-check.sh | 41 ----------------------------- Cargo.lock | 31 +++++++++++++--------- src/taskchampion-cpp/Cargo.toml | 14 ++-------- 3 files changed, 21 insertions(+), 65 deletions(-) diff --git a/.github/workflows/metadata-check.sh b/.github/workflows/metadata-check.sh index 39f608eb7..e22d43453 100755 --- a/.github/workflows/metadata-check.sh +++ b/.github/workflows/metadata-check.sh @@ -9,51 +9,11 @@ trap 'rm -rf -- "${META}"' EXIT cargo metadata --locked --format-version 1 > "${META}" -get_version() { - local package="${1}" - jq -r '.packages[] | select(.name == "'"${package}"'") | .version' "${META}" -} - get_msrv() { local package="${1}" jq -r '.packages[] | select(.name == "'"${package}"'") | .rust_version' "${META}" } -# check that the cxx packages all have the same version -check_cxx_versions() { - local cxx_version=$(get_version "cxx") - local cxx_build_version=$(get_version "cxx-build") - local cxxbridge_cmd_version=$(get_version "cxx-build") - local cxxbridge_flags_version=$(get_version "cxxbridge-flags") - local cxxbridge_macro_version=$(get_version "cxxbridge-macro") - - ok=true - echo "Found cxx version ${cxx_version}" - if [ "${cxx_version}" != "${cxx_build_version}" ]; then - echo "Found differing cxx-build version ${cxx_build_version}" - ok = false - fi - if [ "${cxx_version}" != "${cxxbridge_cmd_version}" ]; then - echo "Found differing cxxbridge-cmd version ${cxxbridge_cmd_version}" - ok = false - fi - if [ "${cxx_version}" != "${cxxbridge_flags_version}" ]; then - echo "Found differing cxxbridge-flags version ${cxxbridge_flags_version}" - ok = false - fi - if [ "${cxx_version}" != "${cxxbridge_macro_version}" ]; then - echo "Found differing cxxbridge-macro version ${cxxbridge_macro_version}" - ok = false - fi - - if ! $ok; then - echo "All cxx packages must be at the same version. Fix this in src/taskchampion-cpp/Cargo.toml." - exit 1 - else - echo "✓ All cxx packages are at the same version." - fi -} - check_msrv() { local taskchampion_msrv=$(get_msrv taskchampion) local taskchampion_lib_msrv=$(get_msrv taskchampion-lib) @@ -69,5 +29,4 @@ check_msrv() { fi } -check_cxx_versions check_msrv diff --git a/Cargo.lock b/Cargo.lock index 0c450807b..3669b5e88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,25 +300,26 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.124" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273dcfd3acd4e1e276af13ed2a43eea7001318823e7a726a6b3ed39b4acc0b82" +checksum = "05e1ec88093d2abd9cf1b09ffd979136b8e922bf31cad966a8fe0d73233112ef" dependencies = [ "cc", + "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", + "foldhash", "link-cplusplus", ] [[package]] name = "cxx-build" -version = "1.0.124" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b2766fbd92be34e9ed143898fce6c572dc009de39506ed6903e5a05b68914e" +checksum = "9afa390d956ee7ccb41aeed7ed7856ab3ffb4fc587e7216be7e0f83e949b4e6c" dependencies = [ "cc", "codespan-reporting", - "once_cell", "proc-macro2", "quote", "scratch", @@ -327,9 +328,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.124" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de30fc7f8b99c54cfd811c581e5af6423a4c45d4774fb5f2534aefa2f345f634" +checksum = "3c23bfff654d6227cbc83de8e059d2f8678ede5fc3a6c5a35d5c379983cc61e6" dependencies = [ "clap", "codespan-reporting", @@ -340,18 +341,19 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.124" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839fcd5e43464614ffaa989eaf1c139ef1f0c51672a1ed08023307fa1b909ccd" +checksum = "f7c01b36e22051bc6928a78583f1621abaaf7621561c2ada1b00f7878fbe2caa" [[package]] name = "cxxbridge-macro" -version = "1.0.124" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2c1c1776b986979be68bb2285da855f8d8a35851a769fca8740df7c3d07877" +checksum = "f6e14013136fac689345d17b9a6df55977251f11d333c0a571e8d963b55e1f95" dependencies = [ "proc-macro2", "quote", + "rustversion", "syn", ] @@ -429,6 +431,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1545,7 +1553,6 @@ version = "0.1.0" dependencies = [ "cxx", "cxx-build", - "cxxbridge-cmd", "taskchampion", ] diff --git a/src/taskchampion-cpp/Cargo.toml b/src/taskchampion-cpp/Cargo.toml index 925210c0f..8853e0b18 100644 --- a/src/taskchampion-cpp/Cargo.toml +++ b/src/taskchampion-cpp/Cargo.toml @@ -10,21 +10,11 @@ crate-type = ["staticlib"] [dependencies] taskchampion = "0.9.0" -# All three cxx* dependencies must have precisely the same version. -cxx = "=1.0.124" +cxx = "1.0.133" [features] # use native CA roots, instead of bundled tls-native-roots = ["taskchampion/tls-native-roots"] [build-dependencies] -# All three cxx* dependencies must have precisely the same version. -cxx-build = "=1.0.124" - -# Include cxxbridge-cmd in Cargo.lock along with the others. This gives a -# warning "ignoring invalid dependency `cxxbridge-cmd` which is missing a lib -# target" but this can be safely ignored. -# See https://github.com/dtolnay/cxx/issues/1407#issuecomment-2509136343 -[target.'cfg(any())'.dependencies] -# All three cxx* dependencies must have precisely the same version. -cxxbridge-cmd = "=1.0.124" +cxx-build = "1.0.133" From 8de7ff52e77e6add0ff322eeeb7fc21bb79688ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 08:13:54 -0500 Subject: [PATCH 157/242] Bump docker/build-push-action from 6.9.0 to 6.10.0 (#3715) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.9.0 to 6.10.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.9.0...v6.10.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index e9f662120..9bbdc6dba 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -44,7 +44,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.10.0 with: context: . file: "./docker/task.dockerfile" From ce70a182c1d69c1c6c28109a7bf416d5e62e8714 Mon Sep 17 00:00:00 2001 From: Kursat Aktas Date: Sat, 7 Dec 2024 19:18:52 +0300 Subject: [PATCH 158/242] Introducing Taskwarrior Guru on Gurubase.io (#3689) * Introducing Taskwarrior Guru on Gurubase.io Signed-off-by: Kursat Aktas --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 548b89af0..f2bc94720 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![Release](https://img.shields.io/github/v/release/GothenburgBitFactory/taskwarrior)](https://github.com/GothenburgBitFactory/taskwarrior/releases/latest) [![Release date](https://img.shields.io/github/release-date/GothenburgBitFactory/taskwarrior)](https://github.com/GothenburgBitFactory/taskwarrior/releases/latest) [![GitHub Sponsors](https://img.shields.io/github/sponsors/GothenburgBitFactory?color=green)](https://github.com/sponsors/GothenburgBitFactory/) +[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20Taskwarrior%20Guru-006BFF)](https://gurubase.io/g/taskwarrior)
    From 3ea726f2bb00f9b0f8801ce382df50c5ec1a243f Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sat, 7 Dec 2024 21:56:34 -0500 Subject: [PATCH 159/242] Cargo update hashbrown (#3716) --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3669b5e88..54fbcdfb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -640,9 +640,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hashlink" @@ -792,7 +792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.2", ] [[package]] From 4add8395488e6b54aa11461dd2a05afcc2452a89 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 9 Dec 2024 06:05:09 -0500 Subject: [PATCH 160/242] Add instructions for running against customized TaskChampion (#3719) This is often useful when doing work that includes changes in both TC and TW. --- doc/devel/contrib/development.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/devel/contrib/development.md b/doc/devel/contrib/development.md index 0c9ae90bb..cfc6bce28 100644 --- a/doc/devel/contrib/development.md +++ b/doc/devel/contrib/development.md @@ -94,3 +94,13 @@ They can be found in the [ctest](https://cmake.org/cmake/help/latest/manual/ctes Note that any development should be performed using a git clone, and the current development branch. The source tarballs do not reflect HEAD, and do not contain the test suite. Follow the [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) for creating a pull request. + +## Using a Custom Version of TaskChampion + +To build against a different version of Taskchampion, modify the requirement in `src/taskchampion-cpp/Cargo.toml`. + +To build from a local checkout, replace the version with a [path dependency](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-path-dependencies), giving the path to the directory containing TaskChampion's `Cargo.toml`: + +```toml +taskchampion = { path = "path/to/taskchampion" } +``` From ddae5c4ba911a8585fe8b2640a3d58d348078ff0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:45:25 +0000 Subject: [PATCH 161/242] Bump taskchampion from 0.9.0 to 1.0.0 (#3722) * Bump taskchampion from 0.9.0 to 1.0.0 Bumps [taskchampion](https://github.com/GothenburgBitFactory/taskchampion) from 0.9.0 to 1.0.0. - [Release notes](https://github.com/GothenburgBitFactory/taskchampion/releases) - [Commits](https://github.com/GothenburgBitFactory/taskchampion/compare/v0.9.0...v1.0.0) --- updated-dependencies: - dependency-name: taskchampion dependency-type: direct:production update-type: version-update:semver-major ... * Bump MSRV * update url to address RUSTSEC-2024-0421 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dustin J. Mitchell --- .github/workflows/checks.yml | 2 +- .github/workflows/tests.yaml | 2 +- Cargo.lock | 1140 ++++++++++++++++++++++++++++--- INSTALL | 2 +- src/taskchampion-cpp/Cargo.toml | 4 +- 5 files changed, 1069 insertions(+), 81 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index e1037c5ce..c5a065868 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -32,7 +32,7 @@ jobs: # 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.73.0" # MSRV + toolchain: "1.78.0" # MSRV override: true - uses: actions-rs/cargo@v1.0.3 diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 80f12ac8d..e17926ee9 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -119,7 +119,7 @@ jobs: # 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.73.0" # MSRV + toolchain: "1.78.0" # MSRV override: true - uses: actions-rs/cargo@v1.0.3 diff --git a/Cargo.lock b/Cargo.lock index 54fbcdfb7..2943a2c54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,389 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "aws-config" +version = "1.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b49afaa341e8dd8577e1a2200468f98956d6eda50bcf4a53246cc00174ba924" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json 0.60.7", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 0.2.12", + "ring", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5ac934720fbb46206292d2c75b57e67acfc56fe7dfd34fb9a02334af08409ea" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-s3" +version = "1.65.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3ba2c5c0f2618937ce3d4a5ad574b86775576fa24006bcb3128c6e2cbf3c34e" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-json 0.61.1", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "fastrand", + "hex", + "hmac", + "http 0.2.12", + "http-body 0.4.6", + "lru", + "once_cell", + "percent-encoding", + "regex-lite", + "sha2", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ca43a4ef210894f93096039ef1d6fa4ad3edfabb3be92b80908b9f2e4b4eab" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json 0.61.1", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abaf490c2e48eed0bb8e2da2fb08405647bd7f253996e0f93b981958ea0f73b0" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json 0.61.1", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68fde0d69c8bfdc1060ea7da21df3e39f6014da316783336deff0a9ec28f4bf" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json 0.61.1", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3820e0c08d0737872ff3c7c1f21ebbb6693d832312d6152bf18ef50a5471c2" +dependencies = [ + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "crypto-bigint 0.5.5", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.2.0", + "once_cell", + "p256", + "percent-encoding", + "ring", + "sha2", + "subtle", + "time", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.60.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1a71073fca26775c8b5189175ea8863afb1c9ea2cceb02a5de5ad9dfbaa795" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http 0.2.12", + "http-body 0.4.6", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef7d0a272725f87e51ba2bf89f8c21e4df61b9e49ae1ac367a6d69916ef7c90" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8bc3e8fdc6b8d07d976e301c02fe553f72a39b7a9fea820e023268467d7ab6" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-json" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4e69cc50921eb913c6b662f8d909131bb3e6ad6cb6090d3a39b66fc5c52095" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f20685047ca9d6f17b994a07f629c813f08b5bce65523e47124879e60103d45" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "http-body 1.0.1", + "httparse", + "hyper", + "hyper-rustls", + "once_cell", + "pin-project-lite", + "pin-utils", + "rustls 0.21.12", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.2.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fbd94a32b3a7d55d3806fe27d98d3ad393050439dd05eb53ece36ec5e3d3510" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.2.0", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0b0166827aa700d3dc519f72f8b3a91c35d0b8d042dc5d643a91e6f80648fc" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -125,6 +508,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base64" version = "0.21.7" @@ -137,6 +526,16 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "base64ct" version = "1.6.0" @@ -182,6 +581,16 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "cc" version = "1.1.30" @@ -279,6 +688,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -288,6 +706,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -357,6 +797,16 @@ dependencies = [ "syn", ] +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der" version = "0.7.9" @@ -386,6 +836,56 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest", + "ff", + "generic-array", + "group", + "pkcs8 0.9.0", + "rand_core", + "sec1", + "subtle", + "zeroize", ] [[package]] @@ -405,9 +905,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "fallible-iterator" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fallible-streaming-iterator" @@ -415,6 +915,22 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "flate2" version = "1.0.34" @@ -551,7 +1067,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror", + "thiserror 1.0.64", "time", "tokio", "tracing", @@ -565,7 +1081,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc279bfb50487d7bcd900e8688406475fc750fe474a835b2ab9ade9eb1fc90e2" dependencies = [ "reqwest", - "thiserror", + "thiserror 1.0.64", "tokio", ] @@ -586,14 +1102,14 @@ dependencies = [ "hex", "once_cell", "percent-encoding", - "pkcs8", + "pkcs8 0.10.2", "regex", "reqwest", "ring", "serde", "serde_json", "sha2", - "thiserror", + "thiserror 1.0.64", "time", "tokio", "tracing", @@ -609,6 +1125,17 @@ dependencies = [ "async-trait", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "h2" version = "0.3.26" @@ -620,7 +1147,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap", "slab", "tokio", @@ -635,7 +1162,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", - "allocator-api2", ] [[package]] @@ -643,21 +1169,26 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hashlink" -version = "0.8.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ "hashbrown 0.14.5", ] [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -671,6 +1202,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.9" @@ -691,6 +1231,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -698,7 +1249,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.2.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -725,8 +1299,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -745,9 +1319,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http", + "http 0.2.12", "hyper", + "log", "rustls 0.21.12", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls", ] @@ -776,13 +1352,142 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.5.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -839,9 +1544,9 @@ checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libsqlite3-sys" -version = "0.26.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ "cc", "pkg-config", @@ -857,6 +1562,12 @@ dependencies = [ "cc", ] +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.12" @@ -873,6 +1584,25 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.2", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.4" @@ -971,6 +1701,23 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -1031,14 +1778,24 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.9", + "spki 0.7.3", ] [[package]] @@ -1055,9 +1812,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1071,6 +1828,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.5.7" @@ -1103,6 +1869,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -1121,8 +1893,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "hyper", "hyper-rustls", "ipnet", @@ -1153,6 +1925,17 @@ dependencies = [ "winreg", ] +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + [[package]] name = "ring" version = "0.17.8" @@ -1170,9 +1953,9 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.29.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ "bitflags 2.6.0", "fallible-iterator", @@ -1188,6 +1971,15 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustls" version = "0.21.12" @@ -1202,9 +1994,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.18" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "log", "once_cell", @@ -1215,6 +2007,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + [[package]] name = "rustls-native-certs" version = "0.7.3" @@ -1316,6 +2120,20 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der 0.6.1", + "generic-array", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -1339,6 +2157,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.210" @@ -1383,6 +2207,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -1400,6 +2235,25 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "simple_asn1" version = "0.6.2" @@ -1408,7 +2262,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 1.0.64", "time", ] @@ -1443,6 +2297,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.3" @@ -1450,9 +2314,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.9", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.11.1" @@ -1461,15 +2331,15 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.25.0" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck", "proc-macro2", @@ -1486,9 +2356,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -1501,6 +2371,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -1524,11 +2405,14 @@ dependencies = [ [[package]] name = "taskchampion" -version = "0.9.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5719b8204660c0f5efc5777bd82e012a45ec36ab7315965c73b32526db2a7eb4" +checksum = "25e1f314f98be15c04291e62ff356f1f4165977c7c5baafe90258761876b84f1" dependencies = [ "anyhow", + "aws-config", + "aws-credential-types", + "aws-sdk-s3", "byteorder", "chrono", "flate2", @@ -1540,7 +2424,7 @@ dependencies = [ "serde_json", "strum", "strum_macros", - "thiserror", + "thiserror 2.0.6", "tokio", "ureq", "url", @@ -1571,7 +2455,16 @@ version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.64", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] @@ -1585,6 +2478,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.36" @@ -1617,20 +2521,15 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.8.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.40.0" @@ -1643,6 +2542,7 @@ dependencies = [ "mio", "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -1740,27 +2640,12 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-width" version = "0.1.14" @@ -1775,16 +2660,16 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.10.1" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ "base64 0.22.1", "flate2", "log", "once_cell", - "rustls 0.23.18", - "rustls-native-certs", + "rustls 0.23.19", + "rustls-native-certs 0.7.3", "rustls-pki-types", "url", "webpki-roots 0.26.6", @@ -1792,9 +2677,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -1807,6 +2692,18 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.11.0" @@ -1829,6 +2726,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "want" version = "0.3.1" @@ -2125,6 +3028,48 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -2145,8 +3090,51 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/INSTALL b/INSTALL index 2b3c8da21..620fe76f1 100644 --- a/INSTALL +++ b/INSTALL @@ -22,7 +22,7 @@ You will need the following libraries: - libuuid (not needed for OSX) You will need a Rust toolchain of the Minimum Supported Rust Version (MSRV): - - rust 1.73.0 + - rust 1.78.0 Basic Installation ------------------ diff --git a/src/taskchampion-cpp/Cargo.toml b/src/taskchampion-cpp/Cargo.toml index 8853e0b18..b5e75a0d8 100644 --- a/src/taskchampion-cpp/Cargo.toml +++ b/src/taskchampion-cpp/Cargo.toml @@ -3,13 +3,13 @@ name = "taskchampion-lib" version = "0.1.0" edition = "2021" publish = false -rust-version = "1.73.0" # MSRV +rust-version = "1.78.0" # MSRV [lib] crate-type = ["staticlib"] [dependencies] -taskchampion = "0.9.0" +taskchampion = "1.0.0" cxx = "1.0.133" [features] From ff325bc19eb4d09f214f8ff5b6e1de957052214b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:59:12 -0500 Subject: [PATCH 162/242] [pre-commit.ci] pre-commit autoupdate (#3725) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v19.1.4 → v19.1.5](https://github.com/pre-commit/mirrors-clang-format/compare/v19.1.4...v19.1.5) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9ffb6e81a..5d0002cdf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v19.1.4 + rev: v19.1.5 hooks: - id: clang-format types_or: [c++, c] From 758ac8f850dd2f6200e875af1c4b7f7b39497d9b Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 16 Dec 2024 20:08:50 -0500 Subject: [PATCH 163/242] Add support for sync to AWS (#3723) This is closely modeled on support for sync to GCP (#3223), but with different authentication options to mirror typical usage of AWS. --- Cargo.lock | 424 +++++++++++++++++++++----------- doc/man/task-sync.5.in | 77 ++++++ src/Context.cpp | 6 + src/commands/CmdShow.cpp | 8 +- src/commands/CmdSync.cpp | 52 +++- src/taskchampion-cpp/Cargo.toml | 2 +- src/taskchampion-cpp/src/lib.rs | 87 +++++++ 7 files changed, 506 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2943a2c54..56f3ab7a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -417,8 +417,8 @@ dependencies = [ "http-body 0.4.6", "http-body 1.0.1", "httparse", - "hyper", - "hyper-rustls", + "hyper 0.14.30", + "hyper-rustls 0.24.2", "once_cell", "pin-project-lite", "pin-utils", @@ -505,7 +505,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -542,12 +542,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.6.0" @@ -606,6 +600,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -618,7 +618,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1054,9 +1054,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "google-cloud-auth" -version = "0.13.2" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf7cb7864f08a92e77c26bb230d021ea57691788fb5dd51793f96965d19e7f9" +checksum = "e57a13fbacc5e9c41ded3ad8d0373175a6b7a6ad430d99e89d314ac121b7ab06" dependencies = [ "async-trait", "base64 0.21.7", @@ -1076,9 +1076,9 @@ dependencies = [ [[package]] name = "google-cloud-metadata" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc279bfb50487d7bcd900e8688406475fc750fe474a835b2ab9ade9eb1fc90e2" +checksum = "04f945a208886a13d07636f38fb978da371d0abc3e34bad338124b9f8c135a8f" dependencies = [ "reqwest", "thiserror 1.0.64", @@ -1087,10 +1087,11 @@ dependencies = [ [[package]] name = "google-cloud-storage" -version = "0.15.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac04b29849ebdeb9fb008988cc1c4d1f0c9d121b4c7f1ddeb8061df124580e93" +checksum = "e81dff54dbfa83705c896179ecaa4f384bfbfac90f3b637f38541443275b8a3f" dependencies = [ + "anyhow", "async-stream", "async-trait", "base64 0.21.7", @@ -1105,6 +1106,7 @@ dependencies = [ "pkcs8 0.10.2", "regex", "reqwest", + "reqwest-middleware", "ring", "serde", "serde_json", @@ -1312,6 +1314,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" version = "0.24.2" @@ -1320,12 +1341,49 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper", + "hyper 0.14.30", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper 1.5.1", + "hyper-util", + "rustls 0.23.19", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.1", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.1", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", ] [[package]] @@ -1738,7 +1796,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1810,6 +1868,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.92" @@ -1819,6 +1886,58 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.19", + "socket2", + "thiserror 2.0.6", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +dependencies = [ + "bytes", + "getrandom", + "rand", + "ring", + "rustc-hash", + "rustls 0.23.19", + "rustls-pki-types", + "slab", + "thiserror 2.0.6", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.37" @@ -1828,6 +1947,27 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -1843,7 +1983,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.6.0", + "bitflags", ] [[package]] @@ -1883,20 +2023,21 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper", - "hyper-rustls", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.1", + "hyper-rustls 0.27.3", + "hyper-util", "ipnet", "js-sys", "log", @@ -1905,15 +2046,16 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", + "quinn", + "rustls 0.23.19", + "rustls-pemfile 2.2.0", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.1", "tokio-util", "tower-service", "url", @@ -1921,8 +2063,23 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.25.4", - "winreg", + "webpki-roots", + "windows-registry", +] + +[[package]] +name = "reqwest-middleware" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ccd3b55e711f91a9885a2fa6fbbb2e39db1776420b062efc058c6410f7e5e3" +dependencies = [ + "anyhow", + "async-trait", + "http 1.2.0", + "reqwest", + "serde", + "thiserror 1.0.64", + "tower-service", ] [[package]] @@ -1957,7 +2114,7 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags 2.6.0", + "bitflags", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1971,6 +2128,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + [[package]] name = "rustc_version" version = "0.4.1" @@ -2055,6 +2218,9 @@ name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -2140,7 +2306,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -2367,9 +2533,12 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] [[package]] name = "synstructure" @@ -2382,32 +2551,11 @@ dependencies = [ "syn", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "taskchampion" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e1f314f98be15c04291e62ff356f1f4165977c7c5baafe90258761876b84f1" +checksum = "c4e41712ec2dd9cb5e7f4f20daf13a8e0937a927fb886153f2523a6686c35983" dependencies = [ "anyhow", "aws-config", @@ -2530,6 +2678,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.40.0" @@ -2569,6 +2732,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls 0.23.19", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -2672,7 +2845,7 @@ dependencies = [ "rustls-native-certs 0.7.3", "rustls-pki-types", "url", - "webpki-roots 0.26.6", + "webpki-roots", ] [[package]] @@ -2838,10 +3011,14 @@ dependencies = [ ] [[package]] -name = "webpki-roots" -version = "0.25.4" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "webpki-roots" @@ -2867,16 +3044,37 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-registry" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-targets 0.48.5", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", ] [[package]] @@ -2885,7 +3083,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2894,22 +3092,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -2918,46 +3101,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2970,64 +3135,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "write16" version = "1.0.0" @@ -3076,6 +3207,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] diff --git a/doc/man/task-sync.5.in b/doc/man/task-sync.5.in index 18f8a8189..a2dacf23b 100644 --- a/doc/man/task-sync.5.in +++ b/doc/man/task-sync.5.in @@ -139,6 +139,83 @@ Then configure Taskwarrior with: $ task config sync.gcp.credential_path .fi +.SS Amazon Web Services + +To synchronize your tasks to AWS, select a region near you and use the AWS +console to create a new S3 bucket. The default settings for the bucket are +adequate. + +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 +Taskwarrior clients. + +.nf + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "TaskChampion", + "Effect": "Allow", + "Action": [ + "s3:PutObject", + "s3:GetObject", + "s3:ListBucket", + "s3:DeleteObject" + ], + "Resource": [ + "arn:aws:s3:::BUCKETNAME", + "arn:aws:s3:::BUCKETNAME/*" + ] + } + ] + } +.fi + +To create such a user, create a new policy in the IAM console, select the JSON +option in the policy editor, and paste the policy. Click "Next" and give the +policy a name such as "TaskwarriorSync". Next, create a new user, with a name +of your choosing, select "Attach Policies Directly", and then choose the +newly-created policy. + +You will need access keys configured for the new user. Find the user in the +user list, open the "Security Credentials" tab, then click "Create access key" +and follow the steps. + +At this point, you can choose how to provide those credentials to Taskwarrior. +The simplest is to include them in the Taskwarrior configuration: + +.nf + $ task config sync.aws.region + $ task config sync.aws.bucket + $ task config sync.aws.access_key_id + $ task config sync.aws.secret_access_key +.fi + +Alternatively, you can set up an AWS CLI profile, using a profile name of your +choosing such as "taskwarrior-creds": + +.nf + $ aws configure --profile taskwarrior-creds +.fi + +Enter the access key ID and secret access key. The default region and format +are not important. Then configure Taskwarrior with: + +.nf + $ task config sync.aws.region + $ task config sync.aws.bucket + $ task config sync.aws.profile taskwarrior-creds +.fi + +To use AWS's default credential sources, such as environment variables, the +default profile, or an instance profile, set + +.nf + $ task config sync.aws.region + $ task config sync.aws.bucket + $ task config sync.aws.default_credentials true +.fi + .SS Local Synchronization In order to take advantage of synchronization's side effect of saving disk diff --git a/src/Context.cpp b/src/Context.cpp index c858f4373..532a2699b 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -318,6 +318,12 @@ std::string configurationDefaults = "#sync.server.client_id # Client ID for sync to a server\n" "#sync.server.url # URL of the sync server\n" "#sync.local.server_dir # Directory for local sync\n" + "#sync.aws.region # region for AWS sync\n" + "#sync.aws.bucket # bucket for AWS sync\n" + "#sync.aws.access_key_id # access_key_id for AWS sync\n" + "#sync.aws.secret_access_key # secret_access_key for AWS sync\n" + "#sync.aws.profile # profile name for AWS sync\n" + "#sync.aws.default_credentials # use default credentials for AWS sync\n" "#sync.gcp.credential_path # Path to JSON file containing credentials to " "authenticate GCP Sync\n" "#sync.gcp.bucket # Bucket for sync to GCP\n" diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index df92df6a1..a5f76e1f2 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -194,9 +194,15 @@ int CmdShow::execute(std::string& output) { " search.case.sensitive" " sugar" " summary.all.projects" - " sync.local.server_dir" + " sync.aws.access_key_id" + " sync.aws.bucket" + " sync.aws.default_credentials" + " sync.aws.profile" + " sync.aws.region" + " sync.aws.secret_access_key" " sync.gcp.credential_path" " sync.gcp.bucket" + " sync.local.server_dir" " sync.server.client_id" " sync.encryption_secret" " sync.server.url" diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index 361df7f4d..e4f07e8f6 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -38,6 +38,7 @@ #include #include +#include #include //////////////////////////////////////////////////////////////////////////////// @@ -65,12 +66,11 @@ int CmdSync::execute(std::string& output) { bool avoid_snapshots = false; bool verbose = Context::getContext().verbose("sync"); - // If no server is set up, quit. std::string origin = Context::getContext().config.get("sync.server.origin"); std::string url = Context::getContext().config.get("sync.server.url"); std::string server_dir = Context::getContext().config.get("sync.local.server_dir"); std::string client_id = Context::getContext().config.get("sync.server.client_id"); - std::string gcp_credential_path = Context::getContext().config.get("sync.gcp.credential_path"); + std::string aws_bucket = Context::getContext().config.get("sync.aws.bucket"); std::string gcp_bucket = Context::getContext().config.get("sync.gcp.bucket"); std::string encryption_secret = Context::getContext().config.get("sync.encryption_secret"); @@ -85,7 +85,55 @@ int CmdSync::execute(std::string& output) { out << format("Syncing with {1}", server_dir) << '\n'; } replica->sync_to_local(server_dir, avoid_snapshots); + } else if (aws_bucket != "") { + std::string aws_region = Context::getContext().config.get("sync.aws.region"); + std::string aws_profile = Context::getContext().config.get("sync.aws.profile"); + std::string aws_access_key_id = Context::getContext().config.get("sync.aws.access_key_id"); + std::string aws_secret_access_key = + Context::getContext().config.get("sync.aws.secret_access_key"); + std::string aws_default_credentials = + Context::getContext().config.get("sync.aws.default_credentials"); + if (aws_region == "") { + throw std::string("sync.aws.region is required"); + } + if (encryption_secret == "") { + throw std::string("sync.encryption_secret is required"); + } + + bool using_profile = false; + bool using_creds = false; + bool using_default = false; + if (aws_profile != "") { + using_profile = true; + } + if (aws_access_key_id != "" || aws_secret_access_key != "") { + using_creds = true; + } + if (aws_default_credentials != "") { + using_default = true; + } + + if (using_profile + using_creds + using_default != 1) { + throw std::string("exactly one method of specifying AWS credentials is required"); + } + + if (verbose) { + out << format("Syncing with AWS bucket {1}", aws_bucket) << '\n'; + } + + if (using_profile) { + replica->sync_to_aws_with_profile(aws_region, aws_bucket, aws_profile, encryption_secret, + avoid_snapshots); + } else if (using_creds) { + replica->sync_to_aws_with_access_key(aws_region, aws_bucket, aws_access_key_id, + aws_secret_access_key, encryption_secret, + avoid_snapshots); + } else { + replica->sync_to_aws_with_default_creds(aws_region, aws_bucket, encryption_secret, + avoid_snapshots); + } } else if (gcp_bucket != "") { + std::string gcp_credential_path = Context::getContext().config.get("sync.gcp.credential_path"); if (encryption_secret == "") { throw std::string("sync.encryption_secret is required"); } diff --git a/src/taskchampion-cpp/Cargo.toml b/src/taskchampion-cpp/Cargo.toml index b5e75a0d8..b806f986f 100644 --- a/src/taskchampion-cpp/Cargo.toml +++ b/src/taskchampion-cpp/Cargo.toml @@ -9,7 +9,7 @@ rust-version = "1.78.0" # MSRV crate-type = ["staticlib"] [dependencies] -taskchampion = "1.0.0" +taskchampion = "=1.0.2" cxx = "1.0.133" [features] diff --git a/src/taskchampion-cpp/src/lib.rs b/src/taskchampion-cpp/src/lib.rs index 829394f63..6fb8ace0a 100644 --- a/src/taskchampion-cpp/src/lib.rs +++ b/src/taskchampion-cpp/src/lib.rs @@ -164,6 +164,36 @@ mod ffi { avoid_snapshots: bool, ) -> Result<()>; + /// Sync with a server created from `ServerConfig::Aws` using `AwsCredentials::Profile`. + fn sync_to_aws_with_profile( + &mut self, + region: String, + bucket: String, + profile_name: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<()>; + + /// Sync with a server created from `ServerConfig::Aws` using `AwsCredentials::AccessKey`. + fn sync_to_aws_with_access_key( + &mut self, + region: String, + bucket: String, + access_key_id: String, + secret_access_key: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<()>; + + /// Sync with a server created from `ServerConfig::Aws` using `AwsCredentials::Default`. + fn sync_to_aws_with_default_creds( + &mut self, + region: String, + bucket: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<()>; + /// Sync with a server created from `ServerConfig::Gcp`. /// /// An empty value for `credential_path` is converted to `Option::None`. @@ -580,6 +610,63 @@ impl Replica { Ok(self.0.sync(&mut server, avoid_snapshots)?) } + fn sync_to_aws_with_profile( + &mut self, + region: String, + bucket: String, + profile_name: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<(), CppError> { + let mut server = tc::server::ServerConfig::Aws { + region, + bucket, + credentials: tc::server::AwsCredentials::Profile { profile_name }, + encryption_secret: encryption_secret.as_bytes().to_vec(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } + + fn sync_to_aws_with_access_key( + &mut self, + region: String, + bucket: String, + access_key_id: String, + secret_access_key: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<(), CppError> { + let mut server = tc::server::ServerConfig::Aws { + region, + bucket, + credentials: tc::server::AwsCredentials::AccessKey { + access_key_id, + secret_access_key, + }, + encryption_secret: encryption_secret.as_bytes().to_vec(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } + + fn sync_to_aws_with_default_creds( + &mut self, + region: String, + bucket: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<(), CppError> { + let mut server = tc::server::ServerConfig::Aws { + region, + bucket, + credentials: tc::server::AwsCredentials::Default, + encryption_secret: encryption_secret.as_bytes().to_vec(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } + fn sync_to_gcp( &mut self, bucket: String, From cc505e488184e958bcaedad6fed86f91d128e6bd Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 16 Dec 2024 20:24:45 -0500 Subject: [PATCH 164/242] Support importing Taskwarrior v2.x data files (#3724) This should ease the pain of upgrading from v2.x to v3.x. --- doc/man/task.1.in | 5 + src/CMakeLists.txt | 1 + src/TF2.cpp | 184 +++++++++++++++++++++++++++++++++++ src/TF2.h | 66 +++++++++++++ src/commands/CMakeLists.txt | 1 + src/commands/CmdCustom.cpp | 3 +- src/commands/CmdImportV2.cpp | 135 +++++++++++++++++++++++++ src/commands/CmdImportV2.h | 46 +++++++++ src/commands/Command.cpp | 3 + test/CMakeLists.txt | 1 + test/import-v2.test.py | 81 +++++++++++++++ 11 files changed, 525 insertions(+), 1 deletion(-) create mode 100644 src/TF2.cpp create mode 100644 src/TF2.h create mode 100644 src/commands/CmdImportV2.cpp create mode 100644 src/commands/CmdImportV2.h create mode 100755 test/import-v2.test.py diff --git a/doc/man/task.1.in b/doc/man/task.1.in index e7e8a71b9..52bbcaab5 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -414,6 +414,11 @@ few example scripts, such as: import-yaml.pl .fi +.TP +.B task import-v2 +Imports tasks from the Taskwarrior v2.x format. This is used when upgrading from +version 2.x to version 3.x. + .TP .B task log Adds a new task that is already completed, to the task list. It is affected by diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9bb7f2e38..45b027c4a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ add_library (task STATIC CLI2.cpp CLI2.h Hooks.cpp Hooks.h Lexer.cpp Lexer.h Operation.cpp Operation.h + TF2.cpp TF2.h TDB2.cpp TDB2.h Task.cpp Task.h Variant.cpp Variant.h diff --git a/src/TF2.cpp b/src/TF2.cpp new file mode 100644 index 000000000..9b7e2f940 --- /dev/null +++ b/src/TF2.cpp @@ -0,0 +1,184 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define STRING_TDB2_REVERTED "Modified task reverted." + +//////////////////////////////////////////////////////////////////////////////// +TF2::TF2() : _loaded_tasks(false), _loaded_lines(false) {} + +//////////////////////////////////////////////////////////////////////////////// +TF2::~TF2() {} + +//////////////////////////////////////////////////////////////////////////////// +void TF2::target(const std::string& f) { _file = File(f); } + +//////////////////////////////////////////////////////////////////////////////// +const std::vector>& TF2::get_tasks() { + if (!_loaded_tasks) load_tasks(); + + return _tasks; +} + +//////////////////////////////////////////////////////////////////////////////// +// Attempt an FF4 parse. +// +// Note that FF1, FF2, FF3, and JSON are no longer supported. +// +// start --> [ --> Att --> ] --> end +// ^ | +// +-------+ +// +std::map TF2::load_task(const std::string& input) { + std::map data; + + // File format version 4, from 2009-5-16 - now, v1.7.1+ + // This is the parse format tried first, because it is most used. + data.clear(); + + if (input[0] == '[') { + // Not using Pig to parse here (which would be idiomatic), because we + // don't need to differentiate betwen utf-8 and normal characters. + // Pig's scanning the string can be expensive. + auto ending_bracket = input.find_last_of(']'); + if (ending_bracket != std::string::npos) { + std::string line = input.substr(1, ending_bracket); + + if (line.length() == 0) throw std::string("Empty record in input."); + + Pig attLine(line); + std::string name; + std::string value; + while (!attLine.eos()) { + if (attLine.getUntilAscii(':', name) && attLine.skip(':') && + attLine.getQuoted('"', value)) { +#ifdef PRODUCT_TASKWARRIOR + legacyAttributeMap(name); +#endif + + data[name] = decode(json::decode(value)); + } + + attLine.skip(' '); + } + + std::string remainder; + attLine.getRemainder(remainder); + if (remainder.length()) throw std::string("Unrecognized characters at end of line."); + } + } else { + throw std::string("Record not recognized as format 4."); + } + + // for compatibility, include all tags in `tags` as `tag_..` attributes + if (data.find("tags") != data.end()) { + for (auto& tag : split(data["tags"], ',')) { + data[Task::tag2Attr(tag)] = "x"; + } + } + + // same for `depends` / `dep_..` + if (data.find("depends") != data.end()) { + for (auto& dep : split(data["depends"], ',')) { + data[Task::dep2Attr(dep)] = "x"; + } + } + + return data; +} + +//////////////////////////////////////////////////////////////////////////////// +// Decode values after parse. +// [ <- &open; +// ] <- &close; +const std::string TF2::decode(const std::string& value) const { + if (value.find('&') == std::string::npos) return value; + + auto modified = str_replace(value, "&open;", "["); + return str_replace(modified, "&close;", "]"); +} + +//////////////////////////////////////////////////////////////////////////////// +void TF2::load_tasks() { + Timer timer; + + if (!_loaded_lines) { + load_lines(); + } + + // Reduce unnecessary allocations/copies. + // Calling it on _tasks is the right thing to do even when from_gc is set. + _tasks.reserve(_lines.size()); + + int line_number = 0; // Used for error message in catch block. + try { + for (auto& line : _lines) { + ++line_number; + auto task = load_task(line); + _tasks.push_back(task); + } + + _loaded_tasks = true; + } + + catch (const std::string& e) { + throw e + format(" in {1} at line {2}", _file._data, line_number); + } + + Context::getContext().time_load_us += timer.total_us(); +} + +//////////////////////////////////////////////////////////////////////////////// +void TF2::load_lines() { + if (_file.open()) { + if (Context::getContext().config.getBoolean("locking")) _file.lock(); + + _file.read(_lines); + _file.close(); + _loaded_lines = true; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// vim: ts=2 et sw=2 diff --git a/src/TF2.h b/src/TF2.h new file mode 100644 index 000000000..fbec1f12b --- /dev/null +++ b/src/TF2.h @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2006 - 2024, 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_TF2 +#define INCLUDED_TF2 + +#include +#include +#include + +#include +#include +#include +#include +#include + +// TF2 Class represents a single 2.x-style file in the task database. +// +// This is only used for importing tasks from 2.x. It only reads format 4, based +// on a stripped-down version of the TF2 class from v2.6.2. +class TF2 { + public: + TF2(); + ~TF2(); + + void target(const std::string&); + + const std::vector>& get_tasks(); + + std::map load_task(const std::string&); + void load_tasks(); + void load_lines(); + const std::string decode(const std::string& value) const; + + bool _loaded_tasks; + bool _loaded_lines; + std::vector> _tasks; + std::vector _lines; + File _file; +}; + +#endif +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CMakeLists.txt b/src/commands/CMakeLists.txt index e12f5cc0e..720b6cbc9 100644 --- a/src/commands/CMakeLists.txt +++ b/src/commands/CMakeLists.txt @@ -35,6 +35,7 @@ set (commands_SRCS Command.cpp Command.h CmdHistory.cpp CmdHistory.h CmdIDs.cpp CmdIDs.h CmdImport.cpp CmdImport.h + CmdImportV2.cpp CmdImportV2.h CmdInfo.cpp CmdInfo.h CmdLog.cpp CmdLog.h CmdLogo.cpp CmdLogo.h diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index 911ac719d..f2ee70c66 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -241,7 +241,8 @@ int CmdCustom::execute(std::string& output) { Color warning = Color(Context::getContext().config.get("color.warning")); std::cerr << warning.colorize(format("Found existing '*.data' files in {1}", location)) << "\n"; std::cerr << " Taskwarrior's storage format changed in 3.0, requiring a manual migration.\n"; - std::cerr << " See https://taskwarrior.org/docs/upgrade-3/\n"; + std::cerr << " See https://taskwarrior.org/docs/upgrade-3/. Run `task import-v2` to import\n"; + std::cerr << " the tasks into the Taskwarrior-3.x format\n"; } feedback_backlog(); diff --git a/src/commands/CmdImportV2.cpp b/src/commands/CmdImportV2.cpp new file mode 100644 index 000000000..e34e597a2 --- /dev/null +++ b/src/commands/CmdImportV2.cpp @@ -0,0 +1,135 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 +// +//////////////////////////////////////////////////////////////////////////////// + +#include +// cmake.h include header must come first + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +CmdImportV2::CmdImportV2() { + _keyword = "import-v2"; + _usage = "task import-v2"; + _description = "Imports Taskwarrior v2.x files"; + _read_only = false; + _displays_id = false; + _needs_gc = false; + _uses_context = false; + _accepts_filter = false; + _accepts_modifications = false; + _accepts_miscellaneous = true; + _category = Command::Category::migration; +} + +//////////////////////////////////////////////////////////////////////////////// +int CmdImportV2::execute(std::string&) { + std::vector> task_data; + + std::string location = (Context::getContext().data_dir); + File pending_file = File(location + "/pending.data"); + if (pending_file.exists()) { + TF2 pending_tf; + pending_tf.target(pending_file); + auto& pending_tasks = pending_tf.get_tasks(); + task_data.insert(task_data.end(), pending_tasks.begin(), pending_tasks.end()); + } + File completed_file = File(location + "/completed.data"); + if (completed_file.exists()) { + TF2 completed_tf; + completed_tf.target(completed_file); + auto& completed_tasks = completed_tf.get_tasks(); + task_data.insert(task_data.end(), completed_tasks.begin(), completed_tasks.end()); + } + + auto count = import(task_data); + + Context::getContext().footnote( + format("Imported {1} tasks from `*.data` files. You may now delete these files.", count)); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +int CmdImportV2::import(const std::vector>& task_data) { + auto count = 0; + const std::string uuid_key = "uuid"; + const std::string id_key = "id"; + const std::string descr_key = "description"; + auto& replica = Context::getContext().tdb2.replica(); + rust::Vec ops; + tc::add_undo_point(ops); + + for (auto& task : task_data) { + auto uuid_iter = task.find(uuid_key); + if (uuid_iter == task.end()) { + std::cout << " err - Task with no UUID\n"; + continue; + } + auto uuid_str = uuid_iter->second; + auto uuid = tc::uuid_from_string(uuid_str); + + bool added_task = false; + auto maybe_task_data = replica->get_task_data(uuid); + auto task_data = maybe_task_data.is_some() ? maybe_task_data.take() : [&]() { + added_task = true; + return tc::create_task(uuid, ops); + }(); + + for (auto& attr : task) { + if (attr.first == uuid_key || attr.first == id_key) { + continue; + } + task_data->update(attr.first, attr.second, ops); + } + count++; + + if (added_task) { + std::cout << " add "; + } else { + std::cout << " mod "; + } + std::cout << uuid_str << ' '; + if (auto descr_iter = task.find(descr_key); descr_iter != task.end()) { + std::cout << descr_iter->second; + } else { + std::cout << "(no description)"; + } + std::cout << "\n"; + } + + replica->commit_operations(std::move(ops)); + return count; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdImportV2.h b/src/commands/CmdImportV2.h new file mode 100644 index 000000000..049d66d0e --- /dev/null +++ b/src/commands/CmdImportV2.h @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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_CMDIMPORTV2 +#define INCLUDED_CMDIMPORTV2 + +#include +#include + +#include +#include + +class CmdImportV2 : public Command { + public: + CmdImportV2(); + int execute(std::string &); + + private: + int import(const std::vector> &task_data); +}; + +#endif +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/Command.cpp b/src/commands/Command.cpp index 0dc215b01..dc6b36d87 100644 --- a/src/commands/Command.cpp +++ b/src/commands/Command.cpp @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -188,6 +189,8 @@ void Command::factory(std::map& all) { all[c->keyword()] = c; c = new CmdImport(); all[c->keyword()] = c; + c = new CmdImportV2(); + all[c->keyword()] = c; c = new CmdInfo(); all[c->keyword()] = c; c = new CmdLog(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0de39af75..08051bab2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -136,6 +136,7 @@ set (pythonTests hyphenate.test.py ids.test.py import.test.py + import-v2.test.py info.test.py limit.test.py list.all.projects.test.py diff --git a/test/import-v2.test.py b/test/import-v2.test.py new file mode 100755 index 000000000..063feedae --- /dev/null +++ b/test/import-v2.test.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +############################################################################### +# +# 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 +# +############################################################################### + +import sys +import os +import unittest +import json + +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Task, TestCase +from basetest.utils import mkstemp + + +class TestImport(TestCase): + def setUp(self): + self.t = Task() + self.t.config("dateformat", "m/d/Y") + + # Multiple tasks. + self.pending = """\ +[description:"bing" due:"1734480000" entry:"1734397061" modified:"1734397061" status:"pending" uuid:"ad7f7585-bff3-4b57-a116-abfc9f71ee4a"] +[description:"baz" entry:"1734397063" modified:"1734397063" status:"pending" uuid:"591ccfee-dd8d-44e9-908a-40618257cf54"]\ +""" + self.completed = """\ +[description:"foo" end:"1734397073" entry:"1734397054" modified:"1734397074" status:"deleted" uuid:"6849568f-55d7-4152-8db0-00356e39f0bb"] +[description:"bar" end:"1734397065" entry:"1734397056" modified:"1734397065" status:"completed" uuid:"51921813-7abb-412d-8ada-7c1417d01209"]\ +""" + + def test_import_v2(self): + with open(os.path.join(self.t.datadir, "pending.data"), "w") as f: + f.write(self.pending) + with open(os.path.join(self.t.datadir, "completed.data"), "w") as f: + f.write(self.completed) + code, out, err = self.t("import-v2") + self.assertIn("Imported 4 tasks", err) + + code, out, err = self.t("list") + self.assertIn("bing", out) + self.assertIn("baz", out) + self.assertNotIn("foo", out) + self.assertNotIn("bar", out) + + code, out, err = self.t("completed") + self.assertNotIn("bing", out) + self.assertNotIn("baz", out) + self.assertNotIn("foo", out) # deleted, not in the completed report + self.assertIn("bar", out) + + +if __name__ == "__main__": + from simpletap import TAPTestRunner + + unittest.main(testRunner=TAPTestRunner()) + +# vim: ai sts=4 et sw=4 ft=python From dcbe916286792e6f5d2d3af3baab79918ebc5f71 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 17 Dec 2024 19:08:48 -0500 Subject: [PATCH 165/242] Make test hooks executable (#3728) --- test/test_hooks/on-add-accept | 0 test/test_hooks/on-add-misbehave1 | 0 test/test_hooks/on-add-misbehave2 | 0 test/test_hooks/on-add-misbehave3 | 0 test/test_hooks/on-add-misbehave4 | 0 test/test_hooks/on-add-misbehave5 | 0 test/test_hooks/on-add-misbehave6 | 0 test/test_hooks/on-add-modify | 0 test/test_hooks/on-add-reject | 0 test/test_hooks/on-add.dummy | 0 test/test_hooks/on-exit-bad | 0 test/test_hooks/on-exit-good | 0 test/test_hooks/on-exit-misbehave1 | 0 test/test_hooks/on-exit-misbehave2 | 0 test/test_hooks/on-exit.dummy | 0 test/test_hooks/on-launch-bad | 0 test/test_hooks/on-launch-good | 0 test/test_hooks/on-launch-good-env | 0 test/test_hooks/on-launch-misbehave1 | 0 test/test_hooks/on-launch-misbehave2 | 0 test/test_hooks/on-launch.dummy | 0 test/test_hooks/on-modify-accept | 0 test/test_hooks/on-modify-for-template-badexit.py | 0 test/test_hooks/on-modify-for-template.py | 0 test/test_hooks/on-modify-misbehave2 | 0 test/test_hooks/on-modify-misbehave3 | 0 test/test_hooks/on-modify-misbehave4 | 0 test/test_hooks/on-modify-misbehave5 | 0 test/test_hooks/on-modify-misbehave6 | 0 test/test_hooks/on-modify-reject | 0 test/test_hooks/on-modify-revert | 0 test/test_hooks/on-modify.dummy | 0 test/test_hooks/wrapper.sh | 0 33 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 test/test_hooks/on-add-accept mode change 100644 => 100755 test/test_hooks/on-add-misbehave1 mode change 100644 => 100755 test/test_hooks/on-add-misbehave2 mode change 100644 => 100755 test/test_hooks/on-add-misbehave3 mode change 100644 => 100755 test/test_hooks/on-add-misbehave4 mode change 100644 => 100755 test/test_hooks/on-add-misbehave5 mode change 100644 => 100755 test/test_hooks/on-add-misbehave6 mode change 100644 => 100755 test/test_hooks/on-add-modify mode change 100644 => 100755 test/test_hooks/on-add-reject mode change 100644 => 100755 test/test_hooks/on-add.dummy mode change 100644 => 100755 test/test_hooks/on-exit-bad mode change 100644 => 100755 test/test_hooks/on-exit-good mode change 100644 => 100755 test/test_hooks/on-exit-misbehave1 mode change 100644 => 100755 test/test_hooks/on-exit-misbehave2 mode change 100644 => 100755 test/test_hooks/on-exit.dummy mode change 100644 => 100755 test/test_hooks/on-launch-bad mode change 100644 => 100755 test/test_hooks/on-launch-good mode change 100644 => 100755 test/test_hooks/on-launch-good-env mode change 100644 => 100755 test/test_hooks/on-launch-misbehave1 mode change 100644 => 100755 test/test_hooks/on-launch-misbehave2 mode change 100644 => 100755 test/test_hooks/on-launch.dummy mode change 100644 => 100755 test/test_hooks/on-modify-accept mode change 100644 => 100755 test/test_hooks/on-modify-for-template-badexit.py mode change 100644 => 100755 test/test_hooks/on-modify-for-template.py mode change 100644 => 100755 test/test_hooks/on-modify-misbehave2 mode change 100644 => 100755 test/test_hooks/on-modify-misbehave3 mode change 100644 => 100755 test/test_hooks/on-modify-misbehave4 mode change 100644 => 100755 test/test_hooks/on-modify-misbehave5 mode change 100644 => 100755 test/test_hooks/on-modify-misbehave6 mode change 100644 => 100755 test/test_hooks/on-modify-reject mode change 100644 => 100755 test/test_hooks/on-modify-revert mode change 100644 => 100755 test/test_hooks/on-modify.dummy mode change 100644 => 100755 test/test_hooks/wrapper.sh diff --git a/test/test_hooks/on-add-accept b/test/test_hooks/on-add-accept old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-add-misbehave1 b/test/test_hooks/on-add-misbehave1 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-add-misbehave2 b/test/test_hooks/on-add-misbehave2 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-add-misbehave3 b/test/test_hooks/on-add-misbehave3 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-add-misbehave4 b/test/test_hooks/on-add-misbehave4 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-add-misbehave5 b/test/test_hooks/on-add-misbehave5 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-add-misbehave6 b/test/test_hooks/on-add-misbehave6 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-add-modify b/test/test_hooks/on-add-modify old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-add-reject b/test/test_hooks/on-add-reject old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-add.dummy b/test/test_hooks/on-add.dummy old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-exit-bad b/test/test_hooks/on-exit-bad old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-exit-good b/test/test_hooks/on-exit-good old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-exit-misbehave1 b/test/test_hooks/on-exit-misbehave1 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-exit-misbehave2 b/test/test_hooks/on-exit-misbehave2 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-exit.dummy b/test/test_hooks/on-exit.dummy old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-launch-bad b/test/test_hooks/on-launch-bad old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-launch-good b/test/test_hooks/on-launch-good old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-launch-good-env b/test/test_hooks/on-launch-good-env old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-launch-misbehave1 b/test/test_hooks/on-launch-misbehave1 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-launch-misbehave2 b/test/test_hooks/on-launch-misbehave2 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-launch.dummy b/test/test_hooks/on-launch.dummy old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify-accept b/test/test_hooks/on-modify-accept old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify-for-template-badexit.py b/test/test_hooks/on-modify-for-template-badexit.py old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify-for-template.py b/test/test_hooks/on-modify-for-template.py old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify-misbehave2 b/test/test_hooks/on-modify-misbehave2 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify-misbehave3 b/test/test_hooks/on-modify-misbehave3 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify-misbehave4 b/test/test_hooks/on-modify-misbehave4 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify-misbehave5 b/test/test_hooks/on-modify-misbehave5 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify-misbehave6 b/test/test_hooks/on-modify-misbehave6 old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify-reject b/test/test_hooks/on-modify-reject old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify-revert b/test/test_hooks/on-modify-revert old mode 100644 new mode 100755 diff --git a/test/test_hooks/on-modify.dummy b/test/test_hooks/on-modify.dummy old mode 100644 new mode 100755 diff --git a/test/test_hooks/wrapper.sh b/test/test_hooks/wrapper.sh old mode 100644 new mode 100755 From 1ee69ea2149c4e7af5b70e7ea1b90e346235b591 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 19 Dec 2024 10:57:43 -0500 Subject: [PATCH 166/242] Release 3.3.0 (#3729) * Release 3.3.0 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- CMakeLists.txt | 2 +- ChangeLog | 22 ++++++++++++++++++++-- src/commands/CmdNews.cpp | 15 +++++++++++++++ src/commands/CmdNews.h | 1 + 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f73d7a5ef..25ed4d9eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ enable_testing() set (CMAKE_EXPORT_COMPILE_COMMANDS ON) project (task - VERSION 3.2.0 + VERSION 3.3.0 DESCRIPTION "Taskwarrior - a command-line TODO list manager" HOMEPAGE_URL https://taskwarrior.org/) diff --git a/ChangeLog b/ChangeLog index a09114d5e..7e7540fc9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,25 @@ ------ current release --------------------------- +- Sync now supports AWS S3 as a backend. +- A new `task import-v2` command allows importing Taskwarrior-2.x + data files directly. + +3.3.0 - + +Thanks to the following people for contributions to this release: + + - Chongyun Lee + - David Tolnay + - Dustin J. Mitchell + - Felix Schurk + - geoffpaulsen + - Kalle Kietäväinen + - Kursat Aktas + - Scott Mcdermott + - Thomas Lauf + +------ old releases ------------------------------ + 3.2.0 - - Support for the journal in `task info` has been restored (#3671) and the @@ -23,8 +43,6 @@ Thanks to the following people for contributions to this release: - Thomas Lauf - Tobias Predel ------- old releases ------------------------------ - 3.1.0 - - Support for `task purge` has been restored, and new support added for automatically diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 8ef979882..3f667fb81 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -159,6 +159,7 @@ std::vector NewsItem::all() { version3_0_0(items); version3_1_0(items); version3_2_0(items); + version3_3_0(items); return items; } @@ -515,6 +516,20 @@ void NewsItem::version3_2_0(std::vector& items) { items.push_back(info); } +void NewsItem::version3_3_0(std::vector& items) { + Version version("3.3.0"); + NewsItem info{ + version, + /*title=*/"AWS S3 Sync", + /*bg_title=*/"", + /*background=*/"", + /*punchline=*/"Use an AWS S3 bucket to sync Taskwarrior", + /*update=*/ + "Taskwarrior now supports AWS as a backend for sync, in addition to existing support\n" + "for GCP and taskchampion-sync-server. See `man task-sync` for details.\n\n"}; + items.push_back(info); +} + //////////////////////////////////////////////////////////////////////////////// int CmdNews::execute(std::string& output) { auto words = Context::getContext().cli2.getWords(); diff --git a/src/commands/CmdNews.h b/src/commands/CmdNews.h index 78055356f..6b70c0132 100644 --- a/src/commands/CmdNews.h +++ b/src/commands/CmdNews.h @@ -52,6 +52,7 @@ class NewsItem { static void version3_0_0(std::vector&); static void version3_1_0(std::vector&); static void version3_2_0(std::vector&); + static void version3_3_0(std::vector&); private: NewsItem(Version, const std::string&, const std::string& = "", const std::string& = "", From 1b9353dcccf14d97a79db944e9be75d56be482fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kiet=C3=A4v=C3=A4inen?= <1026741+kietavainen@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:13:23 +0200 Subject: [PATCH 167/242] Fix suppressing news nag after reading the news (#3731) --- src/commands/CmdNews.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 3f667fb81..9f1c60df0 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -642,7 +642,7 @@ bool CmdNews::should_nag() { Version current_version = Version::Current(); if (news_version == current_version) { - return true; + return false; } // Check if there are actually any interesting news items to show. From 3bf020060215e8432d2d543961aca78ed742be24 Mon Sep 17 00:00:00 2001 From: Tejada-Omar <72728094+Tejada-Omar@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:34:51 -0700 Subject: [PATCH 168/242] Consider news read if `news.version` > current version (#3734) Avoids two installations of taskwarrior with differing versions from constantly nagging and rewriting `news.version` --- src/commands/CmdNews.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 9f1c60df0..27202ff64 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -593,7 +593,7 @@ int CmdNews::execute(std::string& output) { std::cout << outro.str(); // 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); // Revert back to default signal handling after displaying the outro @@ -641,7 +641,7 @@ bool CmdNews::should_nag() { Version current_version = Version::Current(); - if (news_version == current_version) { + if (news_version >= current_version) { return false; } From 9105985c7c0c8f24da8150c5c2d5e8d3e044ed9b Mon Sep 17 00:00:00 2001 From: jrmarino Date: Tue, 31 Dec 2024 11:28:48 -0600 Subject: [PATCH 169/242] Add offline build notes to INSTALL (#3705) (#3740) Document additional steps that have been successful for NixOS and Ravenports. --- INSTALL | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/INSTALL b/INSTALL index 620fe76f1..3d89be316 100644 --- a/INSTALL +++ b/INSTALL @@ -110,6 +110,43 @@ If Taskwarrior will not build on your system, first take a look at the Operating System notes below. If this doesn't help, then go to the Troubleshooting section, which includes instructions on how to contact us for help. +Offline Build Notes +------------------- + +It is common for packaging systems (e.g. NixOS, FreeBSD Ports Collection, pkgsrc, etc) +to disable networking during builds. This restriction requires all distribution files +to be prepositioned after checksum verification as a prerequisite for the build. The +following steps have been successful in allowing Taskwarrior to be built in this +environment: + +1. Extract all crates in a known location, e.g. ${WRKDIR}/cargo-crates +This includes crates needed for corrosion (search for Cargo.lock files) + +2. Create .cargo-checksum.json for each crate +For example: +printf '{"package":"%s","files":{}}' $(sha256 -q ${DISTDIR}/rayon-core-1.12.1.tar.gz) \ + > ${WRKDIR}/cargo-crates/rayon-core-1.12.1/.cargo-checksum.json + +3. Create a custom config.toml file +For example, ${WRKDIR}/.cargo/config.toml +[source.cargo] +directory = '${WRKDIR}/cargo-crates' +[source.crates-io] +replace-with = 'cargo' + +4. After running cmake, configure cargo +For example: +cd ${WRKSRC} && ${SETENV} ${MAKE_ENV} ${CARGO_ENV} \ + /usr/local/bin/cargo update \ + --manifest-path ${WRKDIR}/.cargo/config.toml \ + --verbose + +5. Set CARGO_HOME in environment +For example +CARGO_HOME=${WRKDIR}/.cargo + +The build and installation steps should be the same as a standard build +at this point. Operating System Notes ---------------------- From 630585d7b4b6c14f7f1783ec017c702319230dac Mon Sep 17 00:00:00 2001 From: Karl <51945878+grasegger@users.noreply.github.com> Date: Tue, 31 Dec 2024 19:51:35 +0100 Subject: [PATCH 170/242] Update git log in CMakeLists.txt to not include potential signatures (#3742) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25ed4d9eb..a6cbadc3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ endif (EXISTS ${CMAKE_SOURCE_DIR}/src/libshared/src AND EXISTS ${CMAKE_SOURCE_DI message ("-- Looking for SHA1 references") if (EXISTS ${CMAKE_SOURCE_DIR}/.git/index) set (HAVE_COMMIT true) - execute_process (COMMAND git log -1 --pretty=format:%h + execute_process (COMMAND git log -1 --pretty=format:%h --no-show-signature WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE COMMIT) configure_file ( ${CMAKE_SOURCE_DIR}/commit.h.in From ddeec3512aeb8cd09019a5f5d23f652cb461d33a Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 1 Jan 2025 13:29:17 -0500 Subject: [PATCH 171/242] Add more CMake options to INSTALL (#3743) --- INSTALL | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/INSTALL b/INSTALL index 3d89be316..d46e67d10 100644 --- a/INSTALL +++ b/INSTALL @@ -89,6 +89,11 @@ get absolute installation directories: CMAKE_INSTALL_PREFIX/TASK_MAN1DIR /usr/local/share/man/man1 CMAKE_INSTALL_PREFIX/TASK_MAN5DIR /usr/local/share/man/man5 +The following variables control aspects of the build process: + + SYSTEM_CORROSION - Use system provided corrosion instead of vendored version + ENABLE_TLS_NATIVE_ROOTS - Use the system's TLS root certificates + Uninstallation -------------- From ae3651fd3f41650ba96cb521a216c753c2c2561d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:48:12 -0500 Subject: [PATCH 172/242] [pre-commit.ci] pre-commit autoupdate (#3748) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v19.1.5 → v19.1.6](https://github.com/pre-commit/mirrors-clang-format/compare/v19.1.5...v19.1.6) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5d0002cdf..ea9333263 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v19.1.5 + rev: v19.1.6 hooks: - id: clang-format types_or: [c++, c] From 2e5177aa7c8833d334c9877ab57d66755474a22e Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 6 Jan 2025 13:29:19 -0500 Subject: [PATCH 173/242] Update to TaskChampion 2.0.2 (#3746) --- .github/workflows/checks.yml | 2 +- .github/workflows/tests.yaml | 2 +- Cargo.lock | 4 ++-- INSTALL | 2 +- src/taskchampion-cpp/Cargo.toml | 4 ++-- src/taskchampion-cpp/src/lib.rs | 1 + 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index c5a065868..78d5c74ee 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -32,7 +32,7 @@ jobs: # 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.78.0" # MSRV + toolchain: "1.81.0" # MSRV override: true - uses: actions-rs/cargo@v1.0.3 diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index e17926ee9..78c6b74dc 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -119,7 +119,7 @@ jobs: # 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.78.0" # MSRV + toolchain: "1.81.0" # MSRV override: true - uses: actions-rs/cargo@v1.0.3 diff --git a/Cargo.lock b/Cargo.lock index 56f3ab7a8..b2120f98a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2553,9 +2553,9 @@ dependencies = [ [[package]] name = "taskchampion" -version = "1.0.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e41712ec2dd9cb5e7f4f20daf13a8e0937a927fb886153f2523a6686c35983" +checksum = "830bb062bb2d89bdee0063d7c02d1e24ee0a1702c683f394eb0520fb88dc4a5c" dependencies = [ "anyhow", "aws-config", diff --git a/INSTALL b/INSTALL index d46e67d10..1635fbb9e 100644 --- a/INSTALL +++ b/INSTALL @@ -22,7 +22,7 @@ You will need the following libraries: - libuuid (not needed for OSX) You will need a Rust toolchain of the Minimum Supported Rust Version (MSRV): - - rust 1.78.0 + - rust 1.81.0 Basic Installation ------------------ diff --git a/src/taskchampion-cpp/Cargo.toml b/src/taskchampion-cpp/Cargo.toml index b806f986f..13ee8920d 100644 --- a/src/taskchampion-cpp/Cargo.toml +++ b/src/taskchampion-cpp/Cargo.toml @@ -3,13 +3,13 @@ name = "taskchampion-lib" version = "0.1.0" edition = "2021" publish = false -rust-version = "1.78.0" # MSRV +rust-version = "1.81.0" # MSRV [lib] crate-type = ["staticlib"] [dependencies] -taskchampion = "=1.0.2" +taskchampion = "=2.0.2" cxx = "1.0.133" [features] diff --git a/src/taskchampion-cpp/src/lib.rs b/src/taskchampion-cpp/src/lib.rs index 6fb8ace0a..66017fc59 100644 --- a/src/taskchampion-cpp/src/lib.rs +++ b/src/taskchampion-cpp/src/lib.rs @@ -494,6 +494,7 @@ fn new_replica_on_disk( let storage = tc::StorageConfig::OnDisk { taskdb_dir: PathBuf::from(taskdb_dir), create_if_missing, + access_mode: tc::storage::AccessMode::ReadWrite, } .into_storage()?; Ok(Box::new(tc::Replica::new(storage).into())) From ffcd1c0d79d593cd84fff9a8e9e5334308fba24f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 08:32:46 -0500 Subject: [PATCH 174/242] Bump docker/build-push-action from 6.10.0 to 6.11.0 (#3755) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.10.0 to 6.11.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.10.0...v6.11.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 9bbdc6dba..d774bd3ed 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -44,7 +44,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.10.0 + uses: docker/build-push-action@v6.11.0 with: context: . file: "./docker/task.dockerfile" From 1c9dddcae7d7b7009aad68c6e7d31e6e483a629e Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 13 Jan 2025 08:33:06 -0500 Subject: [PATCH 175/242] Remove unused, undefined method TDB2::dump (#3754) --- src/TDB2.cpp | 5 ----- src/TDB2.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/src/TDB2.cpp b/src/TDB2.cpp index aab85a8e9..2e37640ef 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -450,11 +450,6 @@ int TDB2::num_local_changes() { return (int)replica()->num_local_operations(); } //////////////////////////////////////////////////////////////////////////////// int TDB2::num_reverts_possible() { return (int)replica()->num_undo_points(); } -//////////////////////////////////////////////////////////////////////////////// -void TDB2::dump() { - // TODO -} - //////////////////////////////////////////////////////////////////////////////// // For any task that has depenencies, follow the chain of dependencies until the // end. Along the way, update the Task::is_blocked and Task::is_blocking data diff --git a/src/TDB2.h b/src/TDB2.h index 69ffd0f20..3dafe6748 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -72,8 +72,6 @@ class TDB2 { int num_local_changes(); int num_reverts_possible(); - void dump(); - rust::Box &replica(); private: From aeeec16984eaa3e7f01385f908441fcc8dfc07b8 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sat, 18 Jan 2025 02:20:41 -0500 Subject: [PATCH 176/242] Handle 'until' and 'recur' simiar to handling of 'gc' (#3753) This centralizes updates to recurrence and 'until' in Command, instead of doing so in each individual command implementation. This is preparatory to opening the TaskChampion replica in read-only mode. --- src/Context.cpp | 7 ++++++- src/commands/CmdAdd.cpp | 1 + src/commands/CmdAliases.cpp | 1 + src/commands/CmdAnnotate.cpp | 1 + src/commands/CmdAppend.cpp | 1 + src/commands/CmdAttributes.cpp | 1 + src/commands/CmdBurndown.cpp | 9 +++------ src/commands/CmdCalc.cpp | 1 + src/commands/CmdCalendar.cpp | 3 +-- src/commands/CmdColor.cpp | 1 + src/commands/CmdColumns.cpp | 2 ++ src/commands/CmdCommands.cpp | 14 +++++++++----- src/commands/CmdConfig.cpp | 2 ++ src/commands/CmdContext.cpp | 2 ++ src/commands/CmdCount.cpp | 3 +-- src/commands/CmdCustom.cpp | 5 +---- src/commands/CmdDelete.cpp | 1 + src/commands/CmdDenotate.cpp | 1 + src/commands/CmdDiagnostics.cpp | 1 + src/commands/CmdDone.cpp | 1 + src/commands/CmdDuplicate.cpp | 1 + src/commands/CmdEdit.cpp | 1 + src/commands/CmdExec.cpp | 1 + src/commands/CmdExport.cpp | 5 +---- src/commands/CmdGet.cpp | 1 + src/commands/CmdHelp.cpp | 1 + src/commands/CmdHistory.cpp | 1 + src/commands/CmdIDs.cpp | 8 ++++++-- src/commands/CmdImport.cpp | 1 + src/commands/CmdImportV2.cpp | 1 + src/commands/CmdInfo.cpp | 1 + src/commands/CmdLog.cpp | 1 + src/commands/CmdLogo.cpp | 1 + src/commands/CmdModify.cpp | 1 + src/commands/CmdNews.cpp | 1 + src/commands/CmdPrepend.cpp | 1 + src/commands/CmdProjects.cpp | 6 ++---- src/commands/CmdPurge.cpp | 1 + src/commands/CmdReports.cpp | 1 + src/commands/CmdShow.cpp | 1 + src/commands/CmdStart.cpp | 1 + src/commands/CmdStats.cpp | 1 + src/commands/CmdStop.cpp | 1 + src/commands/CmdSummary.cpp | 3 +-- src/commands/CmdSync.cpp | 1 + src/commands/CmdTags.cpp | 2 ++ src/commands/CmdTimesheet.cpp | 3 +-- src/commands/CmdUDAs.cpp | 2 ++ src/commands/CmdUndo.cpp | 1 + src/commands/CmdUnique.cpp | 1 + src/commands/CmdUrgency.cpp | 1 + src/commands/CmdVersion.cpp | 2 ++ src/commands/Command.cpp | 4 ++++ src/commands/Command.h | 2 ++ src/main.h | 3 --- test/commands.test.py | 4 ++-- 56 files changed, 87 insertions(+), 39 deletions(-) diff --git a/src/Context.cpp b/src/Context.cpp index 532a2699b..ed4c65770 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -852,7 +852,7 @@ int Context::dispatch(std::string& out) { Command* c = commands[command]; assert(c); - // The command know whether they need a GC. + // The command know whether they need a GC or recurrence update. if (c->needs_gc()) { tdb2.gc(); } @@ -869,6 +869,11 @@ int Context::dispatch(std::string& out) { if (config.getBoolean("debug") && config.getInteger("debug.parser") == 1) debug(cli2.dump("Parse Tree (before command-specifіc processing)")); + if (c->needs_recur_update()) { + handleUntil(); + handleRecurrence(); + } + return c->execute(out); } diff --git a/src/commands/CmdAdd.cpp b/src/commands/CmdAdd.cpp index 6661c3d2f..5778ab737 100644 --- a/src/commands/CmdAdd.cpp +++ b/src/commands/CmdAdd.cpp @@ -40,6 +40,7 @@ CmdAdd::CmdAdd() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = false; _accepts_modifications = true; diff --git a/src/commands/CmdAliases.cpp b/src/commands/CmdAliases.cpp index 3554a7ee6..758365a95 100644 --- a/src/commands/CmdAliases.cpp +++ b/src/commands/CmdAliases.cpp @@ -39,6 +39,7 @@ CmdCompletionAliases::CmdCompletionAliases() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdAnnotate.cpp b/src/commands/CmdAnnotate.cpp index 73bbb8882..f5a193236 100644 --- a/src/commands/CmdAnnotate.cpp +++ b/src/commands/CmdAnnotate.cpp @@ -44,6 +44,7 @@ CmdAnnotate::CmdAnnotate() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = true; _accepts_modifications = true; diff --git a/src/commands/CmdAppend.cpp b/src/commands/CmdAppend.cpp index d6b68fab2..ac9e35ae9 100644 --- a/src/commands/CmdAppend.cpp +++ b/src/commands/CmdAppend.cpp @@ -44,6 +44,7 @@ CmdAppend::CmdAppend() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = true; _accepts_modifications = true; diff --git a/src/commands/CmdAttributes.cpp b/src/commands/CmdAttributes.cpp index bc7778572..553b9476d 100644 --- a/src/commands/CmdAttributes.cpp +++ b/src/commands/CmdAttributes.cpp @@ -42,6 +42,7 @@ CmdZshAttributes::CmdZshAttributes() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdBurndown.cpp b/src/commands/CmdBurndown.cpp index eb2a5e2e9..b05af87f3 100644 --- a/src/commands/CmdBurndown.cpp +++ b/src/commands/CmdBurndown.cpp @@ -767,6 +767,7 @@ CmdBurndownMonthly::CmdBurndownMonthly() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; @@ -779,8 +780,6 @@ int CmdBurndownMonthly::execute(std::string& output) { int rc = 0; // Scan the pending tasks, applying any filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); @@ -801,6 +800,7 @@ CmdBurndownWeekly::CmdBurndownWeekly() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; @@ -813,8 +813,6 @@ int CmdBurndownWeekly::execute(std::string& output) { int rc = 0; // Scan the pending tasks, applying any filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); @@ -835,6 +833,7 @@ CmdBurndownDaily::CmdBurndownDaily() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; @@ -847,8 +846,6 @@ int CmdBurndownDaily::execute(std::string& output) { int rc = 0; // Scan the pending tasks, applying any filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); diff --git a/src/commands/CmdCalc.cpp b/src/commands/CmdCalc.cpp index 3ed55e408..562b6d300 100644 --- a/src/commands/CmdCalc.cpp +++ b/src/commands/CmdCalc.cpp @@ -37,6 +37,7 @@ CmdCalc::CmdCalc() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdCalendar.cpp b/src/commands/CmdCalendar.cpp index 88129a44f..bf365d262 100644 --- a/src/commands/CmdCalendar.cpp +++ b/src/commands/CmdCalendar.cpp @@ -49,6 +49,7 @@ CmdCalendar::CmdCalendar() { _read_only = true; _displays_id = true; _needs_gc = true; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; @@ -80,8 +81,6 @@ int CmdCalendar::execute(std::string& output) { monthsPerLine = preferredMonthsPerLine; // Load the pending tasks. - handleUntil(); - handleRecurrence(); auto tasks = Context::getContext().tdb2.pending_tasks(); Datetime today; diff --git a/src/commands/CmdColor.cpp b/src/commands/CmdColor.cpp index bb0d7f548..53e672df6 100644 --- a/src/commands/CmdColor.cpp +++ b/src/commands/CmdColor.cpp @@ -45,6 +45,7 @@ CmdColor::CmdColor() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdColumns.cpp b/src/commands/CmdColumns.cpp index 2b2e32bfb..bf5e00f24 100644 --- a/src/commands/CmdColumns.cpp +++ b/src/commands/CmdColumns.cpp @@ -45,6 +45,7 @@ CmdColumns::CmdColumns() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; @@ -119,6 +120,7 @@ CmdCompletionColumns::CmdCompletionColumns() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdCommands.cpp b/src/commands/CmdCommands.cpp index 4676f57ae..6fb44876d 100644 --- a/src/commands/CmdCommands.cpp +++ b/src/commands/CmdCommands.cpp @@ -45,6 +45,7 @@ CmdCommands::CmdCommands() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; @@ -61,6 +62,7 @@ int CmdCommands::execute(std::string& output) { view.add("R/W", false); view.add("ID", false); view.add("GC", false); + view.add("Recur", false); view.add("Context", false); view.add("Filter", false); view.add("Mods", false); @@ -85,15 +87,17 @@ int CmdCommands::execute(std::string& output) { if (command.second->needs_gc()) view.set(row, 4, "GC"); - if (command.second->uses_context()) view.set(row, 5, "Ctxt"); + if (command.second->needs_recur_update()) view.set(row, 5, "Recur"); - if (command.second->accepts_filter()) view.set(row, 6, "Filt"); + if (command.second->uses_context()) view.set(row, 6, "Ctxt"); - if (command.second->accepts_modifications()) view.set(row, 7, "Mods"); + if (command.second->accepts_filter()) view.set(row, 7, "Filt"); - if (command.second->accepts_miscellaneous()) view.set(row, 8, "Misc"); + if (command.second->accepts_modifications()) view.set(row, 8, "Mods"); - view.set(row, 9, command.second->description()); + if (command.second->accepts_miscellaneous()) view.set(row, 9, "Misc"); + + view.set(row, 10, command.second->description()); } output = optionalBlankLine() + view.render() + optionalBlankLine() + '\n'; diff --git a/src/commands/CmdConfig.cpp b/src/commands/CmdConfig.cpp index 543bf1432..99270f033 100644 --- a/src/commands/CmdConfig.cpp +++ b/src/commands/CmdConfig.cpp @@ -44,6 +44,7 @@ CmdConfig::CmdConfig() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; @@ -217,6 +218,7 @@ CmdCompletionConfig::CmdCompletionConfig() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index adcff94c7..77bc3152d 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -49,6 +49,7 @@ CmdContext::CmdContext() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; @@ -414,6 +415,7 @@ CmdCompletionContext::CmdCompletionContext() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdCount.cpp b/src/commands/CmdCount.cpp index 0da28bc1b..3c158c225 100644 --- a/src/commands/CmdCount.cpp +++ b/src/commands/CmdCount.cpp @@ -40,6 +40,7 @@ CmdCount::CmdCount() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; @@ -50,8 +51,6 @@ CmdCount::CmdCount() { //////////////////////////////////////////////////////////////////////////////// int CmdCount::execute(std::string& output) { // Apply filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index f2ee70c66..9babfe668 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -55,6 +55,7 @@ CmdCustom::CmdCustom(const std::string& keyword, const std::string& usage, _read_only = true; _displays_id = true; _needs_gc = true; + _needs_recur_update = true; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; @@ -100,10 +101,6 @@ int CmdCustom::execute(std::string& output) { // Add the report filter to any existing filter. if (reportFilter != "") Context::getContext().cli2.addFilter(reportFilter); - // Make sure reccurent tasks are generated. - handleUntil(); - handleRecurrence(); - // Apply filter. Filter filter; std::vector filtered; diff --git a/src/commands/CmdDelete.cpp b/src/commands/CmdDelete.cpp index 3026bd05d..b3ac931cb 100644 --- a/src/commands/CmdDelete.cpp +++ b/src/commands/CmdDelete.cpp @@ -49,6 +49,7 @@ CmdDelete::CmdDelete() { _displays_id = false; _needs_confirm = true; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = true; diff --git a/src/commands/CmdDenotate.cpp b/src/commands/CmdDenotate.cpp index 363834843..e8a43ad63 100644 --- a/src/commands/CmdDenotate.cpp +++ b/src/commands/CmdDenotate.cpp @@ -49,6 +49,7 @@ CmdDenotate::CmdDenotate() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; diff --git a/src/commands/CmdDiagnostics.cpp b/src/commands/CmdDiagnostics.cpp index e4ac10bae..8baa39537 100644 --- a/src/commands/CmdDiagnostics.cpp +++ b/src/commands/CmdDiagnostics.cpp @@ -50,6 +50,7 @@ CmdDiagnostics::CmdDiagnostics() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdDone.cpp b/src/commands/CmdDone.cpp index 1e3638d33..d1ea64adb 100644 --- a/src/commands/CmdDone.cpp +++ b/src/commands/CmdDone.cpp @@ -44,6 +44,7 @@ CmdDone::CmdDone() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = true; diff --git a/src/commands/CmdDuplicate.cpp b/src/commands/CmdDuplicate.cpp index 381d6c368..b6a68854d 100644 --- a/src/commands/CmdDuplicate.cpp +++ b/src/commands/CmdDuplicate.cpp @@ -44,6 +44,7 @@ CmdDuplicate::CmdDuplicate() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = true; diff --git a/src/commands/CmdEdit.cpp b/src/commands/CmdEdit.cpp index acdd1ce4c..b7890bdee 100644 --- a/src/commands/CmdEdit.cpp +++ b/src/commands/CmdEdit.cpp @@ -64,6 +64,7 @@ CmdEdit::CmdEdit() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; diff --git a/src/commands/CmdExec.cpp b/src/commands/CmdExec.cpp index 3391f0447..c3457cf67 100644 --- a/src/commands/CmdExec.cpp +++ b/src/commands/CmdExec.cpp @@ -40,6 +40,7 @@ CmdExec::CmdExec() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdExport.cpp b/src/commands/CmdExport.cpp index a51f6428e..c697ff221 100644 --- a/src/commands/CmdExport.cpp +++ b/src/commands/CmdExport.cpp @@ -42,6 +42,7 @@ CmdExport::CmdExport() { _read_only = true; _displays_id = true; _needs_gc = true; + _needs_recur_update = true; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; @@ -82,10 +83,6 @@ int CmdExport::execute(std::string& output) { // Add the report filter to any existing filter. if (reportFilter != "") Context::getContext().cli2.addFilter(reportFilter); - // Make sure reccurent tasks are generated. - handleUntil(); - handleRecurrence(); - // Apply filter. Filter filter; std::vector filtered; diff --git a/src/commands/CmdGet.cpp b/src/commands/CmdGet.cpp index c6114cc28..2954e6848 100644 --- a/src/commands/CmdGet.cpp +++ b/src/commands/CmdGet.cpp @@ -43,6 +43,7 @@ CmdGet::CmdGet() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdHelp.cpp b/src/commands/CmdHelp.cpp index 033b12789..c66715bff 100644 --- a/src/commands/CmdHelp.cpp +++ b/src/commands/CmdHelp.cpp @@ -44,6 +44,7 @@ CmdHelp::CmdHelp() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdHistory.cpp b/src/commands/CmdHistory.cpp index 4449aea13..d6f1f9147 100644 --- a/src/commands/CmdHistory.cpp +++ b/src/commands/CmdHistory.cpp @@ -55,6 +55,7 @@ CmdHistoryBase::CmdHistoryBase() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; diff --git a/src/commands/CmdIDs.cpp b/src/commands/CmdIDs.cpp index 36024163c..acb2df9e7 100644 --- a/src/commands/CmdIDs.cpp +++ b/src/commands/CmdIDs.cpp @@ -46,6 +46,7 @@ CmdIDs::CmdIDs() { _read_only = true; _displays_id = true; _needs_gc = true; + _needs_recur_update = true; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; @@ -56,8 +57,6 @@ CmdIDs::CmdIDs() { //////////////////////////////////////////////////////////////////////////////// int CmdIDs::execute(std::string& output) { // Apply filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); @@ -127,6 +126,7 @@ CmdCompletionIds::CmdCompletionIds() { _read_only = true; _displays_id = true; _needs_gc = true; + _needs_recur_update = true; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; @@ -163,6 +163,7 @@ CmdZshCompletionIds::CmdZshCompletionIds() { _read_only = true; _displays_id = true; _needs_gc = true; + _needs_recur_update = true; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; @@ -199,6 +200,7 @@ CmdUUIDs::CmdUUIDs() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; @@ -234,6 +236,7 @@ CmdCompletionUuids::CmdCompletionUuids() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; @@ -269,6 +272,7 @@ CmdZshCompletionUuids::CmdZshCompletionUuids() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; diff --git a/src/commands/CmdImport.cpp b/src/commands/CmdImport.cpp index 31a2e141a..b3ccc0c2a 100644 --- a/src/commands/CmdImport.cpp +++ b/src/commands/CmdImport.cpp @@ -45,6 +45,7 @@ CmdImport::CmdImport() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdImportV2.cpp b/src/commands/CmdImportV2.cpp index e34e597a2..a4619fdce 100644 --- a/src/commands/CmdImportV2.cpp +++ b/src/commands/CmdImportV2.cpp @@ -46,6 +46,7 @@ CmdImportV2::CmdImportV2() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp index cfb3263e8..7ffb56942 100644 --- a/src/commands/CmdInfo.cpp +++ b/src/commands/CmdInfo.cpp @@ -59,6 +59,7 @@ CmdInfo::CmdInfo() { // Once the test suite is completely modified, this can be corrected. _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; diff --git a/src/commands/CmdLog.cpp b/src/commands/CmdLog.cpp index af3e9f361..6263cf299 100644 --- a/src/commands/CmdLog.cpp +++ b/src/commands/CmdLog.cpp @@ -40,6 +40,7 @@ CmdLog::CmdLog() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = false; _accepts_modifications = true; diff --git a/src/commands/CmdLogo.cpp b/src/commands/CmdLogo.cpp index 37efec23d..75db2c837 100644 --- a/src/commands/CmdLogo.cpp +++ b/src/commands/CmdLogo.cpp @@ -39,6 +39,7 @@ CmdLogo::CmdLogo() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdModify.cpp b/src/commands/CmdModify.cpp index ba86ea1f9..ed6e3d609 100644 --- a/src/commands/CmdModify.cpp +++ b/src/commands/CmdModify.cpp @@ -48,6 +48,7 @@ CmdModify::CmdModify() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = true; _accepts_modifications = true; diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 27202ff64..6eeec1ba6 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -57,6 +57,7 @@ CmdNews::CmdNews() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdPrepend.cpp b/src/commands/CmdPrepend.cpp index 79d67a881..1f148db48 100644 --- a/src/commands/CmdPrepend.cpp +++ b/src/commands/CmdPrepend.cpp @@ -44,6 +44,7 @@ CmdPrepend::CmdPrepend() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = true; diff --git a/src/commands/CmdProjects.cpp b/src/commands/CmdProjects.cpp index dc98f8af3..612ba93ed 100644 --- a/src/commands/CmdProjects.cpp +++ b/src/commands/CmdProjects.cpp @@ -47,6 +47,7 @@ CmdProjects::CmdProjects() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; @@ -59,8 +60,6 @@ int CmdProjects::execute(std::string& output) { int rc = 0; // Get all the tasks. - handleUntil(); - handleRecurrence(); auto tasks = Context::getContext().tdb2.pending_tasks(); if (Context::getContext().config.getBoolean("list.all.projects")) @@ -141,6 +140,7 @@ CmdCompletionProjects::CmdCompletionProjects() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; @@ -151,8 +151,6 @@ CmdCompletionProjects::CmdCompletionProjects() { //////////////////////////////////////////////////////////////////////////////// int CmdCompletionProjects::execute(std::string& output) { // Get all the tasks. - handleUntil(); - handleRecurrence(); auto tasks = Context::getContext().tdb2.pending_tasks(); if (Context::getContext().config.getBoolean("list.all.projects")) diff --git a/src/commands/CmdPurge.cpp b/src/commands/CmdPurge.cpp index 5bb309ddb..e7374064e 100644 --- a/src/commands/CmdPurge.cpp +++ b/src/commands/CmdPurge.cpp @@ -43,6 +43,7 @@ CmdPurge::CmdPurge() { _displays_id = false; _needs_confirm = true; _needs_gc = true; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; diff --git a/src/commands/CmdReports.cpp b/src/commands/CmdReports.cpp index 1b2d83eed..4aa65e712 100644 --- a/src/commands/CmdReports.cpp +++ b/src/commands/CmdReports.cpp @@ -43,6 +43,7 @@ CmdReports::CmdReports() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index a5f76e1f2..60e4f1657 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -53,6 +53,7 @@ CmdShow::CmdShow() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdStart.cpp b/src/commands/CmdStart.cpp index 10a5c9e5c..5216fab88 100644 --- a/src/commands/CmdStart.cpp +++ b/src/commands/CmdStart.cpp @@ -44,6 +44,7 @@ CmdStart::CmdStart() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = true; diff --git a/src/commands/CmdStats.cpp b/src/commands/CmdStats.cpp index 3450d8f11..7639594d9 100644 --- a/src/commands/CmdStats.cpp +++ b/src/commands/CmdStats.cpp @@ -49,6 +49,7 @@ CmdStats::CmdStats() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; diff --git a/src/commands/CmdStop.cpp b/src/commands/CmdStop.cpp index 2698a8436..1c2bf7596 100644 --- a/src/commands/CmdStop.cpp +++ b/src/commands/CmdStop.cpp @@ -43,6 +43,7 @@ CmdStop::CmdStop() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = true; diff --git a/src/commands/CmdSummary.cpp b/src/commands/CmdSummary.cpp index 76a83a29b..6bea3aae1 100644 --- a/src/commands/CmdSummary.cpp +++ b/src/commands/CmdSummary.cpp @@ -49,6 +49,7 @@ CmdSummary::CmdSummary() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; @@ -65,8 +66,6 @@ int CmdSummary::execute(std::string& output) { bool showAllProjects = Context::getContext().config.getBoolean("summary.all.projects"); // Apply filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index e4f07e8f6..427cca5dd 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -49,6 +49,7 @@ CmdSync::CmdSync() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdTags.cpp b/src/commands/CmdTags.cpp index a0317d4fb..4c79832ad 100644 --- a/src/commands/CmdTags.cpp +++ b/src/commands/CmdTags.cpp @@ -46,6 +46,7 @@ CmdTags::CmdTags() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = false; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; @@ -136,6 +137,7 @@ CmdCompletionTags::CmdCompletionTags() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; diff --git a/src/commands/CmdTimesheet.cpp b/src/commands/CmdTimesheet.cpp index bd076acd3..13b8d13c1 100644 --- a/src/commands/CmdTimesheet.cpp +++ b/src/commands/CmdTimesheet.cpp @@ -48,6 +48,7 @@ CmdTimesheet::CmdTimesheet() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = true; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; @@ -90,8 +91,6 @@ int CmdTimesheet::execute(std::string& output) { } // Apply filter to get a set of tasks. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); diff --git a/src/commands/CmdUDAs.cpp b/src/commands/CmdUDAs.cpp index 540d1726b..78742ca0a 100644 --- a/src/commands/CmdUDAs.cpp +++ b/src/commands/CmdUDAs.cpp @@ -48,6 +48,7 @@ CmdUDAs::CmdUDAs() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; @@ -156,6 +157,7 @@ CmdCompletionUDAs::CmdCompletionUDAs() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdUndo.cpp b/src/commands/CmdUndo.cpp index a5257ab93..cf808f6a5 100644 --- a/src/commands/CmdUndo.cpp +++ b/src/commands/CmdUndo.cpp @@ -45,6 +45,7 @@ CmdUndo::CmdUndo() { _read_only = false; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/CmdUnique.cpp b/src/commands/CmdUnique.cpp index 6833afb24..4b24c0043 100644 --- a/src/commands/CmdUnique.cpp +++ b/src/commands/CmdUnique.cpp @@ -43,6 +43,7 @@ CmdUnique::CmdUnique() { _read_only = true; _displays_id = true; _needs_gc = true; + _needs_recur_update = false; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; diff --git a/src/commands/CmdUrgency.cpp b/src/commands/CmdUrgency.cpp index 4d2b4a31f..cbd1b45e3 100644 --- a/src/commands/CmdUrgency.cpp +++ b/src/commands/CmdUrgency.cpp @@ -45,6 +45,7 @@ CmdUrgency::CmdUrgency() { _read_only = true; _displays_id = false; _needs_gc = true; + _needs_recur_update = false; _uses_context = false; _accepts_filter = true; _accepts_modifications = false; diff --git a/src/commands/CmdVersion.cpp b/src/commands/CmdVersion.cpp index fd82bda98..e4dd57e10 100644 --- a/src/commands/CmdVersion.cpp +++ b/src/commands/CmdVersion.cpp @@ -48,6 +48,7 @@ CmdVersion::CmdVersion() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; @@ -101,6 +102,7 @@ CmdCompletionVersion::CmdCompletionVersion() { _read_only = true; _displays_id = false; _needs_gc = false; + _needs_recur_update = false; _uses_context = false; _accepts_filter = false; _accepts_modifications = false; diff --git a/src/commands/Command.cpp b/src/commands/Command.cpp index dc6b36d87..366a10666 100644 --- a/src/commands/Command.cpp +++ b/src/commands/Command.cpp @@ -295,6 +295,7 @@ Command::Command() _displays_id(true), _needs_confirm(false), _needs_gc(true), + _needs_recur_update(false), _uses_context(false), _accepts_filter(false), _accepts_modifications(false), @@ -322,6 +323,9 @@ bool Command::displays_id() const { return _displays_id; } //////////////////////////////////////////////////////////////////////////////// bool Command::needs_gc() const { return _needs_gc; } +//////////////////////////////////////////////////////////////////////////////// +bool Command::needs_recur_update() const { return _needs_recur_update; } + //////////////////////////////////////////////////////////////////////////////// bool Command::uses_context() const { return _uses_context; } diff --git a/src/commands/Command.h b/src/commands/Command.h index 43d6ced89..02ff51ce6 100644 --- a/src/commands/Command.h +++ b/src/commands/Command.h @@ -62,6 +62,7 @@ class Command { bool read_only() const; bool displays_id() const; bool needs_gc() const; + bool needs_recur_update() const; virtual bool uses_context() const; bool accepts_filter() const; bool accepts_modifications() const; @@ -81,6 +82,7 @@ class Command { bool _displays_id; bool _needs_confirm; bool _needs_gc; + bool _needs_recur_update; bool _uses_context; bool _accepts_filter; bool _accepts_modifications; diff --git a/src/main.h b/src/main.h index 4b2ff2a57..9220a1345 100644 --- a/src/main.h +++ b/src/main.h @@ -47,9 +47,6 @@ std::optional getNextRecurrence(Datetime&, std::string&); bool generateDueDates(Task&, std::vector&); void updateRecurrenceMask(Task&); -// recur2.cpp -void handleRecurrence2(); - // nag.cpp void nag(std::vector&); diff --git a/test/commands.test.py b/test/commands.test.py index ee2951852..7ee9e38a3 100755 --- a/test/commands.test.py +++ b/test/commands.test.py @@ -44,7 +44,7 @@ class TestCommands(TestCase): code, out, err = self.t("commands") self.assertRegex(out, r"add\s+operation\s+RW\s+Ctxt\s+Mods\s+Adds a new task") self.assertRegex( - out, r"list\s+report\s+RO\s+ID\s+GC\s+Ctxt\s+Filt\s+Most details of" + out, r"list\s+report\s+RO\s+ID\s+GC\s+Recur\s+Ctxt\s+Filt\s+Most details of" ) self.assertRegex(out, r"modify\s+operation\s+RW\s+Filt\s+Mods\s+Modifies the") @@ -53,7 +53,7 @@ class TestCommands(TestCase): code, out, err = self.t("commands rc._forcecolor:on") self.assertRegex(out, r"add\s+operation\s+RW\s+Ctxt\s+Mods\s+Adds a new task") self.assertRegex( - out, r"list\s+report\s+RO\s+ID\s+GC\s+Ctxt\s+Filt\s+Most details of" + out, r"list\s+report\s+RO\s+ID\s+GC\s+Recur\s+Ctxt\s+Filt\s+Most details of" ) self.assertRegex(out, r"modify\s+operation\s+RW\s+Filt\s+Mods\s+Modifies the") From 20417b311e8fadca79bf72208d002d538ebe2820 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 13:27:51 -0500 Subject: [PATCH 177/242] [pre-commit.ci] pre-commit autoupdate (#3762) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v19.1.6 → v19.1.7](https://github.com/pre-commit/mirrors-clang-format/compare/v19.1.6...v19.1.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ea9333263..7314def8e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v19.1.6 + rev: v19.1.7 hooks: - id: clang-format types_or: [c++, c] From 8d210b526327b72b919ebe3beadbde61c9b56e2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:00:57 -0500 Subject: [PATCH 178/242] Bump docker/build-push-action from 6.11.0 to 6.12.0 (#3761) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.11.0 to 6.12.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.11.0...v6.12.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index d774bd3ed..ffc082981 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -44,7 +44,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.11.0 + uses: docker/build-push-action@v6.12.0 with: context: . file: "./docker/task.dockerfile" From 3ae7413ebde0117fd9fb7cb76e0d3077c85369f9 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 1 Feb 2025 20:37:43 +0000 Subject: [PATCH 179/242] Update task.md json lines end with \n (0xA) not \r (0xD) (#3752) Update task.md to fix mistake in json format. In the code the json lines end with \n (0xA) not \r (0xD) --- doc/devel/rfcs/task.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/devel/rfcs/task.md b/doc/devel/rfcs/task.md index 312b8ee72..89eb7a9de 100644 --- a/doc/devel/rfcs/task.md +++ b/doc/devel/rfcs/task.md @@ -25,7 +25,7 @@ In brief: "MUST" (or "REQUIRED") means that the item is an absolute requirement ## General Format -The format is JSON, specifically a JSON object as a single line of text, terminated by a newline (U+000D). +The format is JSON, specifically a JSON object as a single line of text, terminated by a line feed (U+000A). The JSON looks like this: From 244513fad7fe1a88a6480c64ac0e1d562f794983 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 15:51:35 -0500 Subject: [PATCH 180/242] Bump docker/build-push-action from 6.12.0 to 6.13.0 (#3766) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.12.0 to 6.13.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.12.0...v6.13.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index ffc082981..ee51263da 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -44,7 +44,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.12.0 + uses: docker/build-push-action@v6.13.0 with: context: . file: "./docker/task.dockerfile" From e1fc283da5f7781124fc8271f72d3d5858c9ce4e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:13:57 -0500 Subject: [PATCH 181/242] [pre-commit.ci] pre-commit autoupdate (#3773) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 24.10.0 → 25.1.0](https://github.com/psf/black/compare/24.10.0...25.1.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7314def8e..7e9ce69fa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,6 @@ repos: - id: clang-format types_or: [c++, c] - repo: https://github.com/psf/black - rev: 24.10.0 + rev: 25.1.0 hooks: - id: black From fdb7e5e0202403b7574d5cf8c4d140e12242aa55 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 5 Feb 2025 08:20:35 -0500 Subject: [PATCH 182/242] Test for unusual task data (#3770) * Test for unusual task data A task might have any combination of keys and values, but Taskwarrior often assumes that only valid values can occur, and crashes otherwise. This is highly inconvenient, as it's often impossible to do anything with the invalid task -- Taskwarrior just fails without modifying it. So, this is the beginning of some testing for such invalid tasks, with the goal of making Taskwarrior due something reasonable. In general, an invalid attribute value is treated as if it was not set. This is not exhaustive, and there are likely still bugs of this sort, but as we find them we can fix and add regression tests to this script. This introduces a new test-only binary that creates a "bare" task using TaskChampion, avoiding Taskwarrior's efforts to not create "unusual" tasks. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/TDB2.cpp | 4 - src/Task.cpp | 19 ++-- src/commands/CmdAdd.cpp | 5 + src/commands/CmdInfo.cpp | 60 +++++++---- src/commands/CmdModify.cpp | 2 +- src/feedback.cpp | 7 +- src/recur.cpp | 15 ++- test/CMakeLists.txt | 10 ++ test/basetest/utils.py | 5 +- test/make_tc_task.cpp | 80 +++++++++++++++ test/unusual_task.test.py | 202 +++++++++++++++++++++++++++++++++++++ 11 files changed, 368 insertions(+), 41 deletions(-) create mode 100644 test/make_tc_task.cpp create mode 100755 test/unusual_task.test.py diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 2e37640ef..6f55eedab 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -57,10 +57,6 @@ void TDB2::open_replica(const std::string& location, bool create_if_missing) { //////////////////////////////////////////////////////////////////////////////// // Add the new task to the replica. void TDB2::add(Task& task) { - // Validate a task for addition. This is stricter than `task.validate`, as any - // inconsistency is probably user error. - task.validate_add(); - // Ensure the task is consistent, and provide defaults if necessary. // bool argument to validate() is "applyDefault", to apply default values for // properties not otherwise given. diff --git a/src/Task.cpp b/src/Task.cpp index bab293341..a41f4d5ae 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -321,8 +321,8 @@ void Task::setStatus(Task::status status) { //////////////////////////////////////////////////////////////////////////////// // Determines status of a date attribute. Task::dateState Task::getDateState(const std::string& name) const { - std::string value = get(name); - if (value.length()) { + time_t value = get_date(name); + if (value > 0) { Datetime reference(value); Datetime now; Datetime today("today"); @@ -804,13 +804,16 @@ std::string Task::composeJSON(bool decorate /*= false*/) const { // Date fields are written as ISO 8601. if (type == "date") { - Datetime d(i.second); - out << '"' << (i.first == "modification" ? "modified" : i.first) - << "\":\"" - // Date was deleted, do not export parsed empty string - << (i.second == "" ? "" : d.toISO()) << '"'; + time_t epoch = get_date(i.first); + if (epoch != 0) { + Datetime d(i.second); + out << '"' << (i.first == "modification" ? "modified" : i.first) + << "\":\"" + // Date was deleted, do not export parsed empty string + << (i.second == "" ? "" : d.toISO()) << '"'; - ++attributes_written; + ++attributes_written; + } } /* diff --git a/src/commands/CmdAdd.cpp b/src/commands/CmdAdd.cpp index 5778ab737..429ac9480 100644 --- a/src/commands/CmdAdd.cpp +++ b/src/commands/CmdAdd.cpp @@ -56,6 +56,11 @@ int CmdAdd::execute(std::string& output) { // the task is empty, but DOM references can refer to earlier parts of the // command line, e.g., `task add due:20110101 wait:due`. task.modify(Task::modReplace, true); + + // Validate a task for addition. This is stricter than `task.validate`, as any + // inconsistency is probably user error. + task.validate_add(); + Context::getContext().tdb2.add(task); // Do not display ID 0, users cannot query by that diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp index 7ffb56942..b805277b9 100644 --- a/src/commands/CmdInfo.cpp +++ b/src/commands/CmdInfo.cpp @@ -120,9 +120,11 @@ int CmdInfo::execute(std::string& output) { description += '\n' + std::string(indent, ' ') + Datetime(anno.first.substr(11)).toString(dateformatanno) + ' ' + anno.second; - row = view.addRow(); - view.set(row, 0, "Description"); - view.set(row, 1, description, c); + if (task.has("description")) { + row = view.addRow(); + view.set(row, 0, "Description"); + view.set(row, 1, description, c); + } // status row = view.addRow(); @@ -215,64 +217,76 @@ int CmdInfo::execute(std::string& output) { } // entry - row = view.addRow(); - view.set(row, 0, "Entered"); - Datetime dt(task.get_date("entry")); - std::string entry = dt.toString(dateformat); + if (task.has("entry") && task.get_date("entry")) { + row = view.addRow(); + view.set(row, 0, "Entered"); + Datetime dt(task.get_date("entry")); + std::string entry = dt.toString(dateformat); - std::string age; - auto created = task.get("entry"); - if (created.length()) { - Datetime dt(strtoll(created.c_str(), nullptr, 10)); - age = Duration(now - dt).formatVague(); + std::string age; + auto created = task.get("entry"); + if (created.length()) { + Datetime dt(strtoll(created.c_str(), nullptr, 10)); + 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 - if (task.has("wait")) { + if (validDate("wait")) { row = view.addRow(); view.set(row, 0, "Waiting until"); view.set(row, 1, Datetime(task.get_date("wait")).toString(dateformat)); } // scheduled - if (task.has("scheduled")) { + if (validDate("scheduled")) { row = view.addRow(); view.set(row, 0, "Scheduled"); view.set(row, 1, Datetime(task.get_date("scheduled")).toString(dateformat)); } // start - if (task.has("start")) { + if (validDate("start")) { row = view.addRow(); view.set(row, 0, "Start"); view.set(row, 1, Datetime(task.get_date("start")).toString(dateformat)); } // due (colored) - if (task.has("due")) { + if (validDate("due")) { row = view.addRow(); view.set(row, 0, "Due"); view.set(row, 1, Datetime(task.get_date("due")).toString(dateformat)); } // end - if (task.has("end")) { + if (validDate("end")) { row = view.addRow(); view.set(row, 0, "End"); view.set(row, 1, Datetime(task.get_date("end")).toString(dateformat)); } // until - if (task.has("until")) { + if (validDate("until")) { row = view.addRow(); view.set(row, 0, "Until"); view.set(row, 1, Datetime(task.get_date("until")).toString(dateformat)); } // modified - if (task.has("modified")) { + if (validDate("modified")) { row = view.addRow(); view.set(row, 0, "Last modified"); @@ -632,7 +646,11 @@ std::optional CmdInfo::formatForInfo(const std::vector& } else { // Record the last start time for later duration calculation. 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), diff --git a/src/commands/CmdModify.cpp b/src/commands/CmdModify.cpp index ed6e3d609..8a92fb802 100644 --- a/src/commands/CmdModify.cpp +++ b/src/commands/CmdModify.cpp @@ -119,7 +119,7 @@ void CmdModify::checkConsistency(Task &before, Task &after) { throw std::string("You cannot remove the recurrence from a recurring task."); if ((before.getStatus() == Task::pending) && (after.getStatus() == Task::pending) && - (after.get("end") != "")) + (before.get("end") == "") && (after.get("end") != "")) throw format("Could not modify task {1}. You cannot set an end date on a pending task.", before.identifier(true)); } diff --git a/src/feedback.cpp b/src/feedback.cpp index 60d89a686..22b9f9dc5 100644 --- a/src/feedback.cpp +++ b/src/feedback.cpp @@ -51,7 +51,12 @@ std::string renderAttribute(const std::string& name, const std::string& value, if (Context::getContext().columns.find(name) != Context::getContext().columns.end()) { Column* col = Context::getContext().columns[name]; if (col && col->type() == "date" && value != "") { - Datetime d((time_t)strtoll(value.c_str(), nullptr, 10)); + int64_t epoch = strtoll(value.c_str(), nullptr, 10); + // Do not try to render an un-parseable value. + if (epoch == 0) { + return value; + } + Datetime d((time_t)epoch); if (format == "") return d.toString(Context::getContext().config.get("dateformat")); return d.toString(format); diff --git a/src/recur.cpp b/src/recur.cpp index d6bb47454..4168b6691 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -288,9 +288,11 @@ std::optional getNextRecurrence(Datetime& current, std::string& period else if (unicodeLatinDigit(period[0]) && period[period.length() - 1] == 'q') { int increment = strtol(period.substr(0, period.length() - 1).c_str(), nullptr, 10); - if (increment <= 0) - throw format("Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, - increment); + if (increment <= 0) { + Context::getContext().footnote(format( + "Recurrence period '{1}' is equivalent to {2} and hence invalid.", period, increment)); + return std::nullopt; + } m += 3 * increment; while (m > 12) { @@ -346,8 +348,11 @@ std::optional getNextRecurrence(Datetime& current, std::string& period // Add the period to current, and we're done. std::string::size_type idx = 0; Duration p; - if (!p.parse(period, idx)) - throw std::string(format("The recurrence value '{1}' is not valid.", period)); + if (!p.parse(period, idx)) { + Context::getContext().footnote( + format("Warning: The recurrence value '{1}' is not valid.", period)); + return std::nullopt; + } return checked_add_datetime(current, p.toTime_t()); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 08051bab2..8b9678e6d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -188,6 +188,7 @@ set (pythonTests undo.test.py unicode.test.py unique.test.py + unusual_task.test.py upgrade.test.py urgency.test.py urgency_inherit.test.py @@ -207,6 +208,15 @@ foreach (python_Test ${pythonTests}) ) endforeach(python_Test) +# Create a `make_tc_task` binary, used for unusual_task.test.py. In order to build this +# for the tests, it's added as a dependency of `test_runner`. +add_executable (make_tc_task make_tc_task.cpp) +target_link_libraries (make_tc_task task commands columns libshared task commands columns libshared task commands columns libshared ${TASK_LIBRARIES}) +if (DARWIN) + target_link_libraries (make_tc_task "-framework CoreFoundation -framework Security -framework SystemConfiguration") +endif (DARWIN) +add_dependencies(test_runner make_tc_task) + # -- Shell tests set (shell_SRCS diff --git a/test/basetest/utils.py b/test/basetest/utils.py index 2d665d8c3..d2ba9aae0 100644 --- a/test/basetest/utils.py +++ b/test/basetest/utils.py @@ -28,8 +28,11 @@ ON_POSIX = "posix" in sys.builtin_module_names # Directory relative to basetest module location CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +# From the CMAKE value of the same name. This is substituted at configure. +CMAKE_BINARY_DIR = os.path.abspath("${CMAKE_BINARY_DIR}") + # Location of binary files (usually the src/ folder) -BIN_PREFIX = os.path.abspath(os.path.join("${CMAKE_BINARY_DIR}", "src")) +BIN_PREFIX = os.path.abspath(os.path.join(CMAKE_BINARY_DIR, "src")) # Default location of test hooks DEFAULT_HOOK_PATH = os.path.abspath( diff --git a/test/make_tc_task.cpp b/test/make_tc_task.cpp new file mode 100644 index 000000000..2cd5d0cc8 --- /dev/null +++ b/test/make_tc_task.cpp @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2025, Dustin J. Mitchell +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// https://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// + +#include +// cmake.h include header must come first + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "format.h" + +namespace { + +//////////////////////////////////////////////////////////////////////////////// +int usage() { + std::cerr << "USAGE: make_tc_task DATADIR KEY=VALUE ..\n"; + return 1; +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) { + if (!--argc) { + return usage(); + } + char *datadir = *++argv; + + auto replica = tc::new_replica_on_disk(datadir, true); + auto uuid = tc::uuid_v4(); + auto operations = tc::new_operations(); + auto task = tc::create_task(uuid, operations); + + while (--argc) { + std::string arg = *++argv; + size_t eq_idx = arg.find('='); + if (eq_idx == std::string::npos) { + return usage(); + } + std::string property = arg.substr(0, eq_idx); + std::string value = arg.substr(eq_idx + 1); + task->update(property, value, operations); + } + replica->commit_operations(std::move(operations)); + + std::cout << static_cast(uuid.to_string()) << "\n"; + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/test/unusual_task.test.py b/test/unusual_task.test.py new file mode 100755 index 000000000..8eb5bbaf7 --- /dev/null +++ b/test/unusual_task.test.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +############################################################################### +# +# Copyright 2025 Dustin J. Mitchell +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# https://www.opensource.org/licenses/mit-license.php +# +############################################################################### + +import sys +import os +import re +import time +import unittest + +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Task, TestCase +from basetest.utils import run_cmd_wait, CMAKE_BINARY_DIR + + +class TestUnusualTasks(TestCase): + def setUp(self): + """Executed before each test in the class""" + self.t = Task() + self.t.config( + "report.custom-report.columns", + "id,description,entry,start,end,due,scheduled,modified,until", + ) + self.t.config("verbose", "nothing") + + def make_task(self, **props): + make_tc_task = os.path.abspath( + os.path.join(CMAKE_BINARY_DIR, "test", "make_tc_task") + ) + cmd = [make_tc_task, self.t.datadir] + for p, v in props.items(): + cmd.append(f"{p}={v}") + _, out, _ = run_cmd_wait(cmd) + return out.strip() + + def test_empty_task_info(self): + uuid = self.make_task() + _, out, _ = self.t(f"{uuid} info") + self.assertNotIn("Entered", out) + self.assertNotIn("Waiting", out) + self.assertNotIn("Last modified", out) + self.assertNotIn("Start", out) + self.assertNotIn("End", out) + self.assertNotIn("Due", out) + self.assertNotIn("Until", out) + self.assertRegex(out, r"Status\s+Pending") + + def test_modify_empty_task(self): + uuid = self.make_task() + self.t(f"{uuid} modify a description +taggy due:tomorrow") + _, out, _ = self.t(f"{uuid} info") + self.assertRegex(out, r"Description\s+a description") + self.assertRegex(out, r"Tags\s+taggy") + + def test_empty_task_recurring(self): + uuid = self.make_task(status="recurring") + _, out, _ = self.t(f"{uuid} info") + self.assertRegex(out, r"Status\s+Recurring") + _, out, _ = self.t(f"{uuid} custom-report") + + def test_recurring_invalid_rtype(self): + uuid = self.make_task( + status="recurring", due=str(int(time.time())), rtype="occasional" + ) + _, out, _ = self.t(f"{uuid} info") + self.assertRegex(out, r"Status\s+Recurring") + self.assertRegex(out, r"Recurrence type\s+occasional") + _, out, _ = self.t(f"{uuid} custom-report") + + def test_recurring_invalid_recur(self): + uuid = self.make_task( + status="recurring", + due=str(int(time.time())), + rtype="periodic", + recur="xxxxx", + ) + _, out, _ = self.t(f"{uuid} info") + self.assertRegex(out, r"Status\s+Recurring") + self.assertRegex(out, r"Recurrence type\s+periodic") + _, out, _ = self.t(f"{uuid} custom-report") + + def test_recurring_bad_quarters_rtype(self): + uuid = self.make_task( + status="recurring", due=str(int(time.time())), rtype="periodic", recur="9aq" + ) + _, out, _ = self.t(f"{uuid} custom-report") + + def test_invalid_entry_info(self): + uuid = self.make_task(entry="abcdef") + _, out, _ = self.t(f"{uuid} info") + self.assertNotIn("Entered", out) + + def test_invalid_modified_info(self): + uuid = self.make_task(modified="abcdef") + _, out, _ = self.t(f"{uuid} info") + self.assertNotIn(r"Last modified", out) + + def test_invalid_start_info(self): + uuid = self.make_task(start="abcdef") + _, out, _ = self.t(f"{uuid} info") + + def test_invalid_dates_report(self): + uuid = self.make_task( + wait="wait", + scheduled="scheduled", + start="start", + due="due", + end="end", + until="until", + modified="modified", + ) + _, out, _ = self.t(f"{uuid} custom-report") + + def test_invalid_dates_stop(self): + uuid = self.make_task( + wait="wait", + scheduled="scheduled", + start="start", + due="due", + end="end", + until="until", + modified="modified", + ) + _, out, _ = self.t(f"{uuid} stop") + + def test_invalid_dates_modify(self): + uuid = self.make_task( + wait="wait", + scheduled="scheduled", + start="start", + due="due", + end="end", + until="until", + modified="modified", + ) + _, out, _ = self.t(f"{uuid} mod a description +tag") + + def test_invalid_dates_info(self): + uuid = self.make_task( + wait="wait", + scheduled="scheduled", + start="start", + due="due", + end="end", + until="until", + modified="modified", + ) + _, out, _ = self.t(f"{uuid} info") + self.assertNotRegex("^Entered\s+", out) + self.assertNotRegex("^Start\s+", out) + self.assertIn(r"Wait set to 'wait'", out) + self.assertIn(r"Scheduled set to 'scheduled'", out) + self.assertIn(r"Start set to 'start'", out) + self.assertIn(r"Due set to 'due'", out) + self.assertIn(r"End set to 'end'", out) + self.assertIn(r"Until set to 'until'", out) + # (note that 'modified' is not shown in the journal) + + def test_invalid_dates_export(self): + uuid = self.make_task( + wait="wait", + scheduled="scheduled", + start="start", + due="due", + end="end", + until="until", + modified="modified", + ) + _, out, _ = self.t(f"{uuid} export") + + +if __name__ == "__main__": + from simpletap import TAPTestRunner + + unittest.main(testRunner=TAPTestRunner()) + +# vim: ai sts=4 et sw=4 ft=python From 7871617e9c4b9aaa12afe93adbd5619775044c89 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 6 Feb 2025 02:30:41 -0500 Subject: [PATCH 183/242] Only handleRecurrence/handleUntil when rc.gc=1 (#3772) --- src/Context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Context.cpp b/src/Context.cpp index ed4c65770..9fe1e468b 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -869,7 +869,7 @@ int Context::dispatch(std::string& out) { if (config.getBoolean("debug") && config.getInteger("debug.parser") == 1) debug(cli2.dump("Parse Tree (before command-specifіc processing)")); - if (c->needs_recur_update()) { + if (c->needs_recur_update() && Context::getContext().config.getBoolean("gc")) { handleUntil(); handleRecurrence(); } From d6658fe26f50e5a0f87a3a3705a12da5f0fa81bc Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 6 Feb 2025 02:31:33 -0500 Subject: [PATCH 184/242] Remove missed handleRecurrence/handleUntil calls (#3771) These were missed in #3753. --- src/commands/CmdEdit.cpp | 4 +--- src/commands/CmdHistory.cpp | 4 +--- src/commands/CmdIDs.cpp | 10 ---------- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/commands/CmdEdit.cpp b/src/commands/CmdEdit.cpp index b7890bdee..92dcbc828 100644 --- a/src/commands/CmdEdit.cpp +++ b/src/commands/CmdEdit.cpp @@ -64,7 +64,7 @@ CmdEdit::CmdEdit() { _read_only = false; _displays_id = false; _needs_gc = false; - _needs_recur_update = false; + _needs_recur_update = true; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; @@ -78,8 +78,6 @@ CmdEdit::CmdEdit() { // wrench. To be used sparingly. int CmdEdit::execute(std::string&) { // Filter the tasks. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); diff --git a/src/commands/CmdHistory.cpp b/src/commands/CmdHistory.cpp index d6f1f9147..73788b01a 100644 --- a/src/commands/CmdHistory.cpp +++ b/src/commands/CmdHistory.cpp @@ -55,7 +55,7 @@ CmdHistoryBase::CmdHistoryBase() { _read_only = true; _displays_id = false; _needs_gc = false; - _needs_recur_update = false; + _needs_recur_update = true; _uses_context = true; _accepts_filter = true; _accepts_modifications = false; @@ -294,8 +294,6 @@ int CmdHistoryBase::execute(std::string& output) { completedGroup.clear(); // Apply filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); diff --git a/src/commands/CmdIDs.cpp b/src/commands/CmdIDs.cpp index acb2df9e7..5782aaf63 100644 --- a/src/commands/CmdIDs.cpp +++ b/src/commands/CmdIDs.cpp @@ -137,8 +137,6 @@ CmdCompletionIds::CmdCompletionIds() { //////////////////////////////////////////////////////////////////////////////// int CmdCompletionIds::execute(std::string& output) { // Apply filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); @@ -174,8 +172,6 @@ CmdZshCompletionIds::CmdZshCompletionIds() { //////////////////////////////////////////////////////////////////////////////// int CmdZshCompletionIds::execute(std::string& output) { // Apply filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); @@ -211,8 +207,6 @@ CmdUUIDs::CmdUUIDs() { //////////////////////////////////////////////////////////////////////////////// int CmdUUIDs::execute(std::string& output) { // Apply filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); @@ -247,8 +241,6 @@ CmdCompletionUuids::CmdCompletionUuids() { //////////////////////////////////////////////////////////////////////////////// int CmdCompletionUuids::execute(std::string& output) { // Apply filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); @@ -283,8 +275,6 @@ CmdZshCompletionUuids::CmdZshCompletionUuids() { //////////////////////////////////////////////////////////////////////////////// int CmdZshCompletionUuids::execute(std::string& output) { // Apply filter. - handleUntil(); - handleRecurrence(); Filter filter; std::vector filtered; filter.subset(filtered); From a97deb0c8446b8dfb44cc5c492b8426011dc749e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 10:10:51 -0500 Subject: [PATCH 185/242] Bump sigstore/cosign-installer from 3.7.0 to 3.8.0 (#3778) Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.7.0 to 3.8.0. - [Release notes](https://github.com/sigstore/cosign-installer/releases) - [Commits](https://github.com/sigstore/cosign-installer/compare/v3.7.0...v3.8.0) --- updated-dependencies: - dependency-name: sigstore/cosign-installer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index ee51263da..0101a225a 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -33,7 +33,7 @@ jobs: submodules: "recursive" - name: Install cosign - uses: sigstore/cosign-installer@v3.7.0 + uses: sigstore/cosign-installer@v3.8.0 - name: Log into registry ${{ env.REGISTRY }} uses: docker/login-action@v3.3.0 From a701f8fc7dbae219a7c56b53b2d953e0f3c97244 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 14 Feb 2025 04:25:19 -0500 Subject: [PATCH 186/242] Open Replica read-only when possible (#3776) * Open Replica read-only when possible Specifically, when either - the command is read-only; or - the command requires GC (including recurrence updates) but GC is disabled by config * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/Context.cpp | 18 +++++-- src/TDB2.cpp | 14 +++--- src/TDB2.h | 3 +- src/taskchampion-cpp/src/lib.rs | 12 +++-- test/CMakeLists.txt | 1 + test/make_tc_task.cpp | 2 +- test/read-only.test.py | 83 +++++++++++++++++++++++++++++++++ test/tdb2_test.cpp | 4 +- test/view_test.cpp | 1 + 9 files changed, 121 insertions(+), 17 deletions(-) create mode 100755 test/read-only.test.py diff --git a/src/Context.cpp b/src/Context.cpp index 9fe1e468b..611cbdf4c 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -598,9 +598,6 @@ int Context::initialize(int argc, const char** argv) { createDefaultConfig(); - bool create_if_missing = !config.getBoolean("exit.on.missing.db"); - tdb2.open_replica(data_dir, create_if_missing); - //////////////////////////////////////////////////////////////////////////// // // [3] Instantiate Command objects and capture command entities. @@ -674,6 +671,21 @@ int Context::initialize(int argc, const char** argv) { if (foundAssumed) header("No command specified - assuming 'information'."); } + //////////////////////////////////////////////////////////////////////////// + // + // [7.5] Open the Replica. + // + //////////////////////////////////////////////////////////////////////////// + + bool create_if_missing = !config.getBoolean("exit.on.missing.db"); + Command* c = commands[cli2.getCommand()]; + + // We must allow writes if either 'gc' is enabled and the command performs GC, or the command + // itself is read-write. + bool read_write = + (config.getBoolean("gc") && (c->needs_gc() || c->needs_recur_update())) || !c->read_only(); + tdb2.open_replica(data_dir, create_if_missing, read_write); + //////////////////////////////////////////////////////////////////////////// // // [8] Initialize hooks. diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 6f55eedab..227e22965 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -50,10 +50,13 @@ bool TDB2::debug_mode = false; static void dependency_scan(std::vector&); //////////////////////////////////////////////////////////////////////////////// -void TDB2::open_replica(const std::string& location, bool create_if_missing) { - _replica = tc::new_replica_on_disk(location, create_if_missing); +void TDB2::open_replica(const std::string& location, bool create_if_missing, bool read_write) { + _replica = tc::new_replica_on_disk(location, create_if_missing, read_write); } +//////////////////////////////////////////////////////////////////////////////// +void TDB2::open_replica_in_memory() { _replica = tc::new_replica_in_memory(); } + //////////////////////////////////////////////////////////////////////////////// // Add the new task to the replica. void TDB2::add(Task& task) { @@ -190,11 +193,8 @@ void TDB2::purge(Task& task) { //////////////////////////////////////////////////////////////////////////////// rust::Box& TDB2::replica() { - // Create a replica in-memory if `open_replica` has not been called. This - // occurs in tests. - if (!_replica) { - _replica = tc::new_replica_in_memory(); - } + // One of the open_replica_ methods must be called before this one. + assert(_replica); return _replica.value(); } diff --git a/src/TDB2.h b/src/TDB2.h index 3dafe6748..1720cf9ba 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -46,7 +46,8 @@ class TDB2 { TDB2() = default; - void open_replica(const std::string &, bool create_if_missing); + void open_replica(const std::string &, bool create_if_missing, bool read_write); + void open_replica_in_memory(); void add(Task &); void modify(Task &); void purge(Task &); diff --git a/src/taskchampion-cpp/src/lib.rs b/src/taskchampion-cpp/src/lib.rs index 66017fc59..45a146edc 100644 --- a/src/taskchampion-cpp/src/lib.rs +++ b/src/taskchampion-cpp/src/lib.rs @@ -104,8 +104,11 @@ mod ffi { fn new_replica_in_memory() -> Result>; /// Create a new replica stored on-disk. - fn new_replica_on_disk(taskdb_dir: String, create_if_missing: bool) - -> Result>; + fn new_replica_on_disk( + taskdb_dir: String, + create_if_missing: bool, + read_write: bool, + ) -> Result>; /// Commit the given operations to the replica. fn commit_operations(&mut self, ops: Vec) -> Result<()>; @@ -490,11 +493,14 @@ impl From for Replica { fn new_replica_on_disk( taskdb_dir: String, create_if_missing: bool, + read_write: bool, ) -> Result, CppError> { + use tc::storage::AccessMode::*; + let access_mode = if read_write { ReadWrite } else { ReadOnly }; let storage = tc::StorageConfig::OnDisk { taskdb_dir: PathBuf::from(taskdb_dir), create_if_missing, - access_mode: tc::storage::AccessMode::ReadWrite, + access_mode, } .into_storage()?; Ok(Box::new(tc::Replica::new(storage).into())) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8b9678e6d..acff66c67 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -157,6 +157,7 @@ set (pythonTests purge.test.py quotes.test.py rc.override.test.py + read-only.test.py recurrence.test.py reports.test.py search.test.py diff --git a/test/make_tc_task.cpp b/test/make_tc_task.cpp index 2cd5d0cc8..b14560a8e 100644 --- a/test/make_tc_task.cpp +++ b/test/make_tc_task.cpp @@ -56,7 +56,7 @@ int main(int argc, char **argv) { } char *datadir = *++argv; - auto replica = tc::new_replica_on_disk(datadir, true); + auto replica = tc::new_replica_on_disk(datadir, /*create_if_missing=*/true, /*read_write=*/true); auto uuid = tc::uuid_v4(); auto operations = tc::new_operations(); auto task = tc::create_task(uuid, operations); diff --git a/test/read-only.test.py b/test/read-only.test.py new file mode 100755 index 000000000..9385a1b5c --- /dev/null +++ b/test/read-only.test.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +############################################################################### +# +# 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 +# +############################################################################### + +import sys +import os +import platform +import time +import unittest + +# Ensure python finds the local simpletap module +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from basetest import Task, TestCase + + +class TestReadOnly(TestCase): + def setUp(self): + self.t = Task() + self.t("add foo") + + # set the mtime of the taskdb to an hour ago, so we can see any changes + self.taskdb = self.t.datadir + "/taskchampion.sqlite3" + os.utime(self.taskdb, (time.time() - 3600,) * 2) + + def assertNotModified(self): + self.assertLess(os.stat(self.taskdb).st_mtime, time.time() - 1800) + + def assertModified(self): + self.assertGreater(os.stat(self.taskdb).st_mtime, time.time() - 1800) + + def test_read_only_command(self): + code, out, err = self.t("reports") + self.assertNotModified() + + def test_report(self): + code, out, err = self.t("list") + self.assertModified() + + def test_burndown(self): + code, out, err = self.t("burndown") + self.assertModified() + + def test_report_gc_0(self): + self.t.config("gc", "0") + code, out, err = self.t("list") + self.assertNotModified() + + def test_burndown_gc_0(self): + self.t.config("gc", "0") + code, out, err = self.t("burndown") + self.assertNotModified() + + +if __name__ == "__main__": + from simpletap import TAPTestRunner + + unittest.main(testRunner=TAPTestRunner()) + +# vim: ai sts=4 et sw=4 ft=python diff --git a/test/tdb2_test.cpp b/test/tdb2_test.cpp index 22134d250..76a27cc33 100644 --- a/test/tdb2_test.cpp +++ b/test/tdb2_test.cpp @@ -61,7 +61,7 @@ int TEST_NAME(int, char**) { context.config.set("gc", 1); context.config.set("debug", 1); - context.tdb2.open_replica(".", true); + context.tdb2.open_replica(".", /*create_if_missing=*/true, /*read_write=*/true); // Try reading an empty database. std::vector pending = context.tdb2.pending_tasks(); @@ -108,7 +108,7 @@ int TEST_NAME(int, char**) { // Reset for reuse. cleardb(); - context.tdb2.open_replica(".", true); + context.tdb2.open_replica(".", /*create_if_missing=*/true, /*read_write=*/true); // TODO complete a task // TODO gc diff --git a/test/view_test.cpp b/test/view_test.cpp index 76a27541a..51423e4db 100644 --- a/test/view_test.cpp +++ b/test/view_test.cpp @@ -47,6 +47,7 @@ int TEST_NAME(int, char**) { UnitTest t(1); Context context; Context::setContext(&context); + context.tdb2.open_replica_in_memory(); // Ensure environment has no influence. unsetenv("TASKDATA"); From 284948d9f9d5e7147ca73bd7e72918652c3fa9c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 09:30:06 -0500 Subject: [PATCH 187/242] Bump docker/build-push-action from 6.13.0 to 6.14.0 (#3791) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.13.0 to 6.14.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.13.0...v6.14.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 0101a225a..577a96907 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -44,7 +44,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.13.0 + uses: docker/build-push-action@v6.14.0 with: context: . file: "./docker/task.dockerfile" From 8e90dc15713fe1778c87c97311b1953a0502fb1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 09:30:15 -0500 Subject: [PATCH 188/242] Bump sigstore/cosign-installer from 3.8.0 to 3.8.1 (#3790) Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.8.0 to 3.8.1. - [Release notes](https://github.com/sigstore/cosign-installer/releases) - [Commits](https://github.com/sigstore/cosign-installer/compare/v3.8.0...v3.8.1) --- updated-dependencies: - dependency-name: sigstore/cosign-installer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 577a96907..7c4ea0733 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -33,7 +33,7 @@ jobs: submodules: "recursive" - name: Install cosign - uses: sigstore/cosign-installer@v3.8.0 + uses: sigstore/cosign-installer@v3.8.1 - name: Log into registry ${{ env.REGISTRY }} uses: docker/login-action@v3.3.0 From 55c02f5420f43b57d0532ce34d1740c49c011eae Mon Sep 17 00:00:00 2001 From: Tobias Predel Date: Sat, 1 Mar 2025 16:42:40 +0100 Subject: [PATCH 189/242] Remove unused includes (#3795) * Remove unused include in Variant.h * Remove unused include in util.h * Remove unused include directives in util.cpp * Remove unused include directives in TF2.h * Remove unused include directives in TF2.cpp * Remove unused include directive in TDB2.h * Remove unused include directives in TDB2.cpp * Remove unused include directives in Task.h * Remove unused include directive in rules.cpp * Remove unused include directives in rules.cpp * Remove unused include directives in nag.cpp * Remove unused include directive in main.h * Remove unused include directive in legacy.cpph * Remove unused include directive in Hooks.cpp * Remove unused include directive in Filter.h * Remove unused include directive in Filter.cpp * Remove unused include directive in feedback.cpp * Remove unused include directive in Eval.cpp * Remove unused include directives in dependency.cpp * Remove unused include directivess in Context.cpp --- src/Context.cpp | 3 --- src/Eval.cpp | 1 - src/Filter.cpp | 2 -- src/Filter.h | 1 - src/Hooks.cpp | 1 - src/TDB2.cpp | 3 --- src/TDB2.h | 1 - src/TF2.cpp | 8 -------- src/TF2.h | 3 --- src/Task.h | 1 - src/Variant.h | 1 - src/dependency.cpp | 2 -- src/feedback.cpp | 2 -- src/legacy.cpp | 1 - src/main.h | 1 - src/nag.cpp | 6 ------ src/recur.cpp | 7 ------- src/rules.cpp | 1 - src/util.cpp | 7 ------- src/util.h | 1 - 20 files changed, 53 deletions(-) diff --git a/src/Context.cpp b/src/Context.cpp index 611cbdf4c..ab5a8910c 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -40,12 +40,10 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -55,7 +53,6 @@ #include #endif -#include #include #ifdef SOLARIS diff --git a/src/Eval.cpp b/src/Eval.cpp index 5b869061e..7d82224ab 100644 --- a/src/Eval.cpp +++ b/src/Eval.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include diff --git a/src/Filter.cpp b/src/Filter.cpp index 0de77e88d..2e6d20a00 100644 --- a/src/Filter.cpp +++ b/src/Filter.cpp @@ -36,8 +36,6 @@ #include #include -#include - //////////////////////////////////////////////////////////////////////////////// // Take an input set of tasks and filter into a subset. void Filter::subset(const std::vector& input, std::vector& output) { diff --git a/src/Filter.h b/src/Filter.h index 2c47da6c4..bb7449318 100644 --- a/src/Filter.h +++ b/src/Filter.h @@ -30,7 +30,6 @@ #include #include -#include #include class Filter { diff --git a/src/Hooks.cpp b/src/Hooks.cpp index a8ad76b82..a703b2539 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 227e22965..08db87ddc 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -35,14 +35,11 @@ #include #include #include -#include #include #include #include #include -#include -#include #include #include diff --git a/src/TDB2.h b/src/TDB2.h index 1720cf9ba..89b5ae8e8 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -29,7 +29,6 @@ #include #include -#include #include #include diff --git a/src/TF2.cpp b/src/TF2.cpp index 9b7e2f940..6ab74c24d 100644 --- a/src/TF2.cpp +++ b/src/TF2.cpp @@ -33,16 +33,8 @@ #include #include #include -#include -#include #include -#include -#include -#include -#include -#include - #define STRING_TDB2_REVERTED "Modified task reverted." //////////////////////////////////////////////////////////////////////////////// diff --git a/src/TF2.h b/src/TF2.h index fbec1f12b..14f0f2485 100644 --- a/src/TF2.h +++ b/src/TF2.h @@ -29,12 +29,9 @@ #include #include -#include #include #include -#include -#include #include // TF2 Class represents a single 2.x-style file in the task database. diff --git a/src/Task.h b/src/Task.h index 66e18b13b..b430a2bc2 100644 --- a/src/Task.h +++ b/src/Task.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include diff --git a/src/Variant.h b/src/Variant.h index 3cbc5c6af..735779136 100644 --- a/src/Variant.h +++ b/src/Variant.h @@ -30,7 +30,6 @@ #include #include -#include #include class Variant { diff --git a/src/dependency.cpp b/src/dependency.cpp index 28b592470..8ac462aff 100644 --- a/src/dependency.cpp +++ b/src/dependency.cpp @@ -32,9 +32,7 @@ #include #include -#include #include -#include #include #define STRING_DEPEND_BLOCKED "Task {1} is blocked by:" diff --git a/src/feedback.cpp b/src/feedback.cpp index 22b9f9dc5..0fc7b84fc 100644 --- a/src/feedback.cpp +++ b/src/feedback.cpp @@ -32,12 +32,10 @@ #include #include #include -#include #include #include #include -#include #include #include #include diff --git a/src/legacy.cpp b/src/legacy.cpp index 16b1f3292..a14be7f2f 100644 --- a/src/legacy.cpp +++ b/src/legacy.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #define STRING_LEGACY_PRIORITY "Legacy attribute found. Please change '{1}' to '{2}'." diff --git a/src/main.h b/src/main.h index 9220a1345..c08f159a7 100644 --- a/src/main.h +++ b/src/main.h @@ -32,7 +32,6 @@ #include #include -#include #include #include #include diff --git a/src/nag.cpp b/src/nag.cpp index 057567582..356287f45 100644 --- a/src/nag.cpp +++ b/src/nag.cpp @@ -25,15 +25,9 @@ //////////////////////////////////////////////////////////////////////////////// #include - -#include // cmake.h include header must come first - #include -#include -#include -#include #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/recur.cpp b/src/recur.cpp index 4168b6691..857273926 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -32,24 +32,17 @@ #include #include #include -#include #include #include -#include #include -#include #include #include #include #include #include -#include -#include -#include #include #include -#include // Add a `time_t` delta to a Datetime, checking for and returning nullopt on integer overflow. std::optional checked_add_datetime(Datetime& base, time_t delta) { diff --git a/src/rules.cpp b/src/rules.cpp index 924644233..6843db465 100644 --- a/src/rules.cpp +++ b/src/rules.cpp @@ -31,7 +31,6 @@ #include #include #include -#include static std::map gsColor; static std::vector gsPrecedence; diff --git a/src/util.cpp b/src/util.cpp index 04593791f..78174028b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -38,10 +38,7 @@ #include #include #include -#include -#include #include -#include #include #include #include @@ -51,11 +48,7 @@ #include #include -#include -#include -#include #include -#include #include #include diff --git a/src/util.h b/src/util.h index a4f94a1a9..782311117 100644 --- a/src/util.h +++ b/src/util.h @@ -32,7 +32,6 @@ #include -#include #include #include #if defined(FREEBSD) || defined(OPENBSD) From 81ca04fc8cbab221363f5b0553725b796b96f23f Mon Sep 17 00:00:00 2001 From: Tobias Predel Date: Sat, 1 Mar 2025 20:47:42 +0100 Subject: [PATCH 190/242] Declare in corresponding header files and dissolve main.h (#3796) * Declare in corresponding header files and dissolve main.h Apply include-what-you-use * Remove further unncessary includes * Incorporate review comment * Do not declare static functions and variables in header * Adapt test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/Context.cpp | 3 +- src/Operation.h | 7 +-- src/TDB2.cpp | 2 - src/TDB2.h | 1 - src/TF2.cpp | 4 +- src/Task.cpp | 3 +- src/Variant.h | 1 - src/ViewTask.cpp | 2 +- src/columns/ColDepends.cpp | 3 -- src/columns/ColTags.cpp | 2 +- src/commands/CmdAdd.cpp | 2 +- src/commands/CmdAnnotate.cpp | 2 +- src/commands/CmdAppend.cpp | 2 +- src/commands/CmdBurndown.cpp | 1 - src/commands/CmdCalendar.cpp | 1 - src/commands/CmdColor.cpp | 1 - src/commands/CmdColumns.cpp | 1 - src/commands/CmdContext.cpp | 2 +- src/commands/CmdCount.cpp | 1 - src/commands/CmdCustom.cpp | 6 +-- src/commands/CmdDelete.cpp | 4 +- src/commands/CmdDenotate.cpp | 2 +- src/commands/CmdDone.cpp | 5 +- src/commands/CmdDuplicate.cpp | 2 +- src/commands/CmdEdit.cpp | 1 - src/commands/CmdExport.cpp | 3 +- src/commands/CmdGet.cpp | 1 - src/commands/CmdHistory.cpp | 1 - src/commands/CmdIDs.cpp | 1 - src/commands/CmdInfo.cpp | 4 +- src/commands/CmdLog.cpp | 2 +- src/commands/CmdModify.cpp | 3 +- src/commands/CmdNews.cpp | 1 - src/commands/CmdPrepend.cpp | 2 +- src/commands/CmdProjects.cpp | 3 +- src/commands/CmdPurge.cpp | 2 +- src/commands/CmdShow.cpp | 2 +- src/commands/CmdStart.cpp | 5 +- src/commands/CmdStats.cpp | 1 - src/commands/CmdStop.cpp | 4 +- src/commands/CmdSummary.cpp | 3 +- src/commands/CmdTimesheet.cpp | 3 +- src/commands/CmdUDAs.cpp | 1 - src/commands/CmdUndo.cpp | 3 +- src/commands/CmdUrgency.cpp | 2 - src/commands/Command.cpp | 2 - src/dependency.cpp | 2 +- src/dependency.h | 43 ++++++++++++++++ src/feedback.cpp | 2 +- src/feedback.h | 54 ++++++++++++++++++++ src/legacy.h | 44 +++++++++++++++++ src/main.h | 93 ----------------------------------- src/nag.h | 43 ++++++++++++++++ src/recur.cpp | 3 +- src/recur.h | 57 +++++++++++++++++++++ src/rules.cpp | 2 +- src/rules.h | 47 ++++++++++++++++++ src/sort.h | 51 +++++++++++++++++++ src/util.cpp | 2 +- test/col_test.cpp | 2 - test/make_tc_task.cpp | 4 -- test/t_test.cpp | 3 +- test/tdb2_test.cpp | 4 +- test/tw_2689_test.cpp | 4 +- test/util_test.cpp | 5 +- test/view_test.cpp | 3 +- 66 files changed, 402 insertions(+), 176 deletions(-) create mode 100644 src/dependency.h create mode 100644 src/feedback.h create mode 100644 src/legacy.h delete mode 100644 src/main.h create mode 100644 src/nag.h create mode 100644 src/recur.h create mode 100644 src/rules.h create mode 100644 src/sort.h diff --git a/src/Context.cpp b/src/Context.cpp index ab5a8910c..f90dd984e 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -36,7 +36,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/src/Operation.h b/src/Operation.h index 485b422bd..82c2ea022 100644 --- a/src/Operation.h +++ b/src/Operation.h @@ -1,6 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // -// Copyright 2006 - 2024, Tomas Babej, Paul Beckingham, Federico Hernandez. +// Copyright 2006 - 2025, Tomas Babej, Paul Beckingham, Federico Hernandez, +// Tobias Predel. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -24,8 +25,8 @@ // //////////////////////////////////////////////////////////////////////////////// -#ifndef INCLUDED_OPERATIOn -#define INCLUDED_OPERATIOn +#ifndef INCLUDED_OPERATION +#define INCLUDED_OPERATION #include diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 08db87ddc..c49f513d6 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -33,13 +33,11 @@ #include #include #include -#include #include #include #include #include -#include #include #include diff --git a/src/TDB2.h b/src/TDB2.h index 89b5ae8e8..51669ab6d 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -27,7 +27,6 @@ #ifndef INCLUDED_TDB2 #define INCLUDED_TDB2 -#include #include #include diff --git a/src/TF2.cpp b/src/TF2.cpp index 6ab74c24d..0b78fe531 100644 --- a/src/TF2.cpp +++ b/src/TF2.cpp @@ -31,7 +31,9 @@ #include #include #include -#include +#ifdef PRODUCT_TASKWARRIOR +#include +#endif #include #include diff --git a/src/Task.cpp b/src/Task.cpp index a41f4d5ae..536f17a24 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -58,7 +58,8 @@ #include #include #include -#include +#include +#include #define APPROACHING_INFINITY 1000 // Close enough. This isn't rocket surgery. diff --git a/src/Variant.h b/src/Variant.h index 735779136..5634473b9 100644 --- a/src/Variant.h +++ b/src/Variant.h @@ -28,7 +28,6 @@ #define INCLUDED_VARIANT #include -#include #include diff --git a/src/ViewTask.cpp b/src/ViewTask.cpp index ce4cbaa2a..ad04e9811 100644 --- a/src/ViewTask.cpp +++ b/src/ViewTask.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/columns/ColDepends.cpp b/src/columns/ColDepends.cpp index d0c77d0b3..446737d6d 100644 --- a/src/columns/ColDepends.cpp +++ b/src/columns/ColDepends.cpp @@ -30,13 +30,10 @@ #include #include #include -#include #include -#include #include #include -#include #include #define STRING_COLUMN_LABEL_DEP "Depends" diff --git a/src/columns/ColTags.cpp b/src/columns/ColTags.cpp index 50ccadf9e..2d0f278c0 100644 --- a/src/columns/ColTags.cpp +++ b/src/columns/ColTags.cpp @@ -32,8 +32,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/src/commands/CmdAdd.cpp b/src/commands/CmdAdd.cpp index 429ac9480..f951083dc 100644 --- a/src/commands/CmdAdd.cpp +++ b/src/commands/CmdAdd.cpp @@ -29,8 +29,8 @@ #include #include +#include #include -#include //////////////////////////////////////////////////////////////////////////////// CmdAdd::CmdAdd() { diff --git a/src/commands/CmdAnnotate.cpp b/src/commands/CmdAnnotate.cpp index f5a193236..efee759ea 100644 --- a/src/commands/CmdAnnotate.cpp +++ b/src/commands/CmdAnnotate.cpp @@ -30,8 +30,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/src/commands/CmdAppend.cpp b/src/commands/CmdAppend.cpp index ac9e35ae9..0cdad990b 100644 --- a/src/commands/CmdAppend.cpp +++ b/src/commands/CmdAppend.cpp @@ -30,8 +30,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/src/commands/CmdBurndown.cpp b/src/commands/CmdBurndown.cpp index b05af87f3..b0045cab8 100644 --- a/src/commands/CmdBurndown.cpp +++ b/src/commands/CmdBurndown.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/src/commands/CmdCalendar.cpp b/src/commands/CmdCalendar.cpp index bf365d262..c3b021298 100644 --- a/src/commands/CmdCalendar.cpp +++ b/src/commands/CmdCalendar.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/src/commands/CmdColor.cpp b/src/commands/CmdColor.cpp index 53e672df6..da17b9436 100644 --- a/src/commands/CmdColor.cpp +++ b/src/commands/CmdColor.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include diff --git a/src/commands/CmdColumns.cpp b/src/commands/CmdColumns.cpp index bf5e00f24..4a56e5d0f 100644 --- a/src/commands/CmdColumns.cpp +++ b/src/commands/CmdColumns.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 77bc3152d..37c89682f 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/commands/CmdCount.cpp b/src/commands/CmdCount.cpp index 3c158c225..e25af8fde 100644 --- a/src/commands/CmdCount.cpp +++ b/src/commands/CmdCount.cpp @@ -30,7 +30,6 @@ #include #include #include -#include //////////////////////////////////////////////////////////////////////////////// CmdCount::CmdCount() { diff --git a/src/commands/CmdCustom.cpp b/src/commands/CmdCustom.cpp index 9babfe668..8bba6c902 100644 --- a/src/commands/CmdCustom.cpp +++ b/src/commands/CmdCustom.cpp @@ -34,15 +34,15 @@ #include #include #include +#include #include -#include +#include #include -#include +#include #include #include #include -#include #include #include diff --git a/src/commands/CmdDelete.cpp b/src/commands/CmdDelete.cpp index b3ac931cb..35b5c7ecf 100644 --- a/src/commands/CmdDelete.cpp +++ b/src/commands/CmdDelete.cpp @@ -30,8 +30,10 @@ #include #include #include +#include +#include #include -#include +#include #include #include diff --git a/src/commands/CmdDenotate.cpp b/src/commands/CmdDenotate.cpp index e8a43ad63..7293f8ace 100644 --- a/src/commands/CmdDenotate.cpp +++ b/src/commands/CmdDenotate.cpp @@ -30,8 +30,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/src/commands/CmdDone.cpp b/src/commands/CmdDone.cpp index d1ea64adb..deb10a0dc 100644 --- a/src/commands/CmdDone.cpp +++ b/src/commands/CmdDone.cpp @@ -30,8 +30,11 @@ #include #include #include +#include +#include #include -#include +#include +#include #include #include diff --git a/src/commands/CmdDuplicate.cpp b/src/commands/CmdDuplicate.cpp index b6a68854d..6ef1feb5d 100644 --- a/src/commands/CmdDuplicate.cpp +++ b/src/commands/CmdDuplicate.cpp @@ -30,8 +30,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/src/commands/CmdEdit.cpp b/src/commands/CmdEdit.cpp index 92dcbc828..11cd8d937 100644 --- a/src/commands/CmdEdit.cpp +++ b/src/commands/CmdEdit.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/src/commands/CmdExport.cpp b/src/commands/CmdExport.cpp index c697ff221..2924645dd 100644 --- a/src/commands/CmdExport.cpp +++ b/src/commands/CmdExport.cpp @@ -31,8 +31,9 @@ #include #include #include -#include +#include #include +#include //////////////////////////////////////////////////////////////////////////////// CmdExport::CmdExport() { diff --git a/src/commands/CmdGet.cpp b/src/commands/CmdGet.cpp index 2954e6848..ec7c8da0a 100644 --- a/src/commands/CmdGet.cpp +++ b/src/commands/CmdGet.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdHistory.cpp b/src/commands/CmdHistory.cpp index 73788b01a..d379a89dd 100644 --- a/src/commands/CmdHistory.cpp +++ b/src/commands/CmdHistory.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include diff --git a/src/commands/CmdIDs.cpp b/src/commands/CmdIDs.cpp index 5782aaf63..aa6bc6921 100644 --- a/src/commands/CmdIDs.cpp +++ b/src/commands/CmdIDs.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include diff --git a/src/commands/CmdInfo.cpp b/src/commands/CmdInfo.cpp index b805277b9..b3b3191fc 100644 --- a/src/commands/CmdInfo.cpp +++ b/src/commands/CmdInfo.cpp @@ -34,9 +34,9 @@ #include #include #include +#include #include -#include -#include +#include #include #include #include diff --git a/src/commands/CmdLog.cpp b/src/commands/CmdLog.cpp index 6263cf299..489001e0c 100644 --- a/src/commands/CmdLog.cpp +++ b/src/commands/CmdLog.cpp @@ -29,8 +29,8 @@ #include #include +#include #include -#include //////////////////////////////////////////////////////////////////////////////// CmdLog::CmdLog() { diff --git a/src/commands/CmdModify.cpp b/src/commands/CmdModify.cpp index 8a92fb802..a962ea10b 100644 --- a/src/commands/CmdModify.cpp +++ b/src/commands/CmdModify.cpp @@ -30,8 +30,9 @@ #include #include #include +#include #include -#include +#include #include #include diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index 6eeec1ba6..cbda073d5 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include diff --git a/src/commands/CmdPrepend.cpp b/src/commands/CmdPrepend.cpp index 1f148db48..afdf73025 100644 --- a/src/commands/CmdPrepend.cpp +++ b/src/commands/CmdPrepend.cpp @@ -30,8 +30,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/src/commands/CmdProjects.cpp b/src/commands/CmdProjects.cpp index 612ba93ed..a74308757 100644 --- a/src/commands/CmdProjects.cpp +++ b/src/commands/CmdProjects.cpp @@ -32,10 +32,9 @@ #include #include #include -#include +#include #include -#include #include #include diff --git a/src/commands/CmdPurge.cpp b/src/commands/CmdPurge.cpp index e7374064e..d5b3c89fe 100644 --- a/src/commands/CmdPurge.cpp +++ b/src/commands/CmdPurge.cpp @@ -30,8 +30,8 @@ #include #include #include +#include #include -#include #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index 60e4f1657..7d839e060 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/commands/CmdStart.cpp b/src/commands/CmdStart.cpp index 5216fab88..057b61fc5 100644 --- a/src/commands/CmdStart.cpp +++ b/src/commands/CmdStart.cpp @@ -30,8 +30,11 @@ #include #include #include +#include +#include #include -#include +#include +#include #include #include diff --git a/src/commands/CmdStats.cpp b/src/commands/CmdStats.cpp index 7639594d9..8b5860ea1 100644 --- a/src/commands/CmdStats.cpp +++ b/src/commands/CmdStats.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include diff --git a/src/commands/CmdStop.cpp b/src/commands/CmdStop.cpp index 1c2bf7596..9924c5a58 100644 --- a/src/commands/CmdStop.cpp +++ b/src/commands/CmdStop.cpp @@ -30,8 +30,10 @@ #include #include #include +#include +#include #include -#include +#include #include diff --git a/src/commands/CmdSummary.cpp b/src/commands/CmdSummary.cpp index 6bea3aae1..ca102638e 100644 --- a/src/commands/CmdSummary.cpp +++ b/src/commands/CmdSummary.cpp @@ -33,11 +33,10 @@ #include #include #include -#include +#include #include #include -#include #include #include diff --git a/src/commands/CmdTimesheet.cpp b/src/commands/CmdTimesheet.cpp index 13b8d13c1..9ce0bd4e8 100644 --- a/src/commands/CmdTimesheet.cpp +++ b/src/commands/CmdTimesheet.cpp @@ -33,8 +33,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/src/commands/CmdUDAs.cpp b/src/commands/CmdUDAs.cpp index 78742ca0a..c2fb751a9 100644 --- a/src/commands/CmdUDAs.cpp +++ b/src/commands/CmdUDAs.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include diff --git a/src/commands/CmdUndo.cpp b/src/commands/CmdUndo.cpp index cf808f6a5..4c2e6b7f7 100644 --- a/src/commands/CmdUndo.cpp +++ b/src/commands/CmdUndo.cpp @@ -31,12 +31,11 @@ #include #include #include +#include #include #include -#include "shared.h" - //////////////////////////////////////////////////////////////////////////////// CmdUndo::CmdUndo() { _keyword = "undo"; diff --git a/src/commands/CmdUrgency.cpp b/src/commands/CmdUrgency.cpp index cbd1b45e3..40731c81c 100644 --- a/src/commands/CmdUrgency.cpp +++ b/src/commands/CmdUrgency.cpp @@ -32,8 +32,6 @@ #include #include #include -#include -#include #include diff --git a/src/commands/Command.cpp b/src/commands/Command.cpp index 366a10666..3e52a4389 100644 --- a/src/commands/Command.cpp +++ b/src/commands/Command.cpp @@ -50,9 +50,7 @@ #include #include #include -#include #include -#include #include #include diff --git a/src/dependency.cpp b/src/dependency.cpp index 8ac462aff..380c471cb 100644 --- a/src/dependency.cpp +++ b/src/dependency.cpp @@ -28,8 +28,8 @@ // cmake.h include header must come first #include +#include #include -#include #include #include diff --git a/src/dependency.h b/src/dependency.h new file mode 100644 index 000000000..66cfbd7a2 --- /dev/null +++ b/src/dependency.h @@ -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 include header must come first + +#include + +#define STRING_DEPEND_BLOCKED "Task {1} is blocked by:" +bool dependencyIsCircular(const Task& task); +void dependencyChainOnComplete(Task& task); +void dependencyChainOnStart(Task& task); + +#endif + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/feedback.cpp b/src/feedback.cpp index 0fc7b84fc..efd2302b4 100644 --- a/src/feedback.cpp +++ b/src/feedback.cpp @@ -31,8 +31,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/src/feedback.h b/src/feedback.h new file mode 100644 index 000000000..55b565c00 --- /dev/null +++ b/src/feedback.h @@ -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 include header must come first + +#include + +#include +#include + +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 + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/legacy.h b/src/legacy.h new file mode 100644 index 000000000..9e3258079 --- /dev/null +++ b/src/legacy.h @@ -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 include header must come first + +#include + +void legacyColumnMap(std::string& name); +void legacySortColumnMap(std::string& name); +std::string legacyCheckForDeprecatedVariables(); +std::string legacyCheckForDeprecatedColumns(); +void legacyAttributeMap(std::string& name); + +#endif + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/main.h b/src/main.h deleted file mode 100644 index c08f159a7..000000000 --- a/src/main.h +++ /dev/null @@ -1,93 +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 -#include -#include -#include - -#include -#include -#include -#include -#include - -// recur.cpp -void handleRecurrence(); -void handleUntil(); -std::optional checked_add_datetime(Datetime& base, time_t delta); -std::optional getNextRecurrence(Datetime&, std::string&); -bool generateDueDates(Task&, std::vector&); -void updateRecurrenceMask(Task&); - -// nag.cpp -void nag(std::vector&); - -// 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&, std::vector&, const std::string&); -void sort_projects(std::list>& sorted, - std::map& allProjects); -void sort_projects(std::list>& sorted, - std::map& allProjects); - -// legacy.cpp -void legacyColumnMap(std::string&); -void legacySortColumnMap(std::string&); -std::string legacyCheckForDeprecatedVariables(); -std::string legacyCheckForDeprecatedColumns(); -void legacyAttributeMap(std::string&); - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/nag.h b/src/nag.h new file mode 100644 index 000000000..6a8d2a1b5 --- /dev/null +++ b/src/nag.h @@ -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 include header must come first +#include + +#include + +//////////////////////////////////////////////////////////////////////////////// +// Generates a nag message when there are READY tasks of a higher urgency. +void nag(std::vector& tasks); + +#endif + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/recur.cpp b/src/recur.cpp index 857273926..482fc1060 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -31,9 +31,10 @@ #include #include #include +#include #include -#include #include +#include #include #include #include diff --git a/src/recur.h b/src/recur.h new file mode 100644 index 000000000..db0b6306d --- /dev/null +++ b/src/recur.h @@ -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 include header must come first + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +std::optional checked_add_datetime(Datetime& base, time_t delta); +void handleRecurrence(); +bool generateDueDates(Task& parent, std::vector& allDue); +std::optional getNextRecurrence(Datetime& current, std::string& period); +void updateRecurrenceMask(Task& task); +void handleUntil(); + +#endif + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/rules.cpp b/src/rules.cpp index 6843db465..21c46307d 100644 --- a/src/rules.cpp +++ b/src/rules.cpp @@ -29,7 +29,7 @@ #include #include -#include +#include #include static std::map gsColor; diff --git a/src/rules.h b/src/rules.h new file mode 100644 index 000000000..d0d8c7f85 --- /dev/null +++ b/src/rules.h @@ -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 include header must come first + +#include +#include +#include + +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 + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/sort.h b/src/sort.h new file mode 100644 index 000000000..6972f9c2e --- /dev/null +++ b/src/sort.h @@ -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 include header must come first + +#include + +#include +#include +#include +#include + +void sort_tasks(std::vector& data, std::vector& order, const std::string& keys); + +void sort_projects(std::list>& sorted, + std::map& allProjects); + +void sort_projects(std::list>& sorted, + std::map& allProjects); + +#endif + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/util.cpp b/src/util.cpp index 78174028b..ebbbf2191 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -34,8 +34,8 @@ #ifdef FREEBSD #define _WITH_GETLINE #endif +#include #include -#include #include #include #include diff --git a/test/col_test.cpp b/test/col_test.cpp index d2ffd378c..d6a8f5e1d 100644 --- a/test/col_test.cpp +++ b/test/col_test.cpp @@ -28,8 +28,6 @@ // cmake.h include header must come first #include -#include -#include #include //////////////////////////////////////////////////////////////////////////////// diff --git a/test/make_tc_task.cpp b/test/make_tc_task.cpp index b14560a8e..37f1d963f 100644 --- a/test/make_tc_task.cpp +++ b/test/make_tc_task.cpp @@ -28,16 +28,12 @@ // cmake.h include header must come first #include -#include #include #include #include #include #include -#include - -#include "format.h" namespace { diff --git a/test/t_test.cpp b/test/t_test.cpp index a7063b19f..f953914fd 100644 --- a/test/t_test.cpp +++ b/test/t_test.cpp @@ -27,10 +27,11 @@ #include // cmake.h include header must come first -#include #include #include +#include "Context.h" + //////////////////////////////////////////////////////////////////////////////// int TEST_NAME(int, char**) { UnitTest test(48); diff --git a/test/tdb2_test.cpp b/test/tdb2_test.cpp index 76a27cc33..f1baf2444 100644 --- a/test/tdb2_test.cpp +++ b/test/tdb2_test.cpp @@ -27,12 +27,10 @@ #include // cmake.h include header must come first -#include -#include #include #include -#include +#include "Context.h" namespace { diff --git a/test/tw_2689_test.cpp b/test/tw_2689_test.cpp index cbbad40f1..81610eeba 100644 --- a/test/tw_2689_test.cpp +++ b/test/tw_2689_test.cpp @@ -26,11 +26,9 @@ #include -#include // cmake.h include header must come first -#include -#include +#include #include //////////////////////////////////////////////////////////////////////////////// diff --git a/test/util_test.cpp b/test/util_test.cpp index 8e52a723a..43a9c9bcd 100644 --- a/test/util_test.cpp +++ b/test/util_test.cpp @@ -27,12 +27,11 @@ #include // cmake.h include header must come first -#include -#include +#include +#include #include #include -#include #include //////////////////////////////////////////////////////////////////////////////// diff --git a/test/view_test.cpp b/test/view_test.cpp index 51423e4db..09b0c774c 100644 --- a/test/view_test.cpp +++ b/test/view_test.cpp @@ -31,9 +31,8 @@ #include #include #include -#include +#include #include -#include #include #include From a3e0dada30eca94e550b742248fdcf3b3291fa19 Mon Sep 17 00:00:00 2001 From: Tobias Predel Date: Sun, 2 Mar 2025 17:03:18 +0100 Subject: [PATCH 191/242] Remove unused includes (#3798) --- src/commands/CmdImportV2.h | 1 - src/commands/CmdSync.cpp | 3 --- src/commands/CmdTags.cpp | 1 - src/commands/CmdVersion.cpp | 1 - src/commands/Command.h | 1 - 5 files changed, 7 deletions(-) diff --git a/src/commands/CmdImportV2.h b/src/commands/CmdImportV2.h index 049d66d0e..5cfddd63d 100644 --- a/src/commands/CmdImportV2.h +++ b/src/commands/CmdImportV2.h @@ -31,7 +31,6 @@ #include #include -#include class CmdImportV2 : public Command { public: diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index 427cca5dd..c0fa93c36 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -32,13 +32,10 @@ #include #include #include -#include #include -#include #include #include -#include #include //////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/CmdTags.cpp b/src/commands/CmdTags.cpp index 4c79832ad..ee4b6b018 100644 --- a/src/commands/CmdTags.cpp +++ b/src/commands/CmdTags.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include diff --git a/src/commands/CmdVersion.cpp b/src/commands/CmdVersion.cpp index e4dd57e10..21bce985a 100644 --- a/src/commands/CmdVersion.cpp +++ b/src/commands/CmdVersion.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #ifdef HAVE_COMMIT diff --git a/src/commands/Command.h b/src/commands/Command.h index 02ff51ce6..6292160be 100644 --- a/src/commands/Command.h +++ b/src/commands/Command.h @@ -31,7 +31,6 @@ #include #include -#include class Command { public: From 3bb71390a9db32a5399b11a99cc018019d40a2c1 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 3 Mar 2025 11:43:20 -0500 Subject: [PATCH 192/242] Ignore "target" in creating `source_package` (#3799) Cargo creates this directory if run directly, but it shouldn't be in the release tarball. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a6cbadc3f..3e34703f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,6 +162,6 @@ endforeach (doc_FILE) set (CPACK_SOURCE_GENERATOR "TGZ") set (CPACK_SOURCE_PACKAGE_FILE_NAME ${PACKAGE_NAME}-${PACKAGE_VERSION}) -set (CPACK_SOURCE_IGNORE_FILES "build" "test" "misc/*" "performance" "swp$" "src/lex$" "task-.*.tar.gz" +set (CPACK_SOURCE_IGNORE_FILES "build" "target" "test" "misc/*" "performance" "swp$" "src/lex$" "task-.*.tar.gz" "commit.h" "cmake.h$" "\\\\.gitmodules" "src/libshared/\\\\.git" ".github/" ".*\\\\.gitignore$" "docker-compose.yml" "\\\\.git/") include (CPack) From 1567ea5c060aafba6a5bfb61b42a6a285f88dd75 Mon Sep 17 00:00:00 2001 From: Tobias Predel Date: Mon, 3 Mar 2025 18:14:39 +0100 Subject: [PATCH 193/242] Remove header files out of CMakeLists (#3797) It does not make any difference. --- src/CMakeLists.txt | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 45b027c4a..5905696d2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,20 +6,20 @@ include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src/libshared/src ${TASK_INCLUDE_DIRS}) -add_library (task STATIC CLI2.cpp CLI2.h - Context.cpp Context.h - DOM.cpp DOM.h - Eval.cpp Eval.h - Filter.cpp Filter.h - Hooks.cpp Hooks.h - Lexer.cpp Lexer.h - Operation.cpp Operation.h - TF2.cpp TF2.h - TDB2.cpp TDB2.h - Task.cpp Task.h - Variant.cpp Variant.h - Version.cpp Version.h - ViewTask.cpp ViewTask.h +add_library (task STATIC CLI2.cpp + Context.cpp + DOM.cpp + Eval.cpp + Filter.cpp + Hooks.cpp + Lexer.cpp + Operation.cpp + TF2.cpp + TDB2.cpp + Task.cpp + Variant.cpp + Version.cpp + ViewTask.cpp dependency.cpp feedback.cpp legacy.cpp @@ -27,7 +27,7 @@ add_library (task STATIC CLI2.cpp CLI2.h recur.cpp rules.cpp sort.cpp - util.cpp util.h) + util.cpp) target_link_libraries(task taskchampion-cpp) add_library (libshared STATIC libshared/src/Color.cpp libshared/src/Color.h From bcb3f820ab7b1e3381c62642fc92b1ebea05d94f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:30:31 -0500 Subject: [PATCH 194/242] Bump docker/build-push-action from 6.14.0 to 6.15.0 (#3800) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.14.0 to 6.15.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.14.0...v6.15.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 7c4ea0733..a0414c233 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -44,7 +44,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.14.0 + uses: docker/build-push-action@v6.15.0 with: context: . file: "./docker/task.dockerfile" From 3c12c0dfd080212a5dac82db1a14ffa8a84cccdd Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 6 Mar 2025 06:59:04 -0500 Subject: [PATCH 195/242] Fix errors finding Rust toolchain (#3802) * switch to dtolnay/toolchain * Add .dockerignore to avoid copying target/ into images * update corrosion --- .dockerignore | 1 + .github/workflows/checks.yml | 46 +++++++++++++--------------- .github/workflows/release-check.yaml | 10 +++--- .github/workflows/tests.yaml | 45 ++++++++++++--------------- src/taskchampion-cpp/corrosion | 2 +- 5 files changed, 48 insertions(+), 56 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..eb5a316cb --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +target diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 78d5c74ee..c99fae36a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -15,25 +15,25 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo build - uses: actions/cache@v4 - with: - path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master + id: toolchain with: # If this version is old enough to cause errors, or older than the # TaskChampion MSRV, bump it to the MSRV of the currently-required # TaskChampion package; if necessary, bump that version as well. toolchain: "1.81.0" # MSRV - override: true + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo build + uses: actions/cache@v4 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} - uses: actions-rs/cargo@v1.0.3 with: @@ -53,12 +53,11 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master + id: toolchain with: - profile: minimal - components: rustfmt - toolchain: stable - override: true + toolchain: "stable" + components: "rustfmt" - uses: actions-rs/cargo@v1.0.3 with: @@ -71,12 +70,11 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master + id: toolchain with: - profile: minimal - components: rustfmt - toolchain: stable - override: true + toolchain: "stable" + components: "rustfmt" - name: "Check metadata" run: ".github/workflows/metadata-check.sh" diff --git a/.github/workflows/release-check.yaml b/.github/workflows/release-check.yaml index 8138356a7..57cfa0dc7 100644 --- a/.github/workflows/release-check.yaml +++ b/.github/workflows/release-check.yaml @@ -6,16 +6,14 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + - name: Cache cargo registry uses: actions/cache@v4 with: path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - - uses: actions-rs/toolchain@v1 - with: - toolchain: "stable" - override: true + key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} - name: Install uuid-dev run: sudo apt install uuid-dev diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 78c6b74dc..bdc3347ee 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -43,22 +43,20 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + - name: Cache cargo registry uses: actions/cache@v4 with: path: ~/.cargo/registry - key: ${{ runner.os }}-stable-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} - name: Cache cargo build uses: actions/cache@v4 with: path: target - key: ${{ runner.os }}-stable-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - uses: actions-rs/toolchain@v1 - with: - toolchain: "stable" - override: true + key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} - name: Test MacOS run: bash test/scripts/test_macos.sh @@ -72,22 +70,20 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + - name: Cache cargo registry uses: actions/cache@v4 with: path: ~/.cargo/registry - key: ${{ runner.os }}-stable-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} - name: Cache cargo build uses: actions/cache@v4 with: path: target - key: ${{ runner.os }}-stable-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - uses: actions-rs/toolchain@v1 - with: - toolchain: "stable" - override: true + key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} - name: Test MacOS run: bash test/scripts/test_macos.sh @@ -101,26 +97,25 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + id: toolchain + with: + # If this version is old enough to cause errors, or older than the + # TaskChampion MSRV, bump it to the MSRV of the currently-required + # TaskChampion package; if necessary, bump that version as well. + toolchain: "1.81.0" # MSRV + - name: Cache cargo registry uses: actions/cache@v4 with: path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-cargo-registry-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} - name: Cache cargo build uses: actions/cache@v4 with: path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - uses: actions-rs/toolchain@v1 - with: - # If this version is old enough to cause errors, or older than the - # TaskChampion MSRV, bump it to the MSRV of the currently-required - # TaskChampion package; if necessary, bump that version as well. - # This should match the MSRV in `src/taskchampion-cpp/Cargo.toml`. - toolchain: "1.81.0" # MSRV - override: true + key: ${{ runner.os }}-cargo-build-target-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} - uses: actions-rs/cargo@v1.0.3 with: diff --git a/src/taskchampion-cpp/corrosion b/src/taskchampion-cpp/corrosion index 64289b1d7..fcd8b4198 160000 --- a/src/taskchampion-cpp/corrosion +++ b/src/taskchampion-cpp/corrosion @@ -1 +1 @@ -Subproject commit 64289b1d79d6d19cd2e241db515381a086bb8407 +Subproject commit fcd8b41981cb1e80f4dcc20fa8970dc6aa981c9f From 5ec0f4ebc03c222503b55d8e136e097705abe502 Mon Sep 17 00:00:00 2001 From: Yong Li Date: Thu, 6 Mar 2025 12:33:37 +0000 Subject: [PATCH 196/242] Update task.1.in (#3804) Clearly this is an error. The example command should use foo. --- doc/man/task.1.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/man/task.1.in b/doc/man/task.1.in index 52bbcaab5..c779b2075 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -1037,7 +1037,7 @@ modifier requires that the attribute contain the whole word specified, such that this: .nf - task description.word:bar list + task description.word:foo list .fi Will match the description 'foo bar baz' but does not match 'dog food'. From 022650dbffe04221d3d33f43570920be7a067523 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 6 Mar 2025 07:43:41 -0500 Subject: [PATCH 197/242] Make updates after releasing (#3803) * add symlinks for ease of access * Update things after making a release, to maximize testing time --- CONTRIBUTING.md | 1 + RELEASING.md | 1 + doc/devel/contrib/releasing.md | 3 +++ 3 files changed, 5 insertions(+) create mode 120000 CONTRIBUTING.md create mode 120000 RELEASING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 120000 index 000000000..a5e92ccc7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +doc/devel/contrib/development.md \ No newline at end of file diff --git a/RELEASING.md b/RELEASING.md new file mode 120000 index 000000000..51170bf7a --- /dev/null +++ b/RELEASING.md @@ -0,0 +1 @@ +doc/devel/contrib/releasing.md \ No newline at end of file diff --git a/doc/devel/contrib/releasing.md b/doc/devel/contrib/releasing.md index d8b4fa125..ffaf1ea67 100644 --- a/doc/devel/contrib/releasing.md +++ b/doc/devel/contrib/releasing.md @@ -24,3 +24,6 @@ To release Taskwarrior, follow this process: - Add a new item in `content/news` - Update `data/projects.json` with the latest version and a fake next version for "devel" - Update `data/releases.json` with the new version, and copy the tarball into `content/download`. +- Update various things, in a new PR: + - `cargo update` + - `git submodule update --remote --merge` From 74276b400ccbe051003f36f55b70b5c6e5a7892b Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 6 Mar 2025 10:59:14 -0500 Subject: [PATCH 198/242] Generate valid JSON even with invalid dates (#3801) --- src/Task.cpp | 8 ++++++-- test/unusual_task.test.py | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Task.cpp b/src/Task.cpp index 536f17a24..9779ed3bf 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -798,8 +798,6 @@ std::string Task::composeJSON(bool decorate /*= false*/) const { // If value is an empty string, do not ever output it if (i.second == "") continue; - if (attributes_written) out << ','; - std::string type = Task::attributes[i.first]; if (type == "") type = "string"; @@ -808,6 +806,8 @@ std::string Task::composeJSON(bool decorate /*= false*/) const { time_t epoch = get_date(i.first); if (epoch != 0) { Datetime d(i.second); + if (attributes_written) out << ','; + out << '"' << (i.first == "modification" ? "modified" : i.first) << "\":\"" // Date was deleted, do not export parsed empty string @@ -824,6 +824,8 @@ std::string Task::composeJSON(bool decorate /*= false*/) const { } */ else if (type == "numeric") { + if (attributes_written) out << ','; + out << '"' << i.first << "\":" << i.second; ++attributes_written; @@ -831,6 +833,8 @@ std::string Task::composeJSON(bool decorate /*= false*/) const { // Everything else is a quoted value. else { + if (attributes_written) out << ','; + out << '"' << i.first << "\":\"" << (type == "string" ? json::encode(i.second) : i.second) << '"'; diff --git a/test/unusual_task.test.py b/test/unusual_task.test.py index 8eb5bbaf7..18f8efffe 100755 --- a/test/unusual_task.test.py +++ b/test/unusual_task.test.py @@ -29,6 +29,7 @@ import sys import os import re import time +import json import unittest # Ensure python finds the local simpletap module @@ -192,6 +193,7 @@ class TestUnusualTasks(TestCase): modified="modified", ) _, out, _ = self.t(f"{uuid} export") + json.loads(out) if __name__ == "__main__": From 0c9205aa175655a5a96814760763b8e73cff4a4d Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 6 Mar 2025 19:33:38 -0500 Subject: [PATCH 199/242] Update ring (#3806) --- Cargo.lock | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2120f98a..bd42bbad3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -587,9 +587,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.30" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "shlex", ] @@ -2095,15 +2095,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" dependencies = [ "cc", "cfg-if", "getrandom", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -2457,12 +2456,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "spki" version = "0.6.0" From 5814526429d09d6a750a5a8bf36389ea636cc5fa Mon Sep 17 00:00:00 2001 From: Tobias Predel Date: Sat, 8 Mar 2025 15:04:01 +0100 Subject: [PATCH 200/242] Remove headers (#3807) --- src/CMakeLists.txt | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5905696d2..eaf5025e5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,23 +30,22 @@ add_library (task STATIC CLI2.cpp util.cpp) target_link_libraries(task taskchampion-cpp) -add_library (libshared STATIC libshared/src/Color.cpp libshared/src/Color.h - libshared/src/Configuration.cpp libshared/src/Configuration.h - libshared/src/Datetime.cpp libshared/src/Datetime.h - libshared/src/Duration.cpp libshared/src/Duration.h - libshared/src/FS.cpp libshared/src/FS.h - libshared/src/JSON.cpp libshared/src/JSON.h - libshared/src/Msg.cpp libshared/src/Msg.h - libshared/src/Pig.cpp libshared/src/Pig.h - libshared/src/RX.cpp libshared/src/RX.h - libshared/src/Table.cpp libshared/src/Table.h - libshared/src/Timer.cpp libshared/src/Timer.h - libshared/src/format.cpp libshared/src/format.h +add_library (libshared STATIC libshared/src/Color.cpp + libshared/src/Configuration.cpp + libshared/src/Datetime.cpp + libshared/src/Duration.cpp + libshared/src/FS.cpp + libshared/src/JSON.cpp + libshared/src/Msg.cpp + libshared/src/Pig.cpp + libshared/src/RX.cpp + libshared/src/Table.cpp + libshared/src/Timer.cpp + libshared/src/format.cpp libshared/src/ip.cpp - libshared/src/shared.cpp libshared/src/shared.h - libshared/src/unicode.cpp libshared/src/unicode.h - libshared/src/utf8.cpp libshared/src/utf8.h - libshared/src/wcwidth.h) + libshared/src/shared.cpp + libshared/src/unicode.cpp + libshared/src/utf8.cpp) add_executable (task_executable main.cpp) add_executable (calc_executable calc.cpp) From 5c67d22540999902d012f989d4087034a8d01b0d Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sat, 8 Mar 2025 14:20:21 +0000 Subject: [PATCH 201/242] Remove sponsorship outro from 'task news' --- src/commands/CmdNews.cpp | 56 ---------------------------------------- 1 file changed, 56 deletions(-) diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index cbda073d5..e94ea07f9 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -572,62 +572,6 @@ int CmdNews::execute(std::string& output) { } wait_for_enter(); - // Display outro - Datetime now; - Datetime beginning(2006, 11, 29); - Duration development_time = Duration(now - beginning); - - Color underline = Color("underline"); - - std::stringstream outro; - outro << underline.colorize(bold.colorize("Taskwarrior crowdfunding\n")); - outro << format( - "Taskwarrior has been in development for {1} years but its survival\n" - "depends on your support!\n\n" - "Please consider joining our {2} fundraiser to help us fund maintenance\n" - "and development of new features:\n\n", - std::lround(static_cast(development_time.days()) / 365.25), now.year()); - outro << bold.colorize(" https://github.com/sponsors/GothenburgBitFactory/\n\n"); - outro << "Perks are available for our sponsors.\n"; - - std::cout << outro.str(); - - // Set a mark in the config to remember which version's release notes were displayed - if (news_version < current_version) { - CmdConfig::setConfigVariable("news.version", std::string(current_version), false); - - // Revert back to default signal handling after displaying the outro - signal(SIGINT, SIG_DFL); - - std::string question = format( - "\nWould you like to open Taskwarrior {1} fundraising campaign to read more?", now.year()); - - std::vector options{"yes", "no"}; - std::vector 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; } From f73b42d23f6318d8e14d2c7113fcda0865964e4c Mon Sep 17 00:00:00 2001 From: Ram-Z Date: Wed, 12 Mar 2025 20:48:49 +0000 Subject: [PATCH 202/242] Allow dur/dur division (#3812) It is perfectly valid to divide two durations, it is a ratio with no unit. --- src/Variant.cpp | 5 +++-- test/variant_divide_test.cpp | 11 ++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Variant.cpp b/src/Variant.cpp index a5b68f172..ef0a08ac3 100644 --- a/src/Variant.cpp +++ b/src/Variant.cpp @@ -75,7 +75,6 @@ #define STRING_VARIANT_DIV_DUR_BOOL "Cannot divide duration by Boolean" #define STRING_VARIANT_DIV_DUR_STR "Cannot divide durations by strings" #define STRING_VARIANT_DIV_DUR_DATE "Cannot divide durations by dates" -#define STRING_VARIANT_DIV_DUR_DUR "Cannot divide durations by durations" #define STRING_VARIANT_MOD_BOOL "Cannot modulo Booleans" #define STRING_VARIANT_MOD_DATE "Cannot modulo date values" #define STRING_VARIANT_MOD_DUR "Cannot modulo duration values" @@ -1760,7 +1759,9 @@ Variant& Variant::operator/=(const Variant& other) { throw std::string(STRING_VARIANT_DIV_DUR_DATE); case type_duration: - throw std::string(STRING_VARIANT_DIV_DUR_DUR); + _type = type_real; + _real = static_cast(_duration) / static_cast(right._duration); + break; } break; } diff --git a/test/variant_divide_test.cpp b/test/variant_divide_test.cpp index c1df0c158..b1fc973d3 100644 --- a/test/variant_divide_test.cpp +++ b/test/variant_divide_test.cpp @@ -302,13 +302,10 @@ int TEST_NAME(int, char**) { } // duration / duration -> duration - try { - Variant v55 = v5 / v5; - t.fail("1200 / 1200 --> error"); - } catch (...) { - t.pass("1200 / 1200 --> error"); - } - + Variant v55 = v5 / v5; + t.is(v55.type(), Variant::type_real, "1200 / 1200 --> real"); + t.is(v55.get_real(), 1.0, "1200 / 1200 --> 1.0"); + t.is((v5 / v52).get_real(), 3.14136, EPSILON, "1200 / 382 --> 3.14136"); return 0; } From 063325b0525ed523a32fd0b670f0de0aa65d40ac Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 12 Mar 2025 17:58:49 -0400 Subject: [PATCH 203/242] Release 3.4.0 (#3811) --- CMakeLists.txt | 2 +- ChangeLog | 27 +++++++++++++++++++++++---- doc/man/taskrc.5.in | 3 +++ src/commands/CmdNews.cpp | 16 ++++++++++++++++ src/commands/CmdNews.h | 1 + 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e34703f8..4509430b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ enable_testing() set (CMAKE_EXPORT_COMPILE_COMMANDS ON) project (task - VERSION 3.3.0 + VERSION 3.4.0 DESCRIPTION "Taskwarrior - a command-line TODO list manager" HOMEPAGE_URL https://taskwarrior.org/) diff --git a/ChangeLog b/ChangeLog index 7e7540fc9..13c037a7b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,11 +1,32 @@ ------ current release --------------------------- +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 + +------ old releases ------------------------------ + +3.3.0 - + - Sync now supports AWS S3 as a backend. - A new `task import-v2` command allows importing Taskwarrior-2.x data files directly. -3.3.0 - - Thanks to the following people for contributions to this release: - Chongyun Lee @@ -18,8 +39,6 @@ Thanks to the following people for contributions to this release: - Scott Mcdermott - Thomas Lauf ------- old releases ------------------------------ - 3.2.0 - - Support for the journal in `task info` has been restored (#3671) and the diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 7ad152ab6..6f60a6439 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -212,6 +212,9 @@ This is a path to the hook scripts directory. By default it is ~/.task/hooks. .TP .B gc=1 Can be used to temporarily suspend rebuilding, so that task IDs don't change. +Rebuilding requires read/write access to the database, so disabling `gc` may +result in better performance. + Note that this should be used in the form of a command line override (task rc.gc=0 ...), and not permanently used in the .taskrc file, as this significantly affects performance in the long term. diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index e94ea07f9..cce5f079c 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -160,6 +160,7 @@ std::vector NewsItem::all() { version3_1_0(items); version3_2_0(items); version3_3_0(items); + version3_4_0(items); return items; } @@ -530,6 +531,21 @@ void NewsItem::version3_3_0(std::vector& items) { items.push_back(info); } +void NewsItem::version3_4_0(std::vector& items) { + Version version("3.4.0"); + NewsItem info{version, + /*title=*/"Read-Only Access", + /*bg_title=*/"", + /*background=*/"", + /*punchline=*/"Some Taskwarrior commands operate faster in read-only mode", + /*update=*/ + "Some commands do not need to write to the DB, so can open it in read-only\n" + "mode and thus more quickly. This does not include reports (task lists),\n" + "unless the `gc` config is false. Use `rc.gc=0` in command-lines to allow\n" + "read-only access.\n\n"}; + items.push_back(info); +} + //////////////////////////////////////////////////////////////////////////////// int CmdNews::execute(std::string& output) { auto words = Context::getContext().cli2.getWords(); diff --git a/src/commands/CmdNews.h b/src/commands/CmdNews.h index 6b70c0132..294c0f862 100644 --- a/src/commands/CmdNews.h +++ b/src/commands/CmdNews.h @@ -53,6 +53,7 @@ class NewsItem { static void version3_1_0(std::vector&); static void version3_2_0(std::vector&); static void version3_3_0(std::vector&); + static void version3_4_0(std::vector&); private: NewsItem(Version, const std::string&, const std::string& = "", const std::string& = "", From b792018c005eb7650a300f67e7424b1d18eb22db Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 12 Mar 2025 18:27:55 -0400 Subject: [PATCH 204/242] Updates after 3.4.0 (#3816) * Cargo update * Update submodules --- Cargo.lock | 1150 ++++++++++++++++++++++++++++++++++--------------- src/libshared | 2 +- 2 files changed, 801 insertions(+), 351 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd42bbad3..92c5bef32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -67,9 +67,9 @@ checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "async-stream" @@ -95,15 +95,21 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -112,9 +118,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.10" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b49afaa341e8dd8577e1a2200468f98956d6eda50bcf4a53246cc00174ba924" +checksum = "6a84fe2c5e9965fba0fbc2001db252f1d57527d82a905cca85127df227bca748" dependencies = [ "aws-credential-types", "aws-runtime", @@ -123,7 +129,7 @@ dependencies = [ "aws-sdk-sts", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json 0.60.7", + "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -131,7 +137,7 @@ dependencies = [ "bytes", "fastrand", "hex", - "http 0.2.12", + "http 1.3.1", "ring", "time", "tokio", @@ -142,9 +148,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" +checksum = "4471bef4c22a06d2c7a1b6492493d3fdf24a805323109d6874f9c94d5906ac14" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -153,10 +159,33 @@ dependencies = [ ] [[package]] -name = "aws-runtime" -version = "1.4.4" +name = "aws-lc-rs" +version = "1.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5ac934720fbb46206292d2c75b57e67acfc56fe7dfd34fb9a02334af08409ea" +checksum = "dabb68eb3a7aa08b46fddfd59a3d55c978243557a90ab804769f7e20e67d2b01" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bbe221bbf523b625a4dd8585c7f38166e31167ec2ca98051dbcb4c3b6e825d2" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "aws-runtime" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aff45ffe35196e593ea3b9dd65b320e51e2dda95aff4390bc459e461d09c6ad" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -180,9 +209,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.65.0" +version = "1.79.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3ba2c5c0f2618937ce3d4a5ad574b86775576fa24006bcb3128c6e2cbf3c34e" +checksum = "a8f63ba8f5fca32061c7d62d866ef65470edde38d4c5f8a0ebb8ff40a0521e1c" dependencies = [ "aws-credential-types", "aws-runtime", @@ -191,7 +220,7 @@ dependencies = [ "aws-smithy-checksums", "aws-smithy-eventstream", "aws-smithy-http", - "aws-smithy-json 0.61.1", + "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -202,6 +231,7 @@ dependencies = [ "hex", "hmac", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", "lru", "once_cell", @@ -214,15 +244,15 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.50.0" +version = "1.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ca43a4ef210894f93096039ef1d6fa4ad3edfabb3be92b80908b9f2e4b4eab" +checksum = "1d5330ad4e8a1ff49e9f26b738611caa72b105c41d41733801d1a36e8f9de936" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json 0.61.1", + "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -236,15 +266,15 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.51.0" +version = "1.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abaf490c2e48eed0bb8e2da2fb08405647bd7f253996e0f93b981958ea0f73b0" +checksum = "7956b1a85d49082347a7d17daa2e32df191f3e23c03d47294b99f95413026a78" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json 0.61.1", + "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -258,15 +288,15 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.51.0" +version = "1.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68fde0d69c8bfdc1060ea7da21df3e39f6014da316783336deff0a9ec28f4bf" +checksum = "065c533fbe6f84962af33fcf02b0350b7c1f79285baab5924615d2be3b232855" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json 0.61.1", + "aws-smithy-json", "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -281,9 +311,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3820e0c08d0737872ff3c7c1f21ebbb6693d832312d6152bf18ef50a5471c2" +checksum = "69d03c3c05ff80d54ff860fe38c726f6f494c639ae975203a101335f223386db" dependencies = [ "aws-credential-types", "aws-smithy-eventstream", @@ -296,7 +326,7 @@ dependencies = [ "hex", "hmac", "http 0.2.12", - "http 1.2.0", + "http 1.3.1", "once_cell", "p256", "percent-encoding", @@ -310,9 +340,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.1" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" dependencies = [ "futures-util", "pin-project-lite", @@ -321,15 +351,16 @@ dependencies = [ [[package]] name = "aws-smithy-checksums" -version = "0.60.13" +version = "0.63.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1a71073fca26775c8b5189175ea8863afb1c9ea2cceb02a5de5ad9dfbaa795" +checksum = "b65d21e1ba6f2cdec92044f904356a19f5ad86961acf015741106cdfafd747c0" dependencies = [ "aws-smithy-http", "aws-smithy-types", "bytes", "crc32c", "crc32fast", + "crc64fast-nvme", "hex", "http 0.2.12", "http-body 0.4.6", @@ -342,9 +373,9 @@ dependencies = [ [[package]] name = "aws-smithy-eventstream" -version = "0.60.5" +version = "0.60.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef7d0a272725f87e51ba2bf89f8c21e4df61b9e49ae1ac367a6d69916ef7c90" +checksum = "7c45d3dddac16c5c59d553ece225a88870cf81b7b813c9cc17b78cf4685eac7a" dependencies = [ "aws-smithy-types", "bytes", @@ -353,9 +384,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.11" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8bc3e8fdc6b8d07d976e301c02fe553f72a39b7a9fea820e023268467d7ab6" +checksum = "c5949124d11e538ca21142d1fba61ab0a2a2c1bc3ed323cdb3e4b878bfb83166" dependencies = [ "aws-smithy-eventstream", "aws-smithy-runtime-api", @@ -364,6 +395,7 @@ dependencies = [ "bytes-utils", "futures-core", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", "once_cell", "percent-encoding", @@ -373,19 +405,38 @@ dependencies = [ ] [[package]] -name = "aws-smithy-json" -version = "0.60.7" +name = "aws-smithy-http-client" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +checksum = "0497ef5d53065b7cd6a35e9c1654bd1fefeae5c52900d91d1b188b0af0f29324" dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", "aws-smithy-types", + "h2 0.4.8", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.6.0", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.5", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.23", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tower", + "tracing", ] [[package]] name = "aws-smithy-json" -version = "0.61.1" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4e69cc50921eb913c6b662f8d909131bb3e6ad6cb6090d3a39b66fc5c52095" +checksum = "92144e45819cae7dc62af23eac5a038a58aa544432d2102609654376a900bd07" dependencies = [ "aws-smithy-types", ] @@ -402,42 +453,39 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.4" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f20685047ca9d6f17b994a07f629c813f08b5bce65523e47124879e60103d45" +checksum = "f6328865e36c6fd970094ead6b05efd047d3a80ec5fc3be5e743910da9f2ebf8" dependencies = [ "aws-smithy-async", "aws-smithy-http", + "aws-smithy-http-client", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "fastrand", - "h2", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", - "httparse", - "hyper 0.14.30", - "hyper-rustls 0.24.2", "once_cell", "pin-project-lite", "pin-utils", - "rustls 0.21.12", "tokio", "tracing", ] [[package]] name = "aws-smithy-runtime-api" -version = "1.7.3" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd" +checksum = "3da37cf5d57011cb1753456518ec76e31691f1f474b73934a284eb2a1c76510f" dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.2.0", + "http 1.3.1", "pin-project-lite", "tokio", "tracing", @@ -446,16 +494,16 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.9" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbd94a32b3a7d55d3806fe27d98d3ad393050439dd05eb53ece36ec5e3d3510" +checksum = "836155caafba616c0ff9b07944324785de2ab016141c3550bd1c07882f8cee8f" dependencies = [ "base64-simd", "bytes", "bytes-utils", "futures-core", "http 0.2.12", - "http 1.2.0", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -481,9 +529,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.3" +version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" +checksum = "3873f8deed8927ce8d04487630dc9ff73193bab64742a61d050e57a68dec4125" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -505,7 +553,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -538,15 +586,38 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "bb97d56060ee67d285efb8001fec9d2a4c710c32efd2e14b5cbb5ba71930fc2d" + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn", + "which", +] [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "block-buffer" @@ -559,9 +630,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -571,9 +642,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bytes-utils" @@ -591,9 +662,20 @@ version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -608,9 +690,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", @@ -618,23 +700,34 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets", + "windows-link", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", ] [[package]] name = "clap" -version = "4.5.21" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.21" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" dependencies = [ "anstyle", "clap_lex", @@ -643,9 +736,18 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] [[package]] name = "codespan-reporting" @@ -673,6 +775,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -681,13 +793,28 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32c" version = "0.6.8" @@ -706,6 +833,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crc64fast-nvme" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4955638f00a809894c947f85a024020a20815b65a5eea633798ea7924edab2b3" +dependencies = [ + "crc", +] + [[package]] name = "crypto-bigint" version = "0.4.9" @@ -740,9 +876,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.133" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e1ec88093d2abd9cf1b09ffd979136b8e922bf31cad966a8fe0d73233112ef" +checksum = "4615b1496a78e2c624b792d982e5d2152db2e5b53d401605776ec819e50891ce" dependencies = [ "cc", "cxxbridge-cmd", @@ -754,9 +890,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.133" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afa390d956ee7ccb41aeed7ed7856ab3ffb4fc587e7216be7e0f83e949b4e6c" +checksum = "147bafb46dc3ad9d24717723751371147373ffa8cf5f816e0031e34d6998b339" dependencies = [ "cc", "codespan-reporting", @@ -768,9 +904,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.133" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c23bfff654d6227cbc83de8e059d2f8678ede5fc3a6c5a35d5c379983cc61e6" +checksum = "07d4631e3095af42e8de3c73ee2b7d49fe541578ccd9f6b19920ac3c5fef528c" dependencies = [ "clap", "codespan-reporting", @@ -781,15 +917,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.133" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c01b36e22051bc6928a78583f1621abaaf7621561c2ada1b00f7878fbe2caa" +checksum = "f05c4a04df781bb50f52a16cfd7c580d0d904af8e7c411678be52d84ed3416ab" [[package]] name = "cxxbridge-macro" -version = "1.0.133" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e14013136fac689345d17b9a6df55977251f11d333c0a571e8d963b55e1f95" +checksum = "93be4a484f2b719c2cb56ab5f06e05377987477c7b3bf7a1cf28a266ec8cfaa1" dependencies = [ "proc-macro2", "quote", @@ -850,6 +986,12 @@ dependencies = [ "syn", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "ecdsa" version = "0.14.8" @@ -864,9 +1006,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" @@ -890,18 +1032,28 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] [[package]] name = "fallible-iterator" @@ -933,9 +1085,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.34" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", "miniz_oxide", @@ -949,9 +1101,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "form_urlencoded" @@ -962,6 +1114,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures-channel" version = "0.3.31" @@ -1042,16 +1200,34 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", +] + [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + [[package]] name = "google-cloud-auth" version = "0.17.2" @@ -1067,7 +1243,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "time", "tokio", "tracing", @@ -1076,12 +1252,12 @@ dependencies = [ [[package]] name = "google-cloud-metadata" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f945a208886a13d07636f38fb978da371d0abc3e34bad338124b9f8c135a8f" +checksum = "d901aeb453fd80e51d64df4ee005014f6cf39f2d736dd64f7239c132d9d39a6a" dependencies = [ "reqwest", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", ] @@ -1111,7 +1287,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "thiserror 1.0.64", + "thiserror 1.0.69", "time", "tokio", "tracing", @@ -1157,6 +1333,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.3.1", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1192,12 +1387,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hex" version = "0.4.3" @@ -1215,11 +1404,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1235,9 +1424,9 @@ dependencies = [ [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -1262,27 +1451,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.2.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.2.0", + "futures-core", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1292,15 +1481,15 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -1316,14 +1505,15 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.2.0", + "h2 0.4.8", + "http 1.3.1", "http-body 1.0.1", "httparse", "itoa", @@ -1341,7 +1531,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.32", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", @@ -1351,18 +1541,19 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.2.0", - "hyper 1.5.1", + "http 1.3.1", + "hyper 1.6.0", "hyper-util", - "rustls 0.23.19", + "rustls 0.23.23", + "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tower-service", "webpki-roots", ] @@ -1376,9 +1567,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", - "hyper 1.5.1", + "hyper 1.6.0", "pin-project-lite", "socket2", "tokio", @@ -1550,9 +1741,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -1560,32 +1751,51 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "jsonwebtoken" -version = "9.3.0" +version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "js-sys", "pem", "ring", @@ -1595,10 +1805,32 @@ dependencies = [ ] [[package]] -name = "libc" -version = "0.2.159" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] [[package]] name = "libsqlite3-sys" @@ -1613,18 +1845,24 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" dependencies = [ "cc", ] [[package]] -name = "litemap" -version = "0.7.4" +name = "linux-raw-sys" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "lock_api" @@ -1638,9 +1876,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "lru" @@ -1684,26 +1922,41 @@ dependencies = [ ] [[package]] -name = "miniz_oxide" -version = "0.8.0" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1740,30 +1993,30 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "outref" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" [[package]] name = "p256" @@ -1796,14 +2049,14 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "pem" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ "base64 0.22.1", "serde", @@ -1826,9 +2079,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1858,9 +2111,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "powerfmt" @@ -1870,18 +2123,28 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.23", +] + +[[package]] +name = "prettyplease" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a" +dependencies = [ + "proc-macro2", + "syn", ] [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -1896,10 +2159,10 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", - "rustls 0.23.19", + "rustc-hash 2.1.1", + "rustls 0.23.23", "socket2", - "thiserror 2.0.6", + "thiserror 2.0.12", "tokio", "tracing", ] @@ -1911,14 +2174,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", - "getrandom", + "getrandom 0.2.15", "rand", "ring", - "rustc-hash", - "rustls 0.23.19", + "rustc-hash 2.1.1", + "rustls 0.23.23", "rustls-pki-types", "slab", - "thiserror 2.0.6", + "thiserror 2.0.12", "tinyvec", "tracing", "web-time", @@ -1926,9 +2189,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" +checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944" dependencies = [ "cfg_aliases", "libc", @@ -1940,9 +2203,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -1974,23 +2237,23 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -2000,9 +2263,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2023,20 +2286,20 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "989e327e510263980e231de548a33e63d34962d29ae61b467389a1a09627a254" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-util", "ipnet", "js-sys", @@ -2047,7 +2310,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.19", + "rustls 0.23.23", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", @@ -2055,8 +2318,9 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-util", + "tower", "tower-service", "url", "wasm-bindgen", @@ -2069,16 +2333,16 @@ dependencies = [ [[package]] name = "reqwest-middleware" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1ccd3b55e711f91a9885a2fa6fbbb2e39db1776420b062efc058c6410f7e5e3" +checksum = "64e8975513bd9a7a43aad01030e79b3498e05db14e9d945df6483e8cf9b8c4c4" dependencies = [ "anyhow", "async-trait", - "http 1.2.0", + "http 1.3.1", "reqwest", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", "tower-service", ] @@ -2095,13 +2359,13 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "untrusted", "windows-sys 0.52.0", @@ -2129,9 +2393,15 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "2.1.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -2142,6 +2412,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustls" version = "0.21.12" @@ -2156,10 +2439,11 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -2178,7 +2462,7 @@ dependencies = [ "openssl-probe", "rustls-pemfile 1.0.4", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] @@ -2191,7 +2475,19 @@ dependencies = [ "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.2.0", ] [[package]] @@ -2214,9 +2510,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" dependencies = [ "web-time", ] @@ -2237,6 +2533,7 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -2244,21 +2541,21 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -2271,9 +2568,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" +checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" [[package]] name = "sct" @@ -2306,7 +2603,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -2314,9 +2624,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -2324,24 +2634,24 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -2350,9 +2660,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -2421,13 +2731,13 @@ dependencies = [ [[package]] name = "simple_asn1" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 1.0.64", + "thiserror 2.0.12", "time", ] @@ -2442,15 +2752,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2515,9 +2825,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.90" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -2565,7 +2875,7 @@ dependencies = [ "serde_json", "strum", "strum_macros", - "thiserror 2.0.6", + "thiserror 2.0.12", "tokio", "ureq", "url", @@ -2592,27 +2902,27 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.64", + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "2.0.6" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.6", + "thiserror-impl 2.0.12", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -2621,9 +2931,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.6" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -2632,9 +2942,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" dependencies = [ "deranged", "itoa", @@ -2647,15 +2957,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" dependencies = [ "num-conv", "time-core", @@ -2673,9 +2983,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -2688,9 +2998,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a" dependencies = [ "backtrace", "bytes", @@ -2706,9 +3016,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", @@ -2727,19 +3037,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.19", + "rustls 0.23.23", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -2748,6 +3058,27 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -2756,9 +3087,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -2767,9 +3098,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -2778,9 +3109,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -2793,24 +3124,21 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-width" @@ -2834,7 +3162,7 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls 0.23.19", + "rustls 0.23.23", "rustls-native-certs 0.7.3", "rustls-pki-types", "url", @@ -2872,11 +3200,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.11.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" dependencies = [ - "getrandom", + "getrandom 0.3.1", "serde", ] @@ -2914,25 +3242,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasm-bindgen" -version = "0.2.95" +name = "wasi" +version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -2941,21 +3278,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2963,9 +3301,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -2976,15 +3314,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -2995,9 +3336,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -3015,13 +3356,25 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" dependencies = [ "rustls-pki-types", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "winapi-util" version = "0.1.9" @@ -3037,37 +3390,42 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] -name = "windows-registry" -version = "0.2.0" +name = "windows-link" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", "windows-strings", - "windows-targets", + "windows-targets 0.53.0", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" dependencies = [ - "windows-targets", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-result", - "windows-targets", + "windows-link", ] [[package]] @@ -3076,7 +3434,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3085,7 +3443,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3094,14 +3452,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -3110,48 +3484,105 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "write16" version = "1.0.0" @@ -3200,8 +3631,16 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive 0.8.23", ] [[package]] @@ -3216,19 +3655,30 @@ dependencies = [ ] [[package]] -name = "zerofrom" -version = "0.1.5" +name = "zerocopy-derive" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", diff --git a/src/libshared b/src/libshared index 1a06cb4ca..2aa844cb9 160000 --- a/src/libshared +++ b/src/libshared @@ -1 +1 @@ -Subproject commit 1a06cb4caebdae3c5e58fe83e2fd2211d2959815 +Subproject commit 2aa844cb9b015fca81b947c57fde07999ede002b From 2ee5fb287cf8bc28a4755b662c54ef4334be548e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalle=20Kiet=C3=A4v=C3=A4inen?= <1026741+kietavainen@users.noreply.github.com> Date: Fri, 14 Mar 2025 02:01:16 +0200 Subject: [PATCH 205/242] Fix suppressing news nag after reading the news (#3817) The news nag suppression regressed again in 5c67d22. That commit intended to remove the sponsorship outro from the news, but also removed the bookkeeping that marks the news as read. This commit reverts that part back to its previous state. --- src/commands/CmdNews.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/commands/CmdNews.cpp b/src/commands/CmdNews.cpp index cce5f079c..b6a6f75cb 100644 --- a/src/commands/CmdNews.cpp +++ b/src/commands/CmdNews.cpp @@ -588,6 +588,11 @@ int CmdNews::execute(std::string& output) { } wait_for_enter(); + // Set a mark in the config to remember which version's release notes were displayed + if (news_version < current_version) { + CmdConfig::setConfigVariable("news.version", std::string(current_version), false); + } + return 0; } From 1f6e7de569cea7bced2a334ad9982a42e6ba614e Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 14 Mar 2025 05:20:52 -0400 Subject: [PATCH 206/242] Release 3.4.1 (#3818) --- CMakeLists.txt | 2 +- ChangeLog | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4509430b5..9323dc5a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ enable_testing() set (CMAKE_EXPORT_COMPILE_COMMANDS ON) project (task - VERSION 3.4.0 + VERSION 3.4.1 DESCRIPTION "Taskwarrior - a command-line TODO list manager" HOMEPAGE_URL https://taskwarrior.org/) diff --git a/ChangeLog b/ChangeLog index 13c037a7b..5d372307b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ ------ 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 @@ -19,8 +25,6 @@ Thanks to the following people for contributions to this release: - Yong Li - jrmarino ------- old releases ------------------------------ - 3.3.0 - - Sync now supports AWS S3 as a backend. From f9c17d9b5be16947007323d287f4390c5e690e72 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 19:50:44 -0400 Subject: [PATCH 207/242] [pre-commit.ci] pre-commit autoupdate (#3822) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/pre-commit/mirrors-clang-format: v19.1.7 → v20.1.0](https://github.com/pre-commit/mirrors-clang-format/compare/v19.1.7...v20.1.0) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- test/lexer_test.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7e9ce69fa..c0c0596dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v19.1.7 + rev: v20.1.0 hooks: - id: clang-format types_or: [c++, c] diff --git a/test/lexer_test.cpp b/test/lexer_test.cpp index 715c46081..0189699bb 100644 --- a/test/lexer_test.cpp +++ b/test/lexer_test.cpp @@ -293,7 +293,10 @@ int TEST_NAME(int, char**) { "'wonder'+0 : 'prowonderbread'+3 --> 6"); // Test all Lexer types. -#define NO {"", Lexer::Type::word} +#define NO \ + { \ + "", Lexer::Type::word \ + } struct { const char* input; struct { From 768d45197b6242e049c56d58789c33032fd6ecc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 20:11:14 -0400 Subject: [PATCH 208/242] Bump docker/login-action from 3.3.0 to 3.4.0 (#3821) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v3.3.0...v3.4.0) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index a0414c233..454dadf20 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -36,7 +36,7 @@ jobs: uses: sigstore/cosign-installer@v3.8.1 - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3.3.0 + uses: docker/login-action@v3.4.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.repository_owner }} From 7bf3be2f07b6666c50889c025171b23b9d8489cb Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Thu, 20 Mar 2025 00:47:52 +0100 Subject: [PATCH 209/242] Update dependabot.yml (#3825) --- .github/dependabot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4490fcc9a..6e2bb8297 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,11 @@ updates: directory: "/" schedule: interval: "weekly" + # Enable version updates for git submodules + - package-ecosystem: "gitsubmodule" + directory: "/" + schedule: + interval: "daily" # Enable updates for Rust packages - package-ecosystem: "cargo" directory: "/" # Location of package manifests From bc1629727459f271aa465c68eeecc32a0c203225 Mon Sep 17 00:00:00 2001 From: soerenschneider <56670304+soerenschneider@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:26:31 +0100 Subject: [PATCH 210/242] docs: Add note to disable AWS lifecycle policies (#3828) * docs: Add note to disable AWS lifecycle policies * fix grammar --------- Co-authored-by: Dustin J. Mitchell --- doc/man/task-sync.5.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/man/task-sync.5.in b/doc/man/task-sync.5.in index a2dacf23b..1560e2d8a 100644 --- a/doc/man/task-sync.5.in +++ b/doc/man/task-sync.5.in @@ -143,7 +143,9 @@ Then configure Taskwarrior with: To synchronize your tasks to AWS, select a region near you and use the AWS console to create a new S3 bucket. The default settings for the bucket are -adequate. +adequate. In particular, ensure that no lifecycle policies are enabled, as they +may automatically delete or transition objects, potentially impacting data +availability. You will also need an AWS IAM user with the following policy, where BUCKETNAME is the name of the bucket. The same user can be configured for multiple From a3b44bdef52fd3b8d7923f9c541b9d5e4062ed98 Mon Sep 17 00:00:00 2001 From: Ram-Z Date: Mon, 24 Mar 2025 23:07:27 +0000 Subject: [PATCH 211/242] Replace inacurate comment (#3829) --- src/columns/ColTypeDuration.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/columns/ColTypeDuration.cpp b/src/columns/ColTypeDuration.cpp index 34ae53db1..ea8511475 100644 --- a/src/columns/ColTypeDuration.cpp +++ b/src/columns/ColTypeDuration.cpp @@ -56,11 +56,9 @@ void ColumnTypeDuration::modify(Task& task, const std::string& value) { evaluatedValue = Variant(value); } - // The duration is stored in raw form, but it must still be valid, - // and therefore is parsed first. + // The duration is first parsed, then stored inside the variant as a `time_t` std::string label = " MODIFICATION "; if (evaluatedValue.type() == Variant::type_duration) { - // Store the raw value, for 'recur'. Context::getContext().debug(label + _name + " <-- " + (std::string)evaluatedValue + " <-- '" + value + '\''); task.set(_name, evaluatedValue); From 4a464c13a8305bea79d4067073e479139cef663d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:58:09 -0400 Subject: [PATCH 212/242] Bump src/taskchampion-cpp/corrosion from `fcd8b41` to `c484074` (#3836) Bumps [src/taskchampion-cpp/corrosion](https://github.com/corrosion-rs/corrosion) from `fcd8b41` to `c484074`. - [Release notes](https://github.com/corrosion-rs/corrosion/releases) - [Commits](https://github.com/corrosion-rs/corrosion/compare/fcd8b41981cb1e80f4dcc20fa8970dc6aa981c9f...c4840742d23d1c1a187152e2c5ae65886b9c9007) --- updated-dependencies: - dependency-name: src/taskchampion-cpp/corrosion dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/taskchampion-cpp/corrosion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/taskchampion-cpp/corrosion b/src/taskchampion-cpp/corrosion index fcd8b4198..c4840742d 160000 --- a/src/taskchampion-cpp/corrosion +++ b/src/taskchampion-cpp/corrosion @@ -1 +1 @@ -Subproject commit fcd8b41981cb1e80f4dcc20fa8970dc6aa981c9f +Subproject commit c4840742d23d1c1a187152e2c5ae65886b9c9007 From db23195f4d339a5a5c985dbc6439de91e42e46bd Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Tue, 1 Apr 2025 16:42:42 -0400 Subject: [PATCH 213/242] Remove duplicate word (#3834) * Remove duplicate word * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/commands/CmdContext.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/CmdContext.cpp b/src/commands/CmdContext.cpp index 37c89682f..fae5d3818 100644 --- a/src/commands/CmdContext.cpp +++ b/src/commands/CmdContext.cpp @@ -218,8 +218,8 @@ void CmdContext::defineContext(const std::vector& words, std::strin if (!valid_write_context) { std::stringstream warning; warning - << format("The filter '{1}' is not a valid modification string, because it contains {2}.", - value, reason) + << format("The filter '{1}' is not a valid modification string, because it {2}.", value, + reason) << "\nAs such, value for the write context cannot be set (context will not apply on task " "add / task log).\n\n" << format( From f1cb656f7555abf783b93b0668ad36f1ef2b9a9d Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 7 Apr 2025 20:40:06 -0400 Subject: [PATCH 214/242] Update tokio to 1.44.2 (#3842) --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 92c5bef32..299662c96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2998,9 +2998,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.44.0" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", From 5b70ce6be2f8d5c07c2d89d68b82ad60a02bf125 Mon Sep 17 00:00:00 2001 From: Adrian Wilkins Date: Thu, 10 Apr 2025 13:33:52 +0100 Subject: [PATCH 216/242] Add note about uninstallation (#3845) * Add note about uninstallation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- INSTALL | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index 1635fbb9e..beee2018f 100644 --- a/INSTALL +++ b/INSTALL @@ -34,7 +34,7 @@ Briefly, these shell commands will unpack, build and install Taskwarrior: $ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release . [3] $ cmake --build build [4] $ sudo cmake --install build [5] - $ cd .. ; rm -r task-X.Y.Z [6] + $ cd .. ; rm -r task-X.Y.Z [6] (see: Uninstallation) These commands are explained below: @@ -103,6 +103,13 @@ There is no uninstall option in CMake makefiles. This is a manual process. To uninstall Taskwarrior, remove the files listed in the install_manifest.txt file that was generated when you built Taskwarrior. +```sh +cd task-X.Y.Z +sudo xargs rm < build/install_manifest.txt +``` + +If you want to uninstall this way, you will need to omit step [6] above and +retain the source folder after installation. Taskwarrior Build Notes ----------------------- From 15bb71764e7820a9df4ad5cd5b13963bc90fd6ad Mon Sep 17 00:00:00 2001 From: Nick Grimshaw Date: Mon, 14 Apr 2025 18:08:32 +0100 Subject: [PATCH 217/242] =?UTF-8?q?Redact=20HTTP=20credentials=20from=20"S?= =?UTF-8?q?yncing=E2=80=A6"=20message=20(#3846)=20(#3847)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/commands/CmdSync.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index c0fa93c36..15e07469d 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -36,6 +36,7 @@ #include #include +#include #include //////////////////////////////////////////////////////////////////////////////// @@ -78,6 +79,10 @@ int CmdSync::execute(std::string& output) { out << "sync.server.origin is deprecated. Use sync.server.url instead.\n"; } + // redact credentials from `server_url`, if present + std::regex remove_creds_regex("^(https?://.+):(.+)@(.+)"); + std::string safe_server_url = std::regex_replace(server_url, remove_creds_regex, "$1:****@$3"); + if (server_dir != "") { if (verbose) { out << format("Syncing with {1}", server_dir) << '\n'; @@ -144,7 +149,7 @@ int CmdSync::execute(std::string& output) { throw std::string("sync.server.client_id and sync.encryption_secret are required"); } if (verbose) { - out << format("Syncing with sync server at {1}", server_url) << '\n'; + out << format("Syncing with sync server at {1}", safe_server_url) << '\n'; } replica->sync_to_remote(server_url, tc::uuid_from_string(client_id), encryption_secret, avoid_snapshots); From 2a64b5c880127b5fa6eff4961fef61319f83d0f1 Mon Sep 17 00:00:00 2001 From: Felix Schurk <75752337+felixschurk@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:49:14 +0200 Subject: [PATCH 218/242] change implementation from has (#3849) Change from get(uuid, task) to get_task_data to always have a full uuid match and to improve read performance for the diagnostics command. See #3848 for details. --- src/TDB2.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/TDB2.cpp b/src/TDB2.cpp index c49f513d6..5676a509f 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -354,8 +354,7 @@ bool TDB2::get(const std::string& uuid, Task& task) { //////////////////////////////////////////////////////////////////////////////// // Locate task by UUID, wherever it is. bool TDB2::has(const std::string& uuid) { - Task task; - return get(uuid, task); + return replica()->get_task_data(tc::uuid_from_string(uuid)).is_some(); } //////////////////////////////////////////////////////////////////////////////// From bfea0f68367f8d91bff752f65aea85274a728b3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 11:45:17 -0400 Subject: [PATCH 219/242] Bump src/taskchampion-cpp/corrosion from `c484074` to `bf065b8` (#3850) Bumps [src/taskchampion-cpp/corrosion](https://github.com/corrosion-rs/corrosion) from `c484074` to `bf065b8`. - [Release notes](https://github.com/corrosion-rs/corrosion/releases) - [Commits](https://github.com/corrosion-rs/corrosion/compare/c4840742d23d1c1a187152e2c5ae65886b9c9007...bf065b89a33c4d6ce16cae626e8f15f9914ce4dc) --- updated-dependencies: - dependency-name: src/taskchampion-cpp/corrosion dependency-version: bf065b89a33c4d6ce16cae626e8f15f9914ce4dc dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/taskchampion-cpp/corrosion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/taskchampion-cpp/corrosion b/src/taskchampion-cpp/corrosion index c4840742d..bf065b89a 160000 --- a/src/taskchampion-cpp/corrosion +++ b/src/taskchampion-cpp/corrosion @@ -1 +1 @@ -Subproject commit c4840742d23d1c1a187152e2c5ae65886b9c9007 +Subproject commit bf065b89a33c4d6ce16cae626e8f15f9914ce4dc From bae37d9448541c3cfe3707e4729dbc1ebc4192ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 13:46:42 -0400 Subject: [PATCH 220/242] Bump src/taskchampion-cpp/corrosion from `bf065b8` to `715c235` (#3853) Bumps [src/taskchampion-cpp/corrosion](https://github.com/corrosion-rs/corrosion) from `bf065b8` to `715c235`. - [Release notes](https://github.com/corrosion-rs/corrosion/releases) - [Commits](https://github.com/corrosion-rs/corrosion/compare/bf065b89a33c4d6ce16cae626e8f15f9914ce4dc...715c235daef4b8ee67278f12256334ad3dd4c4ae) --- updated-dependencies: - dependency-name: src/taskchampion-cpp/corrosion dependency-version: 715c235daef4b8ee67278f12256334ad3dd4c4ae dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/taskchampion-cpp/corrosion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/taskchampion-cpp/corrosion b/src/taskchampion-cpp/corrosion index bf065b89a..715c235da 160000 --- a/src/taskchampion-cpp/corrosion +++ b/src/taskchampion-cpp/corrosion @@ -1 +1 @@ -Subproject commit bf065b89a33c4d6ce16cae626e8f15f9914ce4dc +Subproject commit 715c235daef4b8ee67278f12256334ad3dd4c4ae From 31829d61fca049abf5ac05832bab9bc8d20bb56d Mon Sep 17 00:00:00 2001 From: Ram-Z Date: Mon, 21 Apr 2025 01:51:38 +0100 Subject: [PATCH 221/242] Add uuid UDA type (#3827) Mainly so that UDAs that refer to another task can be formated as "short". --- doc/man/taskrc.5.in | 2 +- src/Task.cpp | 2 +- src/columns/ColUDA.cpp | 20 +++++++++++ src/columns/ColUDA.h | 8 +++++ src/columns/Column.cpp | 8 ++++- test/columns.test.py | 79 +++++++++++++++++++++++++++++++++++++----- 6 files changed, 107 insertions(+), 12 deletions(-) diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index 6f60a6439..9b8e4903a 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -1384,7 +1384,7 @@ if you define a UDA named 'estimate', Taskwarrior will not know that this value is weeks, hours, minutes, money, or some other resource count. .TP -.B uda..type=string|numeric|date|duration +.B uda..type=string|numeric|uuid|date|duration .RS Defines a UDA called '', of the specified type. .RE diff --git a/src/Task.cpp b/src/Task.cpp index 9779ed3bf..f6026b848 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -1999,7 +1999,7 @@ void Task::modify(modType type, bool text_required /* = false */) { // Delegate modification to the column object or their base classes. if (name == "depends" || name == "tags" || name == "recur" || column->type() == "date" || column->type() == "duration" || column->type() == "numeric" || - column->type() == "string") { + column->type() == "string" || column->type() == "uuid") { column->modify(*this, value); mods = true; } diff --git a/src/columns/ColUDA.cpp b/src/columns/ColUDA.cpp index 48686ba70..a6452c631 100644 --- a/src/columns/ColUDA.cpp +++ b/src/columns/ColUDA.cpp @@ -297,3 +297,23 @@ void ColumnUDADuration::render(std::vector& lines, Task& task, int } //////////////////////////////////////////////////////////////////////////////// +ColumnUDAUUID::ColumnUDAUUID() { + _name = ""; + _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); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColUDA.h b/src/columns/ColUDA.h index ce8aaef97..04d57a8d7 100644 --- a/src/columns/ColUDA.h +++ b/src/columns/ColUDA.h @@ -31,6 +31,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////////////////// class ColumnUDAString : public ColumnTypeString { @@ -83,5 +84,12 @@ class ColumnUDADuration : public ColumnTypeDuration { std::vector _values; }; +//////////////////////////////////////////////////////////////////////////////// +class ColumnUDAUUID : public ColumnUUID { + public: + ColumnUDAUUID(); + bool validate(const std::string&) const; +}; + #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/Column.cpp b/src/columns/Column.cpp index f9231af1a..131b48da4 100644 --- a/src/columns/Column.cpp +++ b/src/columns/Column.cpp @@ -246,9 +246,15 @@ Column* Column::uda(const std::string& name) { c->_label = label; if (values != "") c->_values = split(values, ','); return c; + } else if (type == "uuid") { + auto c = new ColumnUDAUUID(); + c->_name = name; + c->_label = label; + return c; } else if (type != "") throw std::string( - "User defined attributes may only be of type 'string', 'date', 'duration' or 'numeric'."); + "User defined attributes may only be of type 'string', 'uuid', date', 'duration' or " + "'numeric'."); return nullptr; } diff --git a/test/columns.test.py b/test/columns.test.py index 7e839414e..7f14c571e 100755 --- a/test/columns.test.py +++ b/test/columns.test.py @@ -109,29 +109,26 @@ class TestUUIDFormats(TestCase): def setUpClass(cls): """Executed once before any test in the class""" cls.t = Task() - cls.t.config("report.xxx.columns", "id,uuid") + cls.t.config("report.xxx.columns", "uuid") cls.t.config("verbose", "nothing") cls.t("add zero") code, out, err = cls.t("_get 1.uuid") cls.uuid = out.strip() - def setUp(self): - """Executed before each test in the class""" - def test_uuid_long(self): """Verify formatting of 'uuid.long' column""" - code, out, err = self.t("xxx rc.report.xxx.columns:id,uuid.long") - self.assertIn(self.uuid, out) + code, out, err = self.t("xxx rc.report.xxx.columns:uuid.long") + self.assertEqual(self.uuid, out.strip()) def test_uuid_short(self): """Verify formatting of 'uuid.short' column""" - code, out, err = self.t("xxx rc.report.xxx.columns:id,uuid.short") - self.assertIn(self.uuid[:7], out) + code, out, err = self.t("xxx rc.report.xxx.columns:uuid.short") + self.assertEqual(self.uuid[:8], out.strip()) def test_uuid_format_unrecognized(self): """Verify uuid.donkey formatting fails""" - code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,uuid.donkey") + code, out, err = self.t.runError("xxx rc.report.xxx.columns:uuid.donkey") self.assertEqual(err, "Unrecognized column format 'uuid.donkey'\n") @@ -482,6 +479,70 @@ start active* ✓ """ +class TestUDAUUIDFormats(TestCase): + @classmethod + def setUpClass(cls): + """Executed once before any test in the class""" + cls.t = Task() + cls.t.config("verbose", "nothing") + cls.t.config("uda.uda_uuid.label", "uda_uuid") + cls.t.config("uda.uda_uuid.type", "uuid") + cls.t.config("report.xxx.columns", "uda_uuid") + + cls.t("add zero") + code, out, err = cls.t("_get 1.uuid") + cls.t("add uda_uuid:{} one".format(out.strip())) + code, out, err = cls.t("_get 2.uda_uuid") + cls.uda_uuid = out.strip() + + def test_uda_uuid_invalid_fails(self): + """Verify adding invalid uuid fails""" + code, out, err = self.t.runError("add uda_uuid:shrek three") + self.assertNotEqual(code, 0) + self.assertIn("uda_uuid", err.strip()) + self.assertIn("shrek", err.strip()) + + def test_uda_uuid_long(self): + """Verify formatting of 'uda_uuid.long' column""" + code, out, err = self.t("2 xxx rc.report.xxx.columns:uda_uuid.long") + self.assertEqual(self.uda_uuid, out.strip()) + + def test_uda_uuid_short(self): + """Verify formatting of 'uda_uuid.short' column""" + code, out, err = self.t("2 xxx rc.report.xxx.columns:uda_uuid.short") + self.assertEqual(self.uda_uuid[:8], out.strip()) + + def test_uda_uuid_format_unrecognized(self): + """Verify uda_uuid.donkey formatting fails""" + code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,uda_uuid.donkey") + self.assertEqual(err, "Unrecognized column format 'uda_uuid.donkey'\n") + + +class TestUDAUUIDReconfiguredFromString(TestCase): + @classmethod + def setUpClass(cls): + """Executed once before any test in the class""" + cls.t = Task() + cls.t.config("verbose", "nothing") + cls.t.config("uda.uda_uuid.label", "uda_uuid") + cls.t.config("report.xxx.columns", "uda_uuid") + + cls.t.config("uda.uda_uuid.type", "string") + cls.expected_str = 3 * "littlepigs" + cls.t("add uda_uuid:{} one".format(cls.expected_str)) + cls.t.config("uda.uda_uuid.type", "uuid") + + def test_uda_uuid_long(self): + """Verify formatting of 'uda_uuid.long' column""" + code, out, err = self.t("1 xxx rc.report.xxx.columns:uda_uuid.long") + self.assertEqual(self.expected_str, out.strip()) + + def test_uda_uuid_short(self): + """Verify formatting of 'uda_uuid.short' column""" + code, out, err = self.t("1 xxx rc.report.xxx.columns:uda_uuid.short") + self.assertEqual(self.expected_str[:8], out.strip()) + + class TestFeature1061(TestCase): def setUp(self): """Executed before each test in the class""" From 36a449c9357a871b8c45ced549a64086fb272515 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 10:54:06 -0400 Subject: [PATCH 222/242] Bump sigstore/cosign-installer from 3.8.1 to 3.8.2 (#3855) Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.8.1 to 3.8.2. - [Release notes](https://github.com/sigstore/cosign-installer/releases) - [Commits](https://github.com/sigstore/cosign-installer/compare/v3.8.1...v3.8.2) --- updated-dependencies: - dependency-name: sigstore/cosign-installer dependency-version: 3.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 454dadf20..cbed45d1a 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -33,7 +33,7 @@ jobs: submodules: "recursive" - name: Install cosign - uses: sigstore/cosign-installer@v3.8.1 + uses: sigstore/cosign-installer@v3.8.2 - name: Log into registry ${{ env.REGISTRY }} uses: docker/login-action@v3.4.0 From 7be313e91fb409558c0fc95d2a2ace8f9103ee55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 10:54:21 -0400 Subject: [PATCH 223/242] Bump docker/build-push-action from 6.15.0 to 6.16.0 (#3856) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.15.0 to 6.16.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.15.0...v6.16.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-version: 6.16.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index cbed45d1a..b274c712b 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -44,7 +44,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.15.0 + uses: docker/build-push-action@v6.16.0 with: context: . file: "./docker/task.dockerfile" From b4e25fe42f0ba3118ceeb890a37d4275bb377e04 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 30 Apr 2025 19:22:44 -0400 Subject: [PATCH 224/242] Do not decode a non-JSON value (#3859) None of the other task modifications (modify, prepend, append) treat the input as JSON, so this one shouldn't either. This works around https://github.com/GothenburgBitFactory/libshared/issues/95 --- src/Task.cpp | 2 +- test/quotes.test.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Task.cpp b/src/Task.cpp index f6026b848..4eded903a 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -928,7 +928,7 @@ void Task::addAnnotation(const std::string& description) { ++now; } while (has(key)); - data[key] = json::decode(description); + data[key] = description; ++annotation_count; recalc_urgency = true; } diff --git a/test/quotes.test.py b/test/quotes.test.py index c17e229e6..56b5a5518 100755 --- a/test/quotes.test.py +++ b/test/quotes.test.py @@ -67,21 +67,23 @@ class TestBug268(TestCase): self.assertIn("a/b or c", out) -class TestBug880(TestCase): +class TestBug3858(TestCase): def setUp(self): """Executed before each test in the class""" self.t = Task() def test_backslash_at_eol(self): - """880: Backslash at end of description/annotation causes problems""" + """880: Backslashes at end of description/annotation are handled correctly""" self.t(r"add one\\") code, out, err = self.t("_get 1.description") self.assertEqual("one\\\n", out) - self.t(r"1 annotate 'two\\'") + self.t(r"1 annotate 'two\'") + self.t(r"1 annotate 'three\\'") code, out, err = self.t("info rc.verbose:nothing") self.assertIn("one\\\n", out) self.assertIn("two\\\n", out) + self.assertIn("three\\\\\n", out) class TestBug1436(TestCase): From 36e5f8895d8508374abb4f0df4ef8baf3926f2c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 08:20:55 -0400 Subject: [PATCH 225/242] Bump src/libshared from `2aa844c` to `cb078b0` (#3860) Bumps [src/libshared](https://github.com/GothenburgBitFactory/libshared) from `2aa844c` to `cb078b0`. - [Commits](https://github.com/GothenburgBitFactory/libshared/compare/2aa844cb9b015fca81b947c57fde07999ede002b...cb078b00c5201e116c9dfd7d6951d954473eaa8f) --- updated-dependencies: - dependency-name: src/libshared dependency-version: cb078b00c5201e116c9dfd7d6951d954473eaa8f dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/libshared | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libshared b/src/libshared index 2aa844cb9..cb078b00c 160000 --- a/src/libshared +++ b/src/libshared @@ -1 +1 @@ -Subproject commit 2aa844cb9b015fca81b947c57fde07999ede002b +Subproject commit cb078b00c5201e116c9dfd7d6951d954473eaa8f From 416c6d3ca402aaece1317fdd87d4cde17e737723 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 07:56:13 -0400 Subject: [PATCH 226/242] Bump src/libshared from `cb078b0` to `8ad3646` (#3864) Bumps [src/libshared](https://github.com/GothenburgBitFactory/libshared) from `cb078b0` to `8ad3646`. - [Commits](https://github.com/GothenburgBitFactory/libshared/compare/cb078b00c5201e116c9dfd7d6951d954473eaa8f...8ad3646209c8d2e7820c3cd59319a2be3b3d221e) --- updated-dependencies: - dependency-name: src/libshared dependency-version: 8ad3646209c8d2e7820c3cd59319a2be3b3d221e dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/libshared | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libshared b/src/libshared index cb078b00c..8ad364620 160000 --- a/src/libshared +++ b/src/libshared @@ -1 +1 @@ -Subproject commit cb078b00c5201e116c9dfd7d6951d954473eaa8f +Subproject commit 8ad3646209c8d2e7820c3cd59319a2be3b3d221e From 499f931f67ccaae352da9d585b0c2d26c408e16e Mon Sep 17 00:00:00 2001 From: Felix Stupp Date: Mon, 5 May 2025 16:10:02 +0200 Subject: [PATCH 227/242] use cached urgency() for json exports (#3867) --- src/Hooks.cpp | 2 +- src/Hooks.h | 2 +- src/Task.cpp | 4 ++-- src/Task.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Hooks.cpp b/src/Hooks.cpp index a703b2539..1d1e440a4 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -275,7 +275,7 @@ void Hooks::onAdd(Task& task) const { // - all emitted non-JSON lines are considered feedback or error messages // depending on the status code. // -void Hooks::onModify(const Task& before, Task& after) const { +void Hooks::onModify(Task& before, Task& after) const { if (!_enabled) return; Timer timer; diff --git a/src/Hooks.h b/src/Hooks.h index 6496674b7..448c59744 100644 --- a/src/Hooks.h +++ b/src/Hooks.h @@ -40,7 +40,7 @@ class Hooks { void onLaunch() const; void onExit() const; void onAdd(Task&) const; - void onModify(const Task&, Task&) const; + void onModify(Task&, Task&) const; std::vector list() const; private: diff --git a/src/Task.cpp b/src/Task.cpp index 4eded903a..6608db850 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -777,7 +777,7 @@ void Task::parseLegacy(const std::string& line) { } //////////////////////////////////////////////////////////////////////////////// -std::string Task::composeJSON(bool decorate /*= false*/) const { +std::string Task::composeJSON(bool decorate /*= false*/) { std::stringstream out; out << '{'; @@ -894,7 +894,7 @@ std::string Task::composeJSON(bool decorate /*= false*/) const { #ifdef PRODUCT_TASKWARRIOR // Include urgency. - if (decorate) out << ',' << "\"urgency\":" << urgency_c(); + if (decorate) out << ',' << "\"urgency\":" << urgency(); #endif out << '}'; diff --git a/src/Task.h b/src/Task.h index b430a2bc2..0b577baff 100644 --- a/src/Task.h +++ b/src/Task.h @@ -68,7 +68,7 @@ class Task { Task(rust::Box); void parse(const std::string&); - std::string composeJSON(bool decorate = false) const; + std::string composeJSON(bool decorate = false); // Status values. enum status { pending, completed, deleted, recurring, waiting }; From 97bcc76ac14089a59a105a05d7cde1cea7afd67a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 16:55:43 +0000 Subject: [PATCH 228/242] [pre-commit.ci] pre-commit autoupdate (#3868) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v20.1.0 → v20.1.3](https://github.com/pre-commit/mirrors-clang-format/compare/v20.1.0...v20.1.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c0c0596dd..85ff519ce 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v20.1.0 + rev: v20.1.3 hooks: - id: clang-format types_or: [c++, c] From 0e59a62ead21a208cbb803fbcad8cfd1d27b345e Mon Sep 17 00:00:00 2001 From: Antoni Borowski <83772804+antek5421@users.noreply.github.com> Date: Thu, 8 May 2025 00:29:19 +0200 Subject: [PATCH 229/242] Fix #3571: Added detailed feedback for successful task synchronization (#3758) * Fix #3571: Added detailed feedback for successful task synchronization * Refactor sync logic and add verbose output for synchronization operations * Give a count of local operations sent --------- Co-authored-by: Antoni Borowski Co-authored-by: Dustin J. Mitchell --- src/commands/CmdSync.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index 15e07469d..e90c31061 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -83,6 +83,8 @@ int CmdSync::execute(std::string& output) { std::regex remove_creds_regex("^(https?://.+):(.+)@(.+)"); std::string safe_server_url = std::regex_replace(server_url, remove_creds_regex, "$1:****@$3"); + auto num_local_operations = replica->num_local_operations(); + if (server_dir != "") { if (verbose) { out << format("Syncing with {1}", server_dir) << '\n'; @@ -135,6 +137,7 @@ int CmdSync::execute(std::string& output) { replica->sync_to_aws_with_default_creds(aws_region, aws_bucket, encryption_secret, avoid_snapshots); } + } else if (gcp_bucket != "") { std::string gcp_credential_path = Context::getContext().config.get("sync.gcp.credential_path"); if (encryption_secret == "") { @@ -144,6 +147,7 @@ int CmdSync::execute(std::string& output) { out << format("Syncing with GCP bucket {1}", gcp_bucket) << '\n'; } replica->sync_to_gcp(gcp_bucket, gcp_credential_path, encryption_secret, avoid_snapshots); + } else if (server_url != "") { if (client_id == "" || encryption_secret == "") { throw std::string("sync.server.client_id and sync.encryption_secret are required"); @@ -153,6 +157,7 @@ int CmdSync::execute(std::string& output) { } replica->sync_to_remote(server_url, tc::uuid_from_string(client_id), encryption_secret, avoid_snapshots); + } else { throw std::string("No sync.* settings are configured. See task-sync(5)."); } @@ -161,6 +166,15 @@ int CmdSync::execute(std::string& output) { context.tdb2.expire_tasks(); } + if (verbose) { + out << "Success!\n"; + // Taskchampion does not provide a measure of the number of operations received from + // the server, but we can give some indication of the number sent. + if (num_local_operations) { + out << format("Sent {1} local operations to the server", num_local_operations) << '\n'; + } + } + output = out.str(); return status; } From 79eb38d582af11861f4e7a8e13799deb6313bbbf Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 8 May 2025 13:08:22 -0400 Subject: [PATCH 230/242] Fix compiler warning about unused variable (#3873) This was added to indicate that the return value of chdir was unused, but newer compilers "see through" this and determine it to be unused. The return value is not marked must-use, so just doing nothing with it is sufficient. --- src/commands/CmdEdit.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/commands/CmdEdit.cpp b/src/commands/CmdEdit.cpp index 11cd8d937..8f6e85927 100644 --- a/src/commands/CmdEdit.cpp +++ b/src/commands/CmdEdit.cpp @@ -619,10 +619,9 @@ CmdEdit::editResult CmdEdit::editFile(Task& task) { auto dateformat = Context::getContext().config.get("dateformat.edit"); if (dateformat == "") dateformat = Context::getContext().config.get("dateformat"); - // Change directory for the editor + // Change directory for the editor, doing nothing on error. auto current_dir = Directory::cwd(); - int ignored = chdir(location._data.c_str()); - ++ignored; // Keep compiler quiet. + chdir(location._data.c_str()); // Check if the file already exists, if so, bail out Path filepath = Path(file.str()); @@ -702,7 +701,7 @@ ARE_THESE_REALLY_HARMFUL: // Cleanup. File::remove(file.str()); - ignored = chdir(current_dir.c_str()); + chdir(current_dir.c_str()); return changes ? CmdEdit::editResult::changes : CmdEdit::editResult::nochanges; } From 6c60a8db84fee6a66846e6efff4220b9eddfacb7 Mon Sep 17 00:00:00 2001 From: Tobias Predel Date: Sat, 10 May 2025 02:26:21 +0200 Subject: [PATCH 231/242] Move `timegm` implementation to `libshared` (#3875) Move `timegm` implementation to libshared --- CMakeLists.txt | 1 - cmake.h.in | 3 --- src/util.cpp | 18 ------------------ src/util.h | 4 ---- 4 files changed, 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9323dc5a0..890d23ee3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,6 @@ SET (TASK_BINDIR bin CACHE STRING "Installation directory for the bi # rust libs require these set (TASK_LIBRARIES dl pthread) -check_function_exists (timegm HAVE_TIMEGM) check_function_exists (get_current_dir_name HAVE_GET_CURRENT_DIR_NAME) check_function_exists (wordexp HAVE_WORDEXP) diff --git a/cmake.h.in b/cmake.h.in index ec294bf99..25c0d6acc 100644 --- a/cmake.h.in +++ b/cmake.h.in @@ -41,9 +41,6 @@ /* Found tm_gmtoff */ #cmakedefine HAVE_TM_GMTOFF -/* Found timegm */ -#cmakedefine HAVE_TIMEGM - /* Found st.st_birthtime struct member */ #cmakedefine HAVE_ST_BIRTHTIME diff --git a/src/util.cpp b/src/util.cpp index ebbbf2191..6b945f959 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -218,24 +218,6 @@ const std::vector extractParents(const std::string& project, return vec; } -//////////////////////////////////////////////////////////////////////////////// -#ifndef HAVE_TIMEGM -time_t timegm(struct tm* tm) { - time_t ret; - char* tz; - tz = getenv("TZ"); - setenv("TZ", "UTC", 1); - tzset(); - ret = mktime(tm); - if (tz) - setenv("TZ", tz, 1); - else - unsetenv("TZ"); - tzset(); - return ret; -} -#endif - //////////////////////////////////////////////////////////////////////////////// bool nontrivial(const std::string& input) { std::string::size_type i = 0; diff --git a/src/util.h b/src/util.h index 782311117..d2be723f5 100644 --- a/src/util.h +++ b/src/util.h @@ -54,10 +54,6 @@ const std::string indentProject(const std::string&, const std::string& whitespac const std::vector extractParents(const std::string&, const char& delimiter = '.'); -#ifndef HAVE_TIMEGM -time_t timegm(struct tm* tm); -#endif - bool nontrivial(const std::string&); const char* optionalBlankLine(); void setHeaderUnderline(Table&); From 4620b5fd258f1df04afd7b7fc249a7e0e9116fd0 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 12 May 2025 02:16:54 -0400 Subject: [PATCH 232/242] Make `tags.none:` filter work again (#3877) * Don't remove the legacy 'tags' property * Simulate the `tags` DOM property This refers to the deprecated "tags" property of tasks. This property usually still exists, for compatibility with older versions, but should not be relied on anymore. --- src/DOM.cpp | 7 +++++++ src/commands/CmdEdit.cpp | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/DOM.cpp b/src/DOM.cpp index fe47f2878..31a8e5dc0 100644 --- a/src/DOM.cpp +++ b/src/DOM.cpp @@ -286,6 +286,13 @@ bool getDOM(const std::string& name, const Task* task, Variant& value) { return true; } + // The "tags" property is deprecated, but it is documented as part of the DOM, so simulate it. + if (size == 1 && canonical == "tags") { + auto tags = ref->getTags(); + value = Variant(join(",", tags)); + return true; + } + Column* column = Context::getContext().columns[canonical]; if (size == 1 && column) { diff --git a/src/commands/CmdEdit.cpp b/src/commands/CmdEdit.cpp index 8f6e85927..b6535f1aa 100644 --- a/src/commands/CmdEdit.cpp +++ b/src/commands/CmdEdit.cpp @@ -317,7 +317,6 @@ void CmdEdit::parseTask(Task& task, const std::string& after, const std::string& // tags value = findValue(after, "\n Tags:"); - task.remove("tags"); task.setTags(split(value, ' ')); // description. From 89d84f0bdd3e736b8884c8dcf7ea4cb942de83a8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 14:01:34 -0400 Subject: [PATCH 233/242] [pre-commit.ci] pre-commit autoupdate (#3878) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v20.1.3 → v20.1.4](https://github.com/pre-commit/mirrors-clang-format/compare/v20.1.3...v20.1.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 85ff519ce..60f0e3f5a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v20.1.3 + rev: v20.1.4 hooks: - id: clang-format types_or: [c++, c] From f6824e90a16f03c94f38760f7cf06650f587879d Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 26 May 2025 13:07:35 -0400 Subject: [PATCH 234/242] Correctly handle undo with multiple tasks (#3886) The `std::stringstream::clear` method does not, in fact, clear the string -- it just resets some internal flags. Assigning a new stringstream to the variable is not the most efficient way to do this, but it's the clearest. --- src/commands/CmdUndo.cpp | 2 +- test/undo.test.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/commands/CmdUndo.cpp b/src/commands/CmdUndo.cpp index 4c2e6b7f7..83156410e 100644 --- a/src/commands/CmdUndo.cpp +++ b/src/commands/CmdUndo.cpp @@ -102,7 +102,7 @@ bool CmdUndo::confirm_revert(const std::vector& undo_ops) { view.set(row, 1, mods.str()); } last_uuid = op.get_uuid(); - mods.clear(); + mods = std::stringstream(); } if (op.is_create()) { diff --git a/test/undo.test.py b/test/undo.test.py index c3ce12af0..5df49155c 100755 --- a/test/undo.test.py +++ b/test/undo.test.py @@ -27,6 +27,7 @@ import sys import os +import re import unittest # Ensure python finds the local simpletap module @@ -61,6 +62,25 @@ class TestUndo(TestCase): code, out, err = self.t("_get 1.status") self.assertEqual(out.strip(), "pending") + def test_modify_multiple_tasks(self): + """'add' then 'done' then 'undo'""" + self.t("add one") + self.t("add two") + self.t("add three") + self.t("rc.bulk=0 1,2,3 modify +sometag") + code, out, err = self.t("undo", input="y\n") + # This undo output should show one tag modification for each task, possibly with some + # modification-time updates if the modifications spanned a second boundary. + self.assertRegex( + out, + "\s+".join( + [ + r"""[0-9a-f-]{36} (Update property 'modified' from\s+'[0-9]+' to '[0-9]+'\s+)?Add tag 'sometag'\s+Add property 'tags' with value 'sometag'""" + ] + * 3 + ), + ) + def test_undo_en_passant(self): """Verify that en-passant changes during undo are an error""" self.t("add one") From 75d351afadf279d33a2908dd92481e7f51538935 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 2 Jun 2025 07:59:05 -0400 Subject: [PATCH 235/242] Do not auto-create .taskrc when stdout is not a TTY (#3888) * Do not auto-create .taskrc when stdout is not a TTY This avoids prompting or automatically creating such a file, both of which are unexpected when performing command-line completion. Fixes #3751. * Test case for taskrc creation no longer works A taskrc is only created when stdout is a tty, which would require allocating a pty, which is very platform-dependent and definitely not worth the trouble for this test. --- src/Context.cpp | 7 +++++++ test/taskrc.test.py | 1 + 2 files changed, 8 insertions(+) diff --git a/src/Context.cpp b/src/Context.cpp index f90dd984e..f3c87255b 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -1173,6 +1173,13 @@ void Context::staticInitialization() { void Context::createDefaultConfig() { // Do we need to create a default rc? if (rc_file._data != "" && !rc_file.exists()) { + // If stdout is not a file, we are probably executing in a completion context and should not + // prompt (as the user won't see it) or modify the config (as completion functions are typically + // read-only). + if (!isatty(STDOUT_FILENO)) { + throw std::string("Cannot proceed without rc file."); + } + if (config.getBoolean("confirmation") && !confirm(format("A configuration file could not be found in {1}\n\nWould you like a sample " "{2} created, so Taskwarrior can proceed?", diff --git a/test/taskrc.test.py b/test/taskrc.test.py index 044681881..868ad2382 100755 --- a/test/taskrc.test.py +++ b/test/taskrc.test.py @@ -40,6 +40,7 @@ class TestTaskrc(TestCase): """Executed before each test in the class""" self.t = Task() + @unittest.skip("taskrc generation requires a tty - see #3751") def test_default_taskrc(self): """Verify that a default .taskrc is generated""" os.remove(self.t.taskrc) From e5b69afee2332cbffcb5df49e68f5baf40dc95b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 09:01:38 -0400 Subject: [PATCH 236/242] Bump docker/build-push-action from 6.16.0 to 6.18.0 (#3889) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.16.0 to 6.18.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.16.0...v6.18.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-version: 6.18.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index b274c712b..c6d116b1c 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -44,7 +44,7 @@ jobs: - name: Build and push Taskwarrior Docker image id: build-and-push - uses: docker/build-push-action@v6.16.0 + uses: docker/build-push-action@v6.18.0 with: context: . file: "./docker/task.dockerfile" From 440d3f8c92d9ae99d5627e88b71f793bbd1a7bca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 09:01:53 -0400 Subject: [PATCH 237/242] Bump src/libshared from `8ad3646` to `121f757` (#3890) Bumps [src/libshared](https://github.com/GothenburgBitFactory/libshared) from `8ad3646` to `121f757`. - [Commits](https://github.com/GothenburgBitFactory/libshared/compare/8ad3646209c8d2e7820c3cd59319a2be3b3d221e...121f757c3ec1b1f548f7835208b8c72d85d141a7) --- updated-dependencies: - dependency-name: src/libshared dependency-version: 121f757c3ec1b1f548f7835208b8c72d85d141a7 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/libshared | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libshared b/src/libshared index 8ad364620..121f757c3 160000 --- a/src/libshared +++ b/src/libshared @@ -1 +1 @@ -Subproject commit 8ad3646209c8d2e7820c3cd59319a2be3b3d221e +Subproject commit 121f757c3ec1b1f548f7835208b8c72d85d141a7 From 6d81c8cda096ee3d4e7ba23ae22c0cea4654f684 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 11:49:04 -0400 Subject: [PATCH 238/242] Bump src/taskchampion-cpp/corrosion from `715c235` to `00af456` (#3891) Bumps [src/taskchampion-cpp/corrosion](https://github.com/corrosion-rs/corrosion) from `715c235` to `00af456`. - [Release notes](https://github.com/corrosion-rs/corrosion/releases) - [Commits](https://github.com/corrosion-rs/corrosion/compare/715c235daef4b8ee67278f12256334ad3dd4c4ae...00af4564881e9fc031f6b3303c1d6d19ecfe00f3) --- updated-dependencies: - dependency-name: src/taskchampion-cpp/corrosion dependency-version: 00af4564881e9fc031f6b3303c1d6d19ecfe00f3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/taskchampion-cpp/corrosion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/taskchampion-cpp/corrosion b/src/taskchampion-cpp/corrosion index 715c235da..00af45648 160000 --- a/src/taskchampion-cpp/corrosion +++ b/src/taskchampion-cpp/corrosion @@ -1 +1 @@ -Subproject commit 715c235daef4b8ee67278f12256334ad3dd4c4ae +Subproject commit 00af4564881e9fc031f6b3303c1d6d19ecfe00f3 From ffa0d3e944b4e209ee05fe226b71931a1aa7e298 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 08:08:07 -0400 Subject: [PATCH 239/242] Bump src/taskchampion-cpp/corrosion from `00af456` to `4eccadd` (#3893) Bumps [src/taskchampion-cpp/corrosion](https://github.com/corrosion-rs/corrosion) from `00af456` to `4eccadd`. - [Release notes](https://github.com/corrosion-rs/corrosion/releases) - [Commits](https://github.com/corrosion-rs/corrosion/compare/00af4564881e9fc031f6b3303c1d6d19ecfe00f3...4eccadd67819b427978ca540e0c31e6cce08f226) --- updated-dependencies: - dependency-name: src/taskchampion-cpp/corrosion dependency-version: 4eccadd67819b427978ca540e0c31e6cce08f226 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/taskchampion-cpp/corrosion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/taskchampion-cpp/corrosion b/src/taskchampion-cpp/corrosion index 00af45648..4eccadd67 160000 --- a/src/taskchampion-cpp/corrosion +++ b/src/taskchampion-cpp/corrosion @@ -1 +1 @@ -Subproject commit 00af4564881e9fc031f6b3303c1d6d19ecfe00f3 +Subproject commit 4eccadd67819b427978ca540e0c31e6cce08f226 From a949c698f92a2ebee86ac29c22aa8d05368899de Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 14 Jun 2025 17:34:59 +0200 Subject: [PATCH 240/242] [pre-commit.ci] pre-commit autoupdate (#3887) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v20.1.4 → v20.1.5](https://github.com/pre-commit/mirrors-clang-format/compare/v20.1.4...v20.1.5) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 60f0e3f5a..e8dfa828f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v20.1.4 + rev: v20.1.5 hooks: - id: clang-format types_or: [c++, c] From baaf69202bfe83e5c7f7867608726ae75317c178 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:08:43 -0400 Subject: [PATCH 241/242] [pre-commit.ci] pre-commit autoupdate (#3897) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v20.1.5 → v20.1.6](https://github.com/pre-commit/mirrors-clang-format/compare/v20.1.5...v20.1.6) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e8dfa828f..f17f48519 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-added-large-files - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v20.1.5 + rev: v20.1.6 hooks: - id: clang-format types_or: [c++, c] From c594ecb58df950b9cdd0dbd06e2fb205663deaa4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:18:30 -0400 Subject: [PATCH 242/242] Bump sigstore/cosign-installer from 3.8.2 to 3.9.0 (#3902) Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.8.2 to 3.9.0. - [Release notes](https://github.com/sigstore/cosign-installer/releases) - [Commits](https://github.com/sigstore/cosign-installer/compare/v3.8.2...v3.9.0) --- updated-dependencies: - dependency-name: sigstore/cosign-installer dependency-version: 3.9.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index c6d116b1c..63a0f976c 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -33,7 +33,7 @@ jobs: submodules: "recursive" - name: Install cosign - uses: sigstore/cosign-installer@v3.8.2 + uses: sigstore/cosign-installer@v3.9.0 - name: Log into registry ${{ env.REGISTRY }} uses: docker/login-action@v3.4.0

    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 Aa{vGU diff --git a/taskchampion/docs/assets/cgi/logo/logo_32.png b/taskchampion/docs/assets/cgi/logo/logo_32.png deleted file mode 100755 index b180de372956d4ace3115791c71446f7c065e0ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2820 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 WM6|H*-&f-R0000GF`O^D( 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

6sIElixLvw)3zy_u3(Tpt}SU!i^5Y28}= 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